diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..c04711aa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git/ +CONTRIBUTING.md +Dockerfile +TODO.md +build/ +cockatrice/ +doc/ +oracle/ +sounds/ +travis-compile.sh +travis-dependencies.sh +zonebg/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..581070cc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +FROM ubuntu:trusty +MAINTAINER Gavin Bisesi + +RUN apt-get update && apt-get install -y software-properties-common +RUN apt-add-repository ppa:ubuntu-sdk-team/ppa +RUN apt-get update && apt-get install -y\ + cmake\ + git\ + libprotobuf-dev\ + libqt5sql5-mysql\ + libqt5svg5-dev\ + libqt5webkit5-dev\ + libsqlite3-dev\ + protobuf-compiler\ + qt5-default\ + qtbase5-dev\ + qtdeclarative5-dev\ + qtmultimedia5-dev\ + qttools5-dev-tools\ + qttools5-dev + +ENV dir /home/servatrice/code +WORKDIR $dir +RUN mkdir oracle +COPY COPYING COPYING +COPY CMakeLists.txt CMakeLists.txt +COPY cmake/ cmake/ +COPY common/ common/ +COPY servatrice/ servatrice/ +COPY README.md README.md + +WORKDIR build +RUN cmake .. -DWITH_QT4=0 -DWITH_SERVER=1 -DWITH_CLIENT=0 -DWITH_ORACLE=0 &&\ + make &&\ + make install + +WORKDIR /home/servatrice + +EXPOSE 4747 + +ENTRYPOINT [ "servatrice" ] diff --git a/README.md b/README.md index e6cacd46..d1177ca0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,29 @@ +

+ +--- + +**Table of Contents**    [Cockatrice](#cockatrice) | [Get Involved] (#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building-) | [Running](#running) | [License](#license) + +--- + # Cockatrice Cockatrice is an open-source multiplatform software for playing card games, such as Magic: The Gathering, over a network. It is fully client-server based to prevent any kind of cheating, though it supports single-player games without -a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5. +a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5.
+ # Get Involved [![Gitter chat](https://badges.gitter.im/Cockatrice/Cockatrice.png)](https://gitter.im/Cockatrice/Cockatrice) -Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here. +Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.
+ # Community Resources -- [reddit r/Cockatrice](http://reddit.com/r/cockatrice) -- [Woogerworks Server & Forums](http://www.woogerworks.com) - [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki) +- [reddit r/Cockatrice](http://reddit.com/r/cockatrice) +- [Woogerworks](http://www.woogerworks.com) / [Chickatrice] (http://www.chickatrice.net/) / [Poixen](http://www.poixen.com/) (incomplete Serverlist)
+ # Translation Status [![Cockatrice on Transiflex](https://ds0k0en9abmn1.cloudfront.net/static/charts/images/tx-logo-micro.646b0065fce6.png)](https://www.transifex.com/projects/p/cockatrice/) @@ -23,26 +34,25 @@ Language statistics for `Cockatrice` *(on the left)* and `Oracle` *(on the right [![Cockatrice translations](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/chart/image_png)](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/)      [![Oracle translations](https://www.transifex.com/projects/p/cockatrice/resource/oracle/chart/image_png)](https://www.transifex.com/projects/p/cockatrice/resource/oracle/) -Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information! +Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information!
+ # Building [![Build Status](https://travis-ci.org/Cockatrice/Cockatrice.svg?branch=master)](https://travis-ci.org/Cockatrice/Cockatrice) -**Detailed installation instructions are on the Cockatrice wiki under [Installing Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Installing-Cockatrice)** +**Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)** Dependencies: - - [Qt](http://qt-project.org/) - [protobuf](http://code.google.com/p/protobuf/) - [CMake](http://www.cmake.org/) Oracle can optionally use zlib to load zipped files: - - [zlib](http://www.zlib.net/) The server requires an additional dependency when compiled under Qt4: - - [libgcrypt](http://www.gnu.org/software/libgcrypt/) + To compile: mkdir build @@ -60,12 +70,18 @@ The following flags can be passed to `cmake`: - `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings. - `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files. +#### Building servatrice Docker container +`docker build -t servatrice .`
+ + # Running `oracle` fetches card data `cockatrice` is the game client -`servatrice` is the server +`servatrice` is the server
+ # License -Cockatrice is free software, licensed under the GPLv2; see COPYING for details. +Cockatrice is free software, licensed under the GPLv2; see COPYING for details.
+ diff --git a/cmake/NSIS.template.in b/cmake/NSIS.template.in index 6e431842..6a57b55e 100644 --- a/cmake/NSIS.template.in +++ b/cmake/NSIS.template.in @@ -28,6 +28,7 @@ InstallDir "$PROGRAMFILES\Cockatrice" !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_COMPONENTS !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH @@ -54,8 +55,10 @@ SectionEnd Section "Update configuration" SecUpdateConfig SetShellVarContext current WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "carddatabase" "$LOCALAPPDATA\Cockatrice\cards.xml" + WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "tokendatabase" "$LOCALAPPDATA\Cockatrice\tokens.xml" WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "decks" "$LOCALAPPDATA\Cockatrice\decks" WriteRegStr HKCU "Software\Cockatrice\Cockatrice\paths" "pics" "$LOCALAPPDATA\Cockatrice\pics" + WriteRegStr HKCU "Software\Cockatrice\Cockatrice\replays" "pics" "$LOCALAPPDATA\Cockatrice\replays" WriteRegStr HKCU "Software\Cockatrice\Cockatrice\sound" "path" "$INSTDIR\sounds" SectionEnd @@ -68,7 +71,7 @@ Section "Start menu item" SecStartMenu createShortCut "$SMPROGRAMS\Cockatrice\Usermanual.lnk" "$INSTDIR\Usermanual.pdf" SectionEnd -Section Uninstall +Section "un.Application" UnSecApplication SetShellVarContext all RMDir /r "$INSTDIR\zonebg" RMDir /r "$INSTDIR\plugins" @@ -94,9 +97,14 @@ Section Uninstall RMDir "$SMPROGRAMS\Cockatrice" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" +SectionEnd +; unselected because it is /o +Section /o "un.Configurations, decks, cards, pics" UnSecConfiguration SetShellVarContext current DeleteRegKey HKCU "Software\Cockatrice" + + RMDir /r "$LOCALAPPDATA\Cockatrice" SectionEnd LangString DESC_SecApplication ${LANG_ENGLISH} "Cockatrice program files" @@ -108,6 +116,14 @@ LangString DESC_SecStartMenu ${LANG_ENGLISH} "Create start menu items for Cockat !insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu) !insertmacro MUI_FUNCTION_DESCRIPTION_END +LangString DESC_UnSecApplication ${LANG_ENGLISH} "Cockatrice program files and start menu items" +LangString DESC_UnSecConfiguration ${LANG_ENGLISH} "Configurations, decks, card database, pictures" +!insertmacro MUI_UNFUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${UnSecApplication} $(DESC_UnSecApplication) + !insertmacro MUI_DESCRIPTION_TEXT ${UnSecConfiguration} $(DESC_UnSecConfiguration) +!insertmacro MUI_UNFUNCTION_DESCRIPTION_END + + Function .onInit SetShellVarContext all ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" diff --git a/cockatrice/CMakeLists.txt b/cockatrice/CMakeLists.txt index 108bd885..2555d66f 100644 --- a/cockatrice/CMakeLists.txt +++ b/cockatrice/CMakeLists.txt @@ -11,7 +11,10 @@ SET(cockatrice_SOURCES src/dlg_filter_games.cpp src/dlg_connect.cpp src/dlg_create_token.cpp + src/dlg_edit_avatar.cpp + src/dlg_edit_password.cpp src/dlg_edit_tokens.cpp + src/dlg_edit_user.cpp src/dlg_register.cpp src/abstractclient.cpp src/remoteclient.cpp @@ -92,6 +95,10 @@ SET(cockatrice_SOURCES src/qt-json/json.cpp src/soundengine.cpp src/pending_command.cpp + src/shortcutssettings.cpp + src/sequenceEdit/sequenceedit.cpp + src/sequenceEdit/shortcutstab.cpp + src/lineeditcompleter.cpp ${VERSION_STRING_CPP} ) @@ -220,6 +227,8 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # Build cockatrice binary and link it ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS}) +set_property(TARGET cockatrice PROPERTY CXX_STANDARD 11) +set_property(TARGET cockatrice PROPERTY CXX_STANDARD_REQUIRED ON) if(Qt4_FOUND) if(MSVC) @@ -318,4 +327,4 @@ Data = Resources\") if(WIN32SSLRUNTIME_FOUND) install(FILES ${WIN32SSLRUNTIME_LIBRARIES} DESTINATION ./) endif() -endif() \ No newline at end of file +endif() diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index fda3c178..100047fe 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -1,4 +1,5 @@ +<<<<<<< HEAD resources/back.svg resources/cockatrice.svg @@ -331,4 +332,333 @@ resources/userlevels/admin.svg resources/userlevels/admin_buddy.svg +======= + + resources/back.svg + resources/lock.svg + resources/icon_delete.svg + resources/icon_tab_changed.svg + resources/icon_config_general.svg + resources/icon_config_appearance.svg + resources/icon_config_interface.svg + resources/icon_config_messages.svg + resources/icon_config_deckeditor.svg + resources/icon_config_sound.svg + resources/phases/icon_phase_untap.svg + resources/phases/icon_phase_upkeep.svg + resources/phases/icon_phase_draw.svg + resources/phases/icon_phase_main1.svg + resources/phases/icon_phase_combat_start.svg + resources/phases/icon_phase_combat_attackers.svg + resources/phases/icon_phase_combat_blockers.svg + resources/phases/icon_phase_combat_damage.svg + resources/phases/icon_phase_combat_end.svg + resources/phases/icon_phase_main2.svg + resources/phases/icon_phase_cleanup.svg + resources/phases/icon_phase_nextturn.svg + resources/icon_settings.svg + resources/hand.svg + resources/pencil.svg + resources/icon_search_black.svg + resources/icon_clearsearch.svg + resources/icon_update.png + resources/icon_view.svg + resources/hr.jpg + resources/cockatrice.svg + resources/add_to_sideboard.svg + resources/decrement.svg + resources/increment.svg + resources/remove_row.svg + resources/arrow_left_green.svg + resources/arrow_right_green.svg + resources/arrow_top_green.svg + resources/arrow_up_green.svg + resources/arrow_down_green.svg + resources/arrow_bottom_green.svg + resources/icon_ready_start.svg + resources/icon_not_ready_start.svg + resources/icon_conceded.svg + resources/icon_player.svg + resources/icon_spectator.svg + resources/replay_start.svg + resources/replay_fastforward.svg + resources/replay_pause.svg + resources/genders/male.svg + resources/genders/female.svg + resources/genders/unknown.svg + resources/countries/ad.svg + resources/countries/ae.svg + resources/countries/af.svg + resources/countries/ag.svg + resources/countries/ai.svg + resources/countries/al.svg + resources/countries/am.svg + resources/countries/ao.svg + resources/countries/aq.svg + resources/countries/ar.svg + resources/countries/as.svg + resources/countries/at.svg + resources/countries/au.svg + resources/countries/aw.svg + resources/countries/ax.svg + resources/countries/az.svg + resources/countries/ba.svg + resources/countries/bb.svg + resources/countries/bd.svg + resources/countries/be.svg + resources/countries/bf.svg + resources/countries/bg.svg + resources/countries/bh.svg + resources/countries/bi.svg + resources/countries/bj.svg + resources/countries/bl.svg + resources/countries/bm.svg + resources/countries/bn.svg + resources/countries/bo.svg + resources/countries/bq.svg + resources/countries/br.svg + resources/countries/bs.svg + resources/countries/bt.svg + resources/countries/bv.svg + resources/countries/bw.svg + resources/countries/by.svg + resources/countries/bz.svg + resources/countries/ca.svg + resources/countries/cc.svg + resources/countries/cd.svg + resources/countries/cf.svg + resources/countries/cg.svg + resources/countries/ch.svg + resources/countries/ci.svg + resources/countries/ck.svg + resources/countries/cl.svg + resources/countries/cm.svg + resources/countries/cn.svg + resources/countries/co.svg + resources/countries/cr.svg + resources/countries/cu.svg + resources/countries/cv.svg + resources/countries/cw.svg + resources/countries/cx.svg + resources/countries/cy.svg + resources/countries/cz.svg + resources/countries/de.svg + resources/countries/dj.svg + resources/countries/dk.svg + resources/countries/dm.svg + resources/countries/do.svg + resources/countries/dz.svg + resources/countries/ec.svg + resources/countries/ee.svg + resources/countries/eg.svg + resources/countries/eh.svg + resources/countries/er.svg + resources/countries/es.svg + resources/countries/et.svg + resources/countries/fi.svg + resources/countries/fj.svg + resources/countries/fk.svg + resources/countries/fm.svg + resources/countries/fo.svg + resources/countries/fr.svg + resources/countries/ga.svg + resources/countries/gb.svg + resources/countries/gd.svg + resources/countries/ge.svg + resources/countries/gf.svg + resources/countries/gg.svg + resources/countries/gh.svg + resources/countries/gi.svg + resources/countries/gl.svg + resources/countries/gm.svg + resources/countries/gn.svg + resources/countries/gp.svg + resources/countries/gq.svg + resources/countries/gr.svg + resources/countries/gs.svg + resources/countries/gt.svg + resources/countries/gu.svg + resources/countries/gw.svg + resources/countries/gy.svg + resources/countries/hk.svg + resources/countries/hm.svg + resources/countries/hn.svg + resources/countries/hr.svg + resources/countries/ht.svg + resources/countries/hu.svg + resources/countries/id.svg + resources/countries/ie.svg + resources/countries/il.svg + resources/countries/im.svg + resources/countries/in.svg + resources/countries/io.svg + resources/countries/iq.svg + resources/countries/ir.svg + resources/countries/is.svg + resources/countries/it.svg + resources/countries/je.svg + resources/countries/jm.svg + resources/countries/jo.svg + resources/countries/jp.svg + resources/countries/ke.svg + resources/countries/kg.svg + resources/countries/kh.svg + resources/countries/ki.svg + resources/countries/km.svg + resources/countries/kn.svg + resources/countries/kp.svg + resources/countries/kr.svg + resources/countries/kw.svg + resources/countries/ky.svg + resources/countries/kz.svg + resources/countries/la.svg + resources/countries/lb.svg + resources/countries/lc.svg + resources/countries/li.svg + resources/countries/lk.svg + resources/countries/lr.svg + resources/countries/ls.svg + resources/countries/lt.svg + resources/countries/lu.svg + resources/countries/lv.svg + resources/countries/ly.svg + resources/countries/ma.svg + resources/countries/mc.svg + resources/countries/md.svg + resources/countries/me.svg + resources/countries/mf.svg + resources/countries/mg.svg + resources/countries/mh.svg + resources/countries/mk.svg + resources/countries/ml.svg + resources/countries/mm.svg + resources/countries/mn.svg + resources/countries/mo.svg + resources/countries/mp.svg + resources/countries/mq.svg + resources/countries/mr.svg + resources/countries/ms.svg + resources/countries/mt.svg + resources/countries/mu.svg + resources/countries/mv.svg + resources/countries/mw.svg + resources/countries/mx.svg + resources/countries/my.svg + resources/countries/mz.svg + resources/countries/na.svg + resources/countries/nc.svg + resources/countries/ne.svg + resources/countries/nf.svg + resources/countries/ng.svg + resources/countries/ni.svg + resources/countries/nl.svg + resources/countries/no.svg + resources/countries/np.svg + resources/countries/nr.svg + resources/countries/nu.svg + resources/countries/nz.svg + resources/countries/om.svg + resources/countries/pa.svg + resources/countries/pe.svg + resources/countries/pf.svg + resources/countries/pg.svg + resources/countries/ph.svg + resources/countries/pk.svg + resources/countries/pl.svg + resources/countries/pm.svg + resources/countries/pn.svg + resources/countries/pr.svg + resources/countries/ps.svg + resources/countries/pt.svg + resources/countries/pw.svg + resources/countries/py.svg + resources/countries/qa.svg + resources/countries/re.svg + resources/countries/ro.svg + resources/countries/rs.svg + resources/countries/ru.svg + resources/countries/rw.svg + resources/countries/sa.svg + resources/countries/sb.svg + resources/countries/sc.svg + resources/countries/sd.svg + resources/countries/se.svg + resources/countries/sg.svg + resources/countries/sh.svg + resources/countries/si.svg + resources/countries/sj.svg + resources/countries/sk.svg + resources/countries/sl.svg + resources/countries/sm.svg + resources/countries/sn.svg + resources/countries/so.svg + resources/countries/sr.svg + resources/countries/ss.svg + resources/countries/st.svg + resources/countries/sv.svg + resources/countries/sx.svg + resources/countries/sy.svg + resources/countries/sz.svg + resources/countries/tc.svg + resources/countries/td.svg + resources/countries/tf.svg + resources/countries/tg.svg + resources/countries/th.svg + resources/countries/tj.svg + resources/countries/tk.svg + resources/countries/tl.svg + resources/countries/tm.svg + resources/countries/tn.svg + resources/countries/to.svg + resources/countries/tr.svg + resources/countries/tt.svg + resources/countries/tv.svg + resources/countries/tw.svg + resources/countries/tz.svg + resources/countries/ua.svg + resources/countries/ug.svg + resources/countries/um.svg + resources/countries/us.svg + resources/countries/uy.svg + resources/countries/uz.svg + resources/countries/va.svg + resources/countries/vc.svg + resources/countries/ve.svg + resources/countries/vg.svg + resources/countries/vi.svg + resources/countries/vn.svg + resources/countries/vu.svg + resources/countries/wf.svg + resources/countries/ws.svg + resources/countries/ye.svg + resources/countries/yt.svg + resources/countries/za.svg + resources/countries/zm.svg + resources/countries/zw.svg + resources/counters/w.svg + resources/counters/w_highlight.svg + resources/counters/u.svg + resources/counters/u_highlight.svg + resources/counters/b.svg + resources/counters/b_highlight.svg + resources/counters/r.svg + resources/counters/r_highlight.svg + resources/counters/g.svg + resources/counters/g_highlight.svg + resources/counters/storm.svg + resources/counters/storm_highlight.svg + resources/counters/general.svg + resources/counters/general_highlight.svg + resources/userlevels/normal.svg + resources/userlevels/registered.svg + resources/userlevels/registered_buddy.svg + resources/userlevels/moderator.svg + resources/userlevels/moderator_buddy.svg + resources/userlevels/admin.svg + resources/userlevels/admin_buddy.svg + resources/news/exclamation_mark.svg + resources/news/question_mark.svg + resources/icon_config_shorcuts.svg + +>>>>>>> master diff --git a/cockatrice/resources/icon_config_shorcuts.svg b/cockatrice/resources/icon_config_shorcuts.svg new file mode 100644 index 00000000..d52858cb --- /dev/null +++ b/cockatrice/resources/icon_config_shorcuts.svg @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/cockatrice/src/abstractcarditem.cpp b/cockatrice/src/abstractcarditem.cpp index e9c1b55f..dcd68fa1 100644 --- a/cockatrice/src/abstractcarditem.cpp +++ b/cockatrice/src/abstractcarditem.cpp @@ -123,7 +123,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS } else { painter->save(); transformPainter(painter, translatedSize, angle); - painter->drawPixmap(QPointF(0, angle ? -1 : 0), translatedPixmap); + painter->drawPixmap(QPointF(1, 1), translatedPixmap); painter->restore(); } painter->setBrush(bgColor); diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp index f2e8915b..6faf6b33 100644 --- a/cockatrice/src/abstractclient.cpp +++ b/cockatrice/src/abstractclient.cpp @@ -8,6 +8,7 @@ #include "pb/event_server_shutdown.pb.h" #include "pb/event_connection_closed.pb.h" #include "pb/event_user_message.pb.h" +#include "pb/event_notify_user.pb.h" #include "pb/event_list_rooms.pb.h" #include "pb/event_add_to_list.pb.h" #include "pb/event_remove_from_list.pb.h" @@ -40,6 +41,7 @@ AbstractClient::AbstractClient(QObject *parent) qRegisterMetaType("Event_ListRooms"); qRegisterMetaType("Event_GameJoined"); qRegisterMetaType("Event_UserMessage"); + qRegisterMetaType("Event_NotifyUser"); qRegisterMetaType("ServerInfo_User"); qRegisterMetaType >("QList"); qRegisterMetaType("Event_ReplayAdded"); @@ -75,6 +77,7 @@ void AbstractClient::processProtocolItem(const ServerMessage &item) case SessionEvent::SERVER_SHUTDOWN: emit serverShutdownEventReceived(event.GetExtension(Event_ServerShutdown::ext)); break; case SessionEvent::CONNECTION_CLOSED: emit connectionClosedEventReceived(event.GetExtension(Event_ConnectionClosed::ext)); break; case SessionEvent::USER_MESSAGE: emit userMessageEventReceived(event.GetExtension(Event_UserMessage::ext)); break; + case SessionEvent::NOTIFY_USER: emit notifyUserEventReceived(event.GetExtension(Event_NotifyUser::ext)); break; case SessionEvent::LIST_ROOMS: emit listRoomsEventReceived(event.GetExtension(Event_ListRooms::ext)); break; case SessionEvent::ADD_TO_LIST: emit addToListEventReceived(event.GetExtension(Event_AddToList::ext)); break; case SessionEvent::REMOVE_FROM_LIST: emit removeFromListEventReceived(event.GetExtension(Event_RemoveFromList::ext)); break; diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h index df029a1e..939066bc 100644 --- a/cockatrice/src/abstractclient.h +++ b/cockatrice/src/abstractclient.h @@ -21,6 +21,7 @@ class Event_ServerMessage; class Event_ListRooms; class Event_GameJoined; class Event_UserMessage; +class Event_NotifyUser; class Event_ConnectionClosed; class Event_ServerShutdown; class Event_ReplayAdded; @@ -56,6 +57,7 @@ signals: void listRoomsEventReceived(const Event_ListRooms &event); void gameJoinedEventReceived(const Event_GameJoined &event); void userMessageEventReceived(const Event_UserMessage &event); + void notifyUserEventReceived(const Event_NotifyUser &event); void userInfoChanged(const ServerInfo_User &userInfo); void buddyListReceived(const QList &buddyList); void ignoreListReceived(const QList &ignoreList); diff --git a/cockatrice/src/abstractcounter.cpp b/cockatrice/src/abstractcounter.cpp index 6e6393e7..0b07d980 100644 --- a/cockatrice/src/abstractcounter.cpp +++ b/cockatrice/src/abstractcounter.cpp @@ -1,5 +1,6 @@ #include "abstractcounter.h" #include "player.h" +#include "settingscache.h" #include #include #include @@ -17,6 +18,8 @@ AbstractCounter::AbstractCounter(Player *_player, int _id, const QString &_name, setAcceptHoverEvents(true); #endif + shortcutActive = false; + if (player->getLocal()) { menu = new QMenu(name); aSet = new QAction(this); @@ -39,6 +42,8 @@ AbstractCounter::AbstractCounter(Player *_player, int _id, const QString &_name, } else menu = 0; + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); retranslateUi(); } @@ -65,14 +70,16 @@ void AbstractCounter::retranslateUi() void AbstractCounter::setShortcutsActive() { if (name == "life") { - aSet->setShortcut(QKeySequence("Ctrl+L")); - aDec->setShortcut(QKeySequence("F11")); - aInc->setShortcut(QKeySequence("F12")); + shortcutActive = true; + aSet->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSet")); + aDec->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDec")); + aInc->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aInc")); } } void AbstractCounter::setShortcutsInactive() { + shortcutActive = false; if (name == "life") { aSet->setShortcut(QKeySequence()); aDec->setShortcut(QKeySequence()); @@ -80,6 +87,12 @@ void AbstractCounter::setShortcutsInactive() } } +void AbstractCounter::refreshShortcuts() +{ + if(shortcutActive) + setShortcutsActive(); +} + void AbstractCounter::setValue(int _value) { value = _value; diff --git a/cockatrice/src/abstractcounter.h b/cockatrice/src/abstractcounter.h index 174cbf84..f5a04e9d 100644 --- a/cockatrice/src/abstractcounter.h +++ b/cockatrice/src/abstractcounter.h @@ -26,6 +26,7 @@ private: bool dialogSemaphore, deleteAfterDialog; bool shownInCounterArea; private slots: + void refreshShortcuts(); void incrementCounter(); void setCounter(); public: @@ -44,6 +45,7 @@ public: void setShortcutsActive(); void setShortcutsInactive(); + bool shortcutActive; }; #endif diff --git a/cockatrice/src/abstractgraphicsitem.cpp b/cockatrice/src/abstractgraphicsitem.cpp index c0cc330c..6fae46b1 100644 --- a/cockatrice/src/abstractgraphicsitem.cpp +++ b/cockatrice/src/abstractgraphicsitem.cpp @@ -17,15 +17,7 @@ void AbstractGraphicsItem::paintNumberEllipse(int number, int fontSize, const QC w = h; painter->setPen(QColor(255, 255, 255, 0)); - QRadialGradient grad(QPointF(0.5, 0.5), 0.5); - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - QColor color1(color), color2(color); - color1.setAlpha(255); - color2.setAlpha(0); - grad.setColorAt(0, color1); - grad.setColorAt(0.8, color1); - grad.setColorAt(1, color2); - painter->setBrush(QBrush(grad)); + painter->setBrush(QBrush(QColor(color))); QRectF textRect; if (position == -1) diff --git a/cockatrice/src/carddatabase.cpp b/cockatrice/src/carddatabase.cpp index d2e3a2f7..8978e70c 100644 --- a/cockatrice/src/carddatabase.cpp +++ b/cockatrice/src/carddatabase.cpp @@ -1,6 +1,8 @@ #include "carddatabase.h" #include "settingscache.h" #include "thememanager.h" + +#include #include #include #include @@ -92,7 +94,7 @@ void CardSet::setIsKnown(bool _isknown) settings.setValue("isknown", isknown); } -class SetList::CompareFunctor { +class SetList::KeyCompareFunctor { public: inline bool operator()(CardSet *a, CardSet *b) const { @@ -102,7 +104,39 @@ public: void SetList::sortByKey() { - qSort(begin(), end(), CompareFunctor()); + qSort(begin(), end(), KeyCompareFunctor()); +} + +class SetList::EnabledAndKeyCompareFunctor { +public: + inline bool operator()(CardSet *a, CardSet *b) const + { + if(a->getEnabled()) + { + if(b->getEnabled()) + { + // both enabled: sort by key + return a->getSortKey() < b->getSortKey(); + } else { + // only a enabled + return true; + } + } else { + if(b->getEnabled()) + { + // only b enabled + return false; + } else { + // both disabled: sort by key + return a->getSortKey() < b->getSortKey(); + } + } + } +}; + +void SetList::sortByEnabledAndKey() +{ + qSort(begin(), end(), EnabledAndKeyCompareFunctor()); } int SetList::getEnabledSetsNum() @@ -185,7 +219,7 @@ PictureToLoad::PictureToLoad(CardInfo *_card, bool _hq) { if (card) { sortedSets = card->getSets(); - sortedSets.sortByKey(); + sortedSets.sortByEnabledAndKey(); } } @@ -213,6 +247,9 @@ CardSet *PictureToLoad::getCurrentSet() const return 0; } +QStringList PictureLoader::md5Blacklist = QStringList() + << "db0c48db407a907c16ade38de048a441"; // card back returned by gatherer when card is not found + PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent) : QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq), @@ -338,16 +375,26 @@ QString PictureLoader::getPicUrl() picUrl = picDownloadHq ? settingsCache->getPicUrlHqFallback() : settingsCache->getPicUrlFallback(); picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName())); + picUrl.replace("!name_lower!", QUrl::toPercentEncoding(card->getCorrectedName().toLower())); picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(muid))); - if (set) { + if (set) + { picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName())); + picUrl.replace("!setcode_lower!", QUrl::toPercentEncoding(set->getShortName().toLower())); picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName())); + picUrl.replace("!setname_lower!", QUrl::toPercentEncoding(set->getLongName().toLower())); } - if (picUrl.contains("!name!") || - picUrl.contains("!setcode!") || - picUrl.contains("!setname!") || - picUrl.contains("!cardid!")) { + if ( + picUrl.contains("!name!") || + picUrl.contains("!name_lower!") || + picUrl.contains("!setcode!") || + picUrl.contains("!setcode_lower!") || + picUrl.contains("!setname!") || + picUrl.contains("!setname_lower!") || + picUrl.contains("!cardid!") + ) + { qDebug() << "Insufficient card data to download" << card->getName() << "Url:" << picUrl; return QString(""); } @@ -403,7 +450,28 @@ void PictureLoader::picDownloadFinished(QNetworkReply *reply) qDebug() << "Download failed:" << reply->errorString(); } + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) { + QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + QNetworkRequest req(redirectUrl); + qDebug() << "following redirect:" << cardBeingDownloaded.getCard()->getName() << "Url:" << req.url(); + networkManager->get(req); + return; + } + const QByteArray &picData = reply->peek(reply->size()); //peek is used to keep the data in the buffer for use by QImageReader + + // check if the image is blacklisted + QString md5sum = QCryptographicHash::hash(picData, QCryptographicHash::Md5).toHex(); + if(md5Blacklist.contains(md5sum)) + { + qDebug() << "Picture downloaded, but blacklisted (" << md5sum << "), will consider it as not found"; + picDownloadFailed(); + reply->deleteLater(); + startNextPicDownload(); + return; + } + QImage testImage; QImageReader imgReader; @@ -483,13 +551,16 @@ CardInfo::CardInfo(CardDatabase *_db, const QString &_powtough, const QString &_text, const QStringList &_colors, + const QStringList &_relatedCards, + bool _upsideDownArt, int _loyalty, bool _cipt, int _tableRow, const SetList &_sets, const QStringMap &_customPicURLs, const QStringMap &_customPicURLsHq, - MuidMap _muIds) + MuidMap _muIds + ) : db(_db), name(_name), isToken(_isToken), @@ -500,6 +571,8 @@ CardInfo::CardInfo(CardDatabase *_db, powtough(_powtough), text(_text), colors(_colors), + relatedCards(_relatedCards), + upsideDownArt(_upsideDownArt), loyalty(_loyalty), customPicURLs(_customPicURLs), customPicURLsHq(_customPicURLsHq), @@ -581,7 +654,13 @@ void CardInfo::loadPixmap(QPixmap &pixmap) void CardInfo::imageLoaded(const QImage &image) { if (!image.isNull()) { - QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); + if(upsideDownArt) + { + QImage mirrorImage = image.mirrored(true, true); + QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(mirrorImage)); + } else { + QPixmapCache::insert(pixmapCacheKey, QPixmap::fromImage(image)); + } emit pixmapUpdated(); } } @@ -686,6 +765,10 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) for (int i = 0; i < colors.size(); i++) xml.writeTextElement("color", colors[i]); + const QStringList &related = info->getRelatedCards(); + for (int i = 0; i < related.size(); i++) + xml.writeTextElement("related", related[i]); + xml.writeTextElement("manacost", info->getManaCost()); xml.writeTextElement("cmc", info->getCmc()); xml.writeTextElement("type", info->getCardType()); @@ -699,6 +782,8 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info) xml.writeTextElement("cipt", "1"); if (info->getIsToken()) xml.writeTextElement("token", "1"); + if (info->getUpsideDownArt()) + xml.writeTextElement("upsidedown", "1"); xml.writeEndElement(); // card return xml; @@ -815,6 +900,8 @@ void CardDatabase::clearPixmapCache() } if (noCard) noCard->clearPixmapCache(); + + QPixmapCache::clear(); } void CardDatabase::loadSetsFromXml(QXmlStreamReader &xml) @@ -853,7 +940,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) break; if (xml.name() == "card") { QString name, manacost, cmc, type, pt, text; - QStringList colors; + QStringList colors, relatedCards; QStringMap customPicURLs, customPicURLsHq; MuidMap muids; SetList sets; @@ -861,6 +948,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) int loyalty = 0; bool cipt = false; bool isToken = false; + bool upsideDown = false; while (!xml.atEnd()) { if (xml.readNext() == QXmlStreamReader::EndElement) break; @@ -891,10 +979,14 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } } else if (xml.name() == "color") colors << xml.readElementText(); + else if (xml.name() == "related") + relatedCards << xml.readElementText(); else if (xml.name() == "tablerow") tableRow = xml.readElementText().toInt(); else if (xml.name() == "cipt") cipt = (xml.readElementText() == "1"); + else if (xml.name() == "upsidedown") + upsideDown = (xml.readElementText() == "1"); else if (xml.name() == "loyalty") loyalty = xml.readElementText().toInt(); else if (xml.name() == "token") @@ -902,7 +994,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml, bool tokens) } if (isToken == tokens) { - addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); + addCard(new CardInfo(this, name, isToken, manacost, cmc, type, pt, text, colors, relatedCards, upsideDown, loyalty, cipt, tableRow, sets, customPicURLs, customPicURLsHq, muids)); } } } diff --git a/cockatrice/src/carddatabase.h b/cockatrice/src/carddatabase.h index 30fdc1e0..df3995da 100644 --- a/cockatrice/src/carddatabase.h +++ b/cockatrice/src/carddatabase.h @@ -54,8 +54,10 @@ public: class SetList : public QList { private: - class CompareFunctor; + class KeyCompareFunctor; + class EnabledAndKeyCompareFunctor; public: + void sortByEnabledAndKey(); void sortByKey(); void guessSortKeys(); void enableAllUnknown(); @@ -94,6 +96,7 @@ private: bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning; void startNextPicDownload(); QString getPicUrl(); + static QStringList md5Blacklist; public: PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0); ~PictureLoader(); @@ -132,6 +135,8 @@ private: QString powtough; QString text; QStringList colors; + QStringList relatedCards; + bool upsideDownArt; int loyalty; QStringMap customPicURLs, customPicURLsHq; MuidMap muIds; @@ -148,13 +153,16 @@ public: const QString &_powtough = QString(), const QString &_text = QString(), const QStringList &_colors = QStringList(), + const QStringList &_relatedCards = QStringList(), + bool _upsideDownArt = false, int _loyalty = 0, bool _cipt = false, int _tableRow = 0, const SetList &_sets = SetList(), const QStringMap &_customPicURLs = QStringMap(), const QStringMap &_customPicURLsHq = QStringMap(), - MuidMap muids = MuidMap()); + MuidMap muids = MuidMap() + ); ~CardInfo(); const QString &getName() const { return name; } const QString &getSimpleName() const { return simpleName; } @@ -174,6 +182,8 @@ public: void setText(const QString &_text) { text = _text; emit cardInfoChanged(this); } void setColors(const QStringList &_colors) { colors = _colors; emit cardInfoChanged(this); } const QStringList &getColors() const { return colors; } + const QStringList &getRelatedCards() const { return relatedCards; } + bool getUpsideDownArt() const { return upsideDownArt; } QString getCustomPicURL(const QString &set) const { return customPicURLs.value(set); } QString getCustomPicURLHq(const QString &set) const { return customPicURLsHq.value(set); } int getMuId(const QString &set) const { return muIds.value(set); } diff --git a/cockatrice/src/cardframe.cpp b/cockatrice/src/cardframe.cpp index bf82cad3..d91a8ecf 100644 --- a/cockatrice/src/cardframe.cpp +++ b/cockatrice/src/cardframe.cpp @@ -7,42 +7,52 @@ #include "cardinfotext.h" #include "settingscache.h" +#include #include -CardFrame::CardFrame(int width, int height, - const QString &cardName, QWidget *parent) - : QTabWidget(parent) - , info(0) - , cardTextOnly(false) +CardFrame::CardFrame(const QString &cardName, QWidget *parent) + : QTabWidget(parent), info(0), cardTextOnly(false) { - setMaximumWidth(width); - setMinimumWidth(width); - setMinimumHeight(height); - - pic = new CardInfoPicture(width); + setContentsMargins(3, 3, 3, 3); + pic = new CardInfoPicture(); + pic->setObjectName("pic"); text = new CardInfoText(); + text->setObjectName("text"); tab1 = new QWidget(this); tab2 = new QWidget(this); tab3 = new QWidget(this); + + tab1->setObjectName("tab1"); + tab2->setObjectName("tab2"); + tab3->setObjectName("tab3"); + insertTab(ImageOnlyView, tab1, QString()); insertTab(TextOnlyView, tab2, QString()); insertTab(ImageAndTextView, tab3, QString()); connect(this, SIGNAL(currentChanged(int)), this, SLOT(setViewMode(int))); tab1Layout = new QVBoxLayout(); + tab1Layout->setObjectName("tab1Layout"); tab1Layout->setContentsMargins(0, 0, 0, 0); tab1Layout->setSpacing(0); tab1->setLayout(tab1Layout); tab2Layout = new QVBoxLayout(); + tab2Layout->setObjectName("tab2Layout"); tab2Layout->setContentsMargins(0, 0, 0, 0); tab2Layout->setSpacing(0); tab2->setLayout(tab2Layout); + splitter = new QSplitter(); + splitter->setObjectName("splitter"); + splitter->setOrientation(Qt::Vertical); + tab3Layout = new QVBoxLayout(); + tab3Layout->setObjectName("tab3Layout"); tab3Layout->setContentsMargins(0, 0, 0, 0); tab3Layout->setSpacing(0); + tab3Layout->addWidget(splitter); tab3->setLayout(tab3Layout); setViewMode(settingsCache->getCardInfoViewMode()); @@ -70,8 +80,8 @@ void CardFrame::setViewMode(int mode) tab2Layout->addWidget(text); break; case ImageAndTextView: - tab3Layout->addWidget(pic); - tab3Layout->addWidget(text); + splitter->addWidget(pic); + splitter->addWidget(text); break; } diff --git a/cockatrice/src/cardframe.h b/cockatrice/src/cardframe.h index 3f00ad99..cbd776c0 100644 --- a/cockatrice/src/cardframe.h +++ b/cockatrice/src/cardframe.h @@ -8,6 +8,7 @@ class CardInfo; class CardInfoPicture; class CardInfoText; class QVBoxLayout; +class QSplitter; class CardFrame : public QTabWidget { Q_OBJECT @@ -19,11 +20,11 @@ private: bool cardTextOnly; QWidget *tab1, *tab2, *tab3; QVBoxLayout *tab1Layout, *tab2Layout, *tab3Layout; - + QSplitter *splitter; public: enum ViewMode { ImageOnlyView, TextOnlyView, ImageAndTextView }; - CardFrame(int width, int height, const QString &cardName = QString(), + CardFrame(const QString &cardName = QString(), QWidget *parent = 0); void retranslateUi(); public slots: diff --git a/cockatrice/src/cardinfopicture.cpp b/cockatrice/src/cardinfopicture.cpp index 3cdf9097..9bce273e 100644 --- a/cockatrice/src/cardinfopicture.cpp +++ b/cockatrice/src/cardinfopicture.cpp @@ -1,25 +1,19 @@ #include "cardinfopicture.h" -#include +#include +#include +#include + #include "carditem.h" #include "carddatabase.h" #include "main.h" -CardInfoPicture::CardInfoPicture(int maximumWidth, QWidget *parent) - : QLabel(parent) - , info(0) - , noPicture(true) +CardInfoPicture::CardInfoPicture(QWidget *parent) + : QWidget(parent), + info(0), + pixmapDirty(true) { - setAlignment(Qt::AlignCenter); - setMaximumWidth(maximumWidth); -} - -void CardInfoPicture::setNoPicture(bool status) -{ - if (noPicture != status) { - noPicture = status; - emit hasPictureChanged(); - } + setMinimumHeight(100); } void CardInfoPicture::setCard(CardInfo *card) @@ -32,26 +26,37 @@ void CardInfoPicture::setCard(CardInfo *card) updatePixmap(); } -void CardInfoPicture::resizeEvent(QResizeEvent * /* e */) +void CardInfoPicture::resizeEvent(QResizeEvent *) { updatePixmap(); } void CardInfoPicture::updatePixmap() { - if (info == 0 || width() == 0 || height() == 0) { - setNoPicture(true); - return; - } - - QPixmap resizedPixmap; - info->getPixmap(size(), resizedPixmap); - - if (resizedPixmap.isNull()) { - setNoPicture(true); - db->getCard()->getPixmap(size(), resizedPixmap); - } else { - setNoPicture(false); - } - this->setPixmap(resizedPixmap); + pixmapDirty = true; + update(); +} + +void CardInfoPicture::loadPixmap() +{ + if(info) + info->getPixmap(size(), resizedPixmap); + else + resizedPixmap = QPixmap(); + + + if (resizedPixmap.isNull()) + db->getCard()->getPixmap(size(), resizedPixmap); +} + +void CardInfoPicture::paintEvent(QPaintEvent *) +{ + if (width() == 0 || height() == 0) + return; + + if(pixmapDirty) + loadPixmap(); + + QPainter painter(this); + style()->drawItemPixmap(&painter, rect(), Qt::AlignHCenter, resizedPixmap); } diff --git a/cockatrice/src/cardinfopicture.h b/cockatrice/src/cardinfopicture.h index 380260c4..50cf3e84 100644 --- a/cockatrice/src/cardinfopicture.h +++ b/cockatrice/src/cardinfopicture.h @@ -1,29 +1,25 @@ #ifndef CARDINFOPICTURE_H #define CARDINFOPICTURE_H -#include +#include class AbstractCardItem; class CardInfo; -class CardInfoPicture : public QLabel { +class CardInfoPicture : public QWidget { Q_OBJECT -signals: - void hasPictureChanged(); - private: CardInfo *info; - bool noPicture; + QPixmap resizedPixmap; + bool pixmapDirty; public: - CardInfoPicture(int maximumWidth, QWidget *parent = 0); - bool hasPicture() const { return !noPicture; } -private: - void setNoPicture(bool status); + CardInfoPicture(QWidget *parent = 0); protected: void resizeEvent(QResizeEvent *event); - + void paintEvent(QPaintEvent *); + void loadPixmap(); public slots: void setCard(CardInfo *card); void updatePixmap(); diff --git a/cockatrice/src/cardinfotext.cpp b/cockatrice/src/cardinfotext.cpp index 21336a83..ace2a95f 100644 --- a/cockatrice/src/cardinfotext.cpp +++ b/cockatrice/src/cardinfotext.cpp @@ -8,8 +8,7 @@ #include "main.h" CardInfoText::CardInfoText(QWidget *parent) - : QFrame(parent) - , info(0) + : QFrame(parent), info(0) { nameLabel1 = new QLabel; nameLabel2 = new QLabel; @@ -17,6 +16,9 @@ CardInfoText::CardInfoText(QWidget *parent) manacostLabel1 = new QLabel; manacostLabel2 = new QLabel; manacostLabel2->setWordWrap(true); + colorLabel1 = new QLabel; + colorLabel2 = new QLabel; + colorLabel2->setWordWrap(true); cardtypeLabel1 = new QLabel; cardtypeLabel2 = new QLabel; cardtypeLabel2->setWordWrap(true); @@ -34,6 +36,8 @@ CardInfoText::CardInfoText(QWidget *parent) grid->addWidget(nameLabel2, row++, 1); grid->addWidget(manacostLabel1, row, 0); grid->addWidget(manacostLabel2, row++, 1); + grid->addWidget(colorLabel1, row, 0); + grid->addWidget(colorLabel2, row++, 1); grid->addWidget(cardtypeLabel1, row, 0); grid->addWidget(cardtypeLabel2, row++, 1); grid->addWidget(powtoughLabel1, row, 0); @@ -51,6 +55,7 @@ void CardInfoText::setCard(CardInfo *card) { nameLabel2->setText(card->getName()); manacostLabel2->setText(card->getManaCost()); + colorLabel2->setText(card->getColors().join("")); cardtypeLabel2->setText(card->getCardType()); powtoughLabel2->setText(card->getPowTough()); loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString()); @@ -61,6 +66,7 @@ void CardInfoText::retranslateUi() { nameLabel1->setText(tr("Name:")); manacostLabel1->setText(tr("Mana cost:")); + colorLabel1->setText(tr("Color(s):")); cardtypeLabel1->setText(tr("Card type:")); powtoughLabel1->setText(tr("P / T:")); loyaltyLabel1->setText(tr("Loyalty:")); diff --git a/cockatrice/src/cardinfotext.h b/cockatrice/src/cardinfotext.h index c0f59480..2c7da466 100644 --- a/cockatrice/src/cardinfotext.h +++ b/cockatrice/src/cardinfotext.h @@ -13,6 +13,7 @@ class CardInfoText : public QFrame { private: QLabel *nameLabel1, *nameLabel2; QLabel *manacostLabel1, *manacostLabel2; + QLabel *colorLabel1, *colorLabel2; QLabel *cardtypeLabel1, *cardtypeLabel2; QLabel *powtoughLabel1, *powtoughLabel2; QLabel *loyaltyLabel1, *loyaltyLabel2; diff --git a/cockatrice/src/cardinfowidget.cpp b/cockatrice/src/cardinfowidget.cpp index 57d59552..8ee5663c 100644 --- a/cockatrice/src/cardinfowidget.cpp +++ b/cockatrice/src/cardinfowidget.cpp @@ -39,6 +39,9 @@ CardInfoWidget::CardInfoWidget(ResizeMode _mode, const QString &cardName, QWidge manacostLabel1 = new QLabel; manacostLabel2 = new QLabel; manacostLabel2->setWordWrap(true); + colorLabel1 = new QLabel; + colorLabel2 = new QLabel; + colorLabel2->setWordWrap(true); cardtypeLabel1 = new QLabel; cardtypeLabel2 = new QLabel; cardtypeLabel2->setWordWrap(true); @@ -59,6 +62,8 @@ CardInfoWidget::CardInfoWidget(ResizeMode _mode, const QString &cardName, QWidge grid->addWidget(nameLabel2, row++, 1); grid->addWidget(manacostLabel1, row, 0); grid->addWidget(manacostLabel2, row++, 1); + grid->addWidget(colorLabel1, row, 0); + grid->addWidget(colorLabel2, row++, 1); grid->addWidget(cardtypeLabel1, row, 0); grid->addWidget(cardtypeLabel2, row++, 1); grid->addWidget(powtoughLabel1, row, 0); @@ -94,14 +99,12 @@ void CardInfoWidget::minimizeClicked(int newMinimized) bool CardInfoWidget::shouldShowPowTough() { -// return (!info->getPowTough().isEmpty() && (minimized != 0)); - return (minimized != 0); + return !info->getPowTough().isEmpty(); } bool CardInfoWidget::shouldShowLoyalty() { -// return ((info->getLoyalty() > 0) && (minimized != 0)); - return (minimized != 0); + return (info->getLoyalty() > 0); } void CardInfoWidget::setMinimized(int _minimized) @@ -117,6 +120,8 @@ void CardInfoWidget::setMinimized(int _minimized) nameLabel2->setVisible(showAll); manacostLabel1->setVisible(showAll); manacostLabel2->setVisible(showAll); + colorLabel1->setVisible(showAll); + colorLabel2->setVisible(showAll); cardtypeLabel1->setVisible(showAll); cardtypeLabel2->setVisible(showAll); powtoughLabel1->setVisible(showPowTough); @@ -153,6 +158,7 @@ void CardInfoWidget::setCard(CardInfo *card) updatePixmap(); nameLabel2->setText(card->getName()); manacostLabel2->setText(card->getManaCost()); + colorLabel2->setText(card->getColors().join("")); cardtypeLabel2->setText(card->getCardType()); powtoughLabel2->setText(card->getPowTough()); loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString()); @@ -200,6 +206,7 @@ void CardInfoWidget::retranslateUi() { nameLabel1->setText(tr("Name:")); manacostLabel1->setText(tr("Mana cost:")); + colorLabel1->setText(tr("Color(s):")); cardtypeLabel1->setText(tr("Card type:")); powtoughLabel1->setText(tr("P / T:")); loyaltyLabel1->setText(tr("Loyalty:")); diff --git a/cockatrice/src/cardinfowidget.h b/cockatrice/src/cardinfowidget.h index b9988a7a..19e9cc61 100644 --- a/cockatrice/src/cardinfowidget.h +++ b/cockatrice/src/cardinfowidget.h @@ -31,6 +31,7 @@ private: QLabel *cardPicture; QLabel *nameLabel1, *nameLabel2; QLabel *manacostLabel1, *manacostLabel2; + QLabel *colorLabel1, *colorLabel2; QLabel *cardtypeLabel1, *cardtypeLabel2; QLabel *powtoughLabel1, *powtoughLabel2; QLabel *loyaltyLabel1, *loyaltyLabel2; diff --git a/cockatrice/src/carditem.cpp b/cockatrice/src/carditem.cpp index 43d24dcd..0ae87ee8 100644 --- a/cockatrice/src/carditem.cpp +++ b/cockatrice/src/carditem.cpp @@ -110,7 +110,7 @@ void CardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QStringList ptDbSplit = db->getCard(name)->getPowTough().split("/"); QStringList ptSplit = pt.split("/"); - if (ptDbSplit.at(0) != ptSplit.at(0) || ptDbSplit.at(1) != ptSplit.at(1)) + if (getFaceDown() || ptDbSplit.at(0) != ptSplit.at(0) || ptDbSplit.at(1) != ptSplit.at(1)) painter->setPen(QColor(255, 150, 0)); else painter->setPen(Qt::white); @@ -358,7 +358,7 @@ void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void CardItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { - if (settingsCache->getDoubleClickToPlay()) { + if (settingsCache->getDoubleClickToPlay() && event->buttons() == Qt::LeftButton) { if (revealedCard) zone->removeCard(this); else diff --git a/cockatrice/src/chatview.cpp b/cockatrice/src/chatview.cpp index c11f2d1c..00dbd3a6 100644 --- a/cockatrice/src/chatview.cpp +++ b/cockatrice/src/chatview.cpp @@ -53,14 +53,15 @@ QTextCursor ChatView::prepareBlock(bool same) QTextCursor cursor(document()->lastBlock()); cursor.movePosition(QTextCursor::End); - if (!same) { + if (same) { + cursor.insertHtml("
"); + } else { QTextBlockFormat blockFormat; if ((evenNumber = !evenNumber)) blockFormat.setBackground(palette().alternateBase()); blockFormat.setBottomMargin(4); cursor.insertBlock(blockFormat); - } else - cursor.insertHtml("
"); + } return cursor; } @@ -73,10 +74,16 @@ void ChatView::appendHtml(const QString &html) verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } -void ChatView::appendHtmlServerMessage(const QString &html) +void ChatView::appendHtmlServerMessage(const QString &html, bool optionalIsBold, QString optionalFontColor) { bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum(); - prepareBlock().insertHtml("" + html + ""); + + QString htmlText = "" + html + ""; + + if (optionalIsBold) + htmlText = "" + htmlText + ""; + + prepareBlock().insertHtml(htmlText); if (atBottom) verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } @@ -120,6 +127,7 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use QTextCursor cursor = prepareBlock(sameSender); lastSender = sender; + // timestamp if (showTimestamps && !sameSender) { QTextCharFormat timeFormat; timeFormat.setForeground(QColor(SERVER_MESSAGE_COLOR)); @@ -128,7 +136,8 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use cursor.setCharFormat(timeFormat); cursor.insertText(QDateTime::currentDateTime().toString("[hh:mm:ss] ")); } - + + // nickname QTextCharFormat senderFormat; if (tabSupervisor && tabSupervisor->getUserInfo() && (sender == QString::fromStdString(tabSupervisor->getUserInfo()->name()))) { senderFormat.setForeground(QBrush(getCustomMentionColor())); @@ -140,7 +149,9 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use } senderFormat.setAnchor(true); senderFormat.setAnchorHref("user://" + QString::number(userLevel) + "_" + sender); - if (!sameSender) { + if (sameSender) { + cursor.insertText(" "); + } else { if (!sender.isEmpty() && tabSupervisor->getUserListsTab()) { const int pixelSize = QFontInfo(cursor.charFormat().font()).pixelSize(); QMap buddyList = tabSupervisor->getUserListsTab()->getBuddyList()->getUsers(); @@ -151,142 +162,48 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use if (!sender.isEmpty()) sender.append(": "); cursor.insertText(sender); - } else - cursor.insertText(" "); - - QTextCharFormat messageFormat; - if (sender.isEmpty()) { - messageFormat.setForeground(Qt::darkGreen); - messageFormat.setFontWeight(QFont::Bold); } - cursor.setCharFormat(messageFormat); - - int index = -1, bracketFirstIndex = -1, mentionFirstIndex = -1, urlFirstIndex = -1; + + // use different color for server messages + defaultFormat = QTextCharFormat(); + if (sender.isEmpty()) { + defaultFormat.setForeground(Qt::darkGreen); + defaultFormat.setFontWeight(QFont::Bold); + } + cursor.setCharFormat(defaultFormat); + bool mentionEnabled = settingsCache->getChatMention(); + highlightedWords = settingsCache->getHighlightWords().split(' ', QString::SkipEmptyParts); + + // parse the message while (message.size()) { - // search for the first [ or @ - bracketFirstIndex = message.indexOf('['); - mentionFirstIndex = mentionEnabled ? message.indexOf('@') : -1; - urlFirstIndex = message.indexOf(QRegExp("https?://|www.")); - if(bracketFirstIndex == -1) { - if(mentionFirstIndex == -1) { - if (urlFirstIndex == -1) { - // quick way out - cursor.insertText(message); - break; - } else { - // url - index = urlFirstIndex; - } - } else { - if (urlFirstIndex == -1) { - // mention - index = mentionFirstIndex; - } else { - index = std::min(urlFirstIndex, mentionFirstIndex); - } - } - } else { - if(mentionFirstIndex == -1) { - // bracket - index = bracketFirstIndex; - } else { - // both, pick up the first one - index = std::min(bracketFirstIndex, mentionFirstIndex); - } - if(urlFirstIndex != -1) { - index = std::min(index, urlFirstIndex); - } - } - - // insert the message text up to the [ / @ / https:// - if(index > 0) + QChar c = message.at(0); + switch(c.toLatin1()) { - cursor.insertText(message.left(index), defaultFormat); - message = message.mid(index); - } - - if(index == bracketFirstIndex) - { - if (message.startsWith("[card]")) { - message = message.mid(6); - int closeTagIndex = message.indexOf("[/card]"); - QString cardName = message.left(closeTagIndex); - if (closeTagIndex == -1) - message.clear(); - else - message = message.mid(closeTagIndex + 7); - - appendCardTag(cursor, cardName); - } else if (message.startsWith("[[")) { - message = message.mid(2); - int closeTagIndex = message.indexOf("]]"); - QString cardName = message.left(closeTagIndex); - if (closeTagIndex == -1) - message.clear(); - else - message = message.mid(closeTagIndex + 2); - - appendCardTag(cursor, cardName); - } else if (message.startsWith("[url]")) { - message = message.mid(5); - int closeTagIndex = message.indexOf("[/url]"); - QString url = message.left(closeTagIndex); - if (closeTagIndex == -1) - message.clear(); - else - message = message.mid(closeTagIndex + 6); - - appendUrlTag(cursor, url); - } else { - // not a recognized [tag] - cursor.insertText("[", defaultFormat); + case '[': + checkTag(cursor, message); + break; + case '@': + if(mentionEnabled) { + checkMention(cursor, message, sender, userLevel); + } else { + cursor.insertText(c, defaultFormat); + message = message.mid(1); + } + break; + case ' ': + cursor.insertText(c, defaultFormat); message = message.mid(1); - } - } else if (index == urlFirstIndex) { - int urlEndIndex = message.indexOf(QRegExp("\\s"), 0); - if (urlEndIndex == -1) - urlEndIndex = message.size(); - QString urlText = message.left(urlEndIndex); - QUrl qUrl(urlText); - if (qUrl.isValid()) - appendUrlTag(cursor, urlText); - else - cursor.insertText(urlText); - if (urlEndIndex == -1) - message.clear(); - else - message = message.mid(urlEndIndex); - } else { - if (message.startsWith(mention, Qt::CaseInsensitive)) { - // you have been mentioned - mentionFormat.setBackground(QBrush(getCustomMentionColor())); - mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white):QBrush(Qt::black)); - cursor.insertText(mention, mentionFormat); - message = message.mid(mention.size()); - QApplication::alert(this); - if (settingsCache->getShowMentionPopup() && shouldShowSystemPopup()) { - QString ref = sender.left(sender.length() - 2); - showSystemPopup(ref); + break; + default: + if(c.isLetterOrNumber()) { + checkWord(cursor, message); + } else { + cursor.insertText(c, defaultFormat); + message = message.mid(1); } - } else { - int mentionEndIndex = message.indexOf(QRegExp("\\s"), 1);// from 1 as @ is non-char - if (mentionEndIndex == -1) - mentionEndIndex = message.size(); // there is no text after the mention - QString userMention = message.left(mentionEndIndex); - QString userName = userMention.right(userMention.size()-1).normalized(QString::NormalizationForm_D); - QMap userList = tabSupervisor->getUserListsTab()->getAllUsersList()->getUsers(); - QString correctUserName = getNameFromUserList(userList, userName); - if (!correctUserName.isEmpty()) { - UserListTWI *vlu = userList.value(correctUserName); - mentionFormatOtherUser.setAnchorHref("user://" + QString::number(vlu->getUserInfo().user_level()) + "_" + correctUserName); - cursor.insertText("@" + correctUserName, mentionFormatOtherUser); - } else - cursor.insertText("@" + userName, defaultFormat); - message = message.mid(userName.size() + 1); - } - cursor.setCharFormat(defaultFormat); // reset format after each iteration + break; } } @@ -294,6 +211,207 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use verticalScrollBar()->setValue(verticalScrollBar()->maximum()); } +void ChatView::checkTag(QTextCursor &cursor, QString &message) +{ + if (message.startsWith("[card]")) + { + message = message.mid(6); + int closeTagIndex = message.indexOf("[/card]"); + QString cardName = message.left(closeTagIndex); + if (closeTagIndex == -1) + message.clear(); + else + message = message.mid(closeTagIndex + 7); + + appendCardTag(cursor, cardName); + return; + } + + if (message.startsWith("[[")) + { + message = message.mid(2); + int closeTagIndex = message.indexOf("]]"); + QString cardName = message.left(closeTagIndex); + if (closeTagIndex == -1) + message.clear(); + else + message = message.mid(closeTagIndex + 2); + + appendCardTag(cursor, cardName); + return; + } + + if (message.startsWith("[url]")) + { + message = message.mid(5); + int closeTagIndex = message.indexOf("[/url]"); + QString url = message.left(closeTagIndex); + if (closeTagIndex == -1) + message.clear(); + else + message = message.mid(closeTagIndex + 6); + + appendUrlTag(cursor, url); + return; + } + + // no valid tag found + checkWord(cursor, message); +} + +void ChatView::checkMention(QTextCursor &cursor, QString &message, QString &sender, UserLevelFlags userLevel) +{ + const QRegExp notALetterOrNumber = QRegExp("[^a-zA-Z0-9]"); + + int firstSpace = message.indexOf(' '); + QString fullMentionUpToSpaceOrEnd = (firstSpace == -1) ? message.mid(1) : message.mid(1, firstSpace - 1); + QString mentionIntact = fullMentionUpToSpaceOrEnd; + + QMap userList = tabSupervisor->getUserListsTab()->getAllUsersList()->getUsers(); + + while (fullMentionUpToSpaceOrEnd.size()) + { + if (isFullMentionAValidUser(userList, fullMentionUpToSpaceOrEnd)) // Is there a user online named this? + { + if (userName.toLower() == fullMentionUpToSpaceOrEnd.toLower()) // Is this user you? + { + // You have received a valid mention!! + mentionFormat.setBackground(QBrush(getCustomMentionColor())); + mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white) : QBrush(Qt::black)); + cursor.insertText(mention, mentionFormat); + message = message.mid(mention.size()); + QApplication::alert(this); + if (settingsCache->getShowMentionPopup() && shouldShowSystemPopup()) + { + QString ref = sender.left(sender.length() - 2); + showSystemPopup(ref); + } + } else { + QString correctUserName = getNameFromUserList(userList, fullMentionUpToSpaceOrEnd); + UserListTWI *vlu = userList.value(correctUserName); + mentionFormatOtherUser.setAnchorHref("user://" + QString::number(vlu->getUserInfo().user_level()) + "_" + correctUserName); + cursor.insertText("@" + correctUserName, mentionFormatOtherUser); + + message = message.mid(correctUserName.size() + 1); + } + + cursor.setCharFormat(defaultFormat); + return; + } + + if (isModeratorSendingGlobal(userLevel, fullMentionUpToSpaceOrEnd)) { + // Moderator Sending Global Message + mentionFormat.setBackground(QBrush(getCustomMentionColor())); + mentionFormat.setForeground(settingsCache->getChatMentionForeground() ? QBrush(Qt::white) : QBrush(Qt::black)); + cursor.insertText("@" + fullMentionUpToSpaceOrEnd, mentionFormat); + message = message.mid(fullMentionUpToSpaceOrEnd.size() + 1); + QApplication::alert(this); + if (settingsCache->getShowMentionPopup() && shouldShowSystemPopup()) + { + QString ref = sender.left(sender.length() - 2); + showSystemPopup(ref); + } + + cursor.setCharFormat(defaultFormat); + return; + } + + if (fullMentionUpToSpaceOrEnd.right(1).indexOf(notALetterOrNumber) == -1 || fullMentionUpToSpaceOrEnd.size() < 2) + { + cursor.insertText("@" + mentionIntact, defaultFormat); + message = message.mid(mentionIntact.size() + 1); + cursor.setCharFormat(defaultFormat); + return; + } + + fullMentionUpToSpaceOrEnd.chop(1); + } + + // no valid mention found + checkWord(cursor, message); +} + +void ChatView::checkWord(QTextCursor &cursor, QString &message) +{ + // extract the first word + QString rest; + QString fullWordUpToSpaceOrEnd = extractNextWord(message, rest); + + // check urls + if (fullWordUpToSpaceOrEnd.startsWith("http://", Qt::CaseInsensitive) || + fullWordUpToSpaceOrEnd.startsWith("https://", Qt::CaseInsensitive) || + fullWordUpToSpaceOrEnd.startsWith("www.", Qt::CaseInsensitive)) + { + QUrl qUrl(fullWordUpToSpaceOrEnd); + if (qUrl.isValid()) + { + appendUrlTag(cursor, fullWordUpToSpaceOrEnd); + cursor.insertText(rest, defaultFormat); + return; + } + } + + // check word mentions + foreach (QString word, highlightedWords) + { + if (fullWordUpToSpaceOrEnd.compare(word, Qt::CaseInsensitive) == 0) + { + // You have received a valid mention of custom word!! + highlightFormat.setBackground(QBrush(getCustomHighlightColor())); + highlightFormat.setForeground(settingsCache->getChatHighlightForeground() ? QBrush(Qt::white) : QBrush(Qt::black)); + cursor.insertText(fullWordUpToSpaceOrEnd, highlightFormat); + cursor.insertText(rest, defaultFormat); + QApplication::alert(this); + return; + } + } + + // not a special word; just print it + cursor.insertText(fullWordUpToSpaceOrEnd + rest, defaultFormat); +} + +QString ChatView::extractNextWord(QString &message, QString &rest) +{ + // get the first next space and extract the word + QString word; + int firstSpace = message.indexOf(' '); + if(firstSpace == -1) + { + word = message; + message.clear(); + } else { + word = message.mid(0, firstSpace); + message = message.mid(firstSpace); + } + + // remove any punctution from the end and pass it separately + for (int len = word.size() - 1; len >= 0; --len) + { + if(word.at(len).isLetterOrNumber()) + { + rest = word.mid(len + 1); + return word.mid(0, len + 1); + } + } + + rest = word; + return QString(); +} + + +bool ChatView::isModeratorSendingGlobal(QFlags userLevelFlag, QString message) +{ + int userLevel = QString::number(userLevelFlag).toInt(); + + QStringList getAttentionList; + getAttentionList << "/all"; // Send a message to all users + + if (getAttentionList.contains(message) && (userLevel & ServerInfo_User::IsModerator || userLevel & ServerInfo_User::IsAdmin)) + return true; + + return false; +} + void ChatView::actMessageClicked() { emit messageClickedSignal(); } @@ -306,13 +424,18 @@ void ChatView::showSystemPopup(QString &sender) { emit showMentionPopup(sender); } - QColor ChatView::getCustomMentionColor() { QColor customColor; customColor.setNamedColor("#" + settingsCache->getChatMentionColor()); return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR; } +QColor ChatView::getCustomHighlightColor() { + QColor customColor; + customColor.setNamedColor("#" + settingsCache->getChatHighlightColor()); + return customColor.isValid() ? customColor : DEFAULT_MENTION_COLOR; +} + /** Returns the correct case version of the provided username, if no correct casing version was found then the provided name is not available and will return an empty QString. @@ -327,6 +450,18 @@ QString ChatView::getNameFromUserList(QMap &userList, QS return QString(); } +bool ChatView::isFullMentionAValidUser(QMap &userList, QString userNameToMatch) +{ + QString userNameToMatchLower = userNameToMatch.toLower(); + QMap::iterator i; + + for (i = userList.begin(); i != userList.end(); ++i) + if (i.key().toLower() == userNameToMatchLower) + return true; + + return false; +} + void ChatView::clearChat() { document()->clear(); lastSender = ""; diff --git a/cockatrice/src/chatview.h b/cockatrice/src/chatview.h index b7437f45..d8fdc764 100644 --- a/cockatrice/src/chatview.h +++ b/cockatrice/src/chatview.h @@ -27,8 +27,10 @@ private: QString userName; QString mention; QTextCharFormat mentionFormat; + QTextCharFormat highlightFormat; QTextCharFormat mentionFormatOtherUser; QTextCharFormat defaultFormat; + QStringList highlightedWords; bool evenNumber; bool showTimestamps; HoveredItemType hoveredItemType; @@ -39,9 +41,16 @@ private: void appendCardTag(QTextCursor &cursor, const QString &cardName); void appendUrlTag(QTextCursor &cursor, QString url); QString getNameFromUserList(QMap &userList, QString &userName); + bool isFullMentionAValidUser(QMap &userList, QString userNameToMatch); QColor getCustomMentionColor(); + QColor getCustomHighlightColor(); bool shouldShowSystemPopup(); void showSystemPopup(QString &sender); + bool isModeratorSendingGlobal(QFlags userLevelFlag, QString message); + void checkTag(QTextCursor &cursor, QString &message); + void checkMention(QTextCursor &cursor, QString &message, QString &sender, UserLevelFlags userLevel); + void checkWord(QTextCursor &cursor, QString &message); + QString extractNextWord(QString &message, QString &rest); private slots: void openLink(const QUrl &link); void actMessageClicked(); @@ -49,7 +58,7 @@ public: ChatView(const TabSupervisor *_tabSupervisor, TabGame *_game, bool _showTimestamps, QWidget *parent = 0); void retranslateUi(); void appendHtml(const QString &html); - void appendHtmlServerMessage(const QString &html); + void appendHtmlServerMessage(const QString &html, bool optionalIsBold = false, QString optionalFontColor = QString()); void appendMessage(QString message, QString sender = QString(), UserLevelFlags userLevel = UserLevelFlags(), bool playerBold = false); void clearChat(); protected: diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index f08fe992..d2f1319e 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -108,6 +108,9 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const int color = 90 + 60 * node->depth(); return QBrush(QColor(color, 255, color)); } + case Qt::ForegroundRole: { + return QBrush(QColor(0 ,0 ,0)); + } default: return QVariant(); } } else { @@ -125,6 +128,9 @@ QVariant DeckListModel::data(const QModelIndex &index, int role) const int color = 255 - (index.row() % 2) * 30; return QBrush(QColor(color, color, color)); } + case Qt::ForegroundRole: { + return QBrush(QColor(0 ,0 ,0)); + } default: return QVariant(); } } diff --git a/cockatrice/src/deckview.cpp b/cockatrice/src/deckview.cpp index 0f439c17..cd5d1022 100644 --- a/cockatrice/src/deckview.cpp +++ b/cockatrice/src/deckview.cpp @@ -228,6 +228,13 @@ QSizeF DeckViewCardContainer::calculateBoundingRect(const QList return QSizeF(getCardTypeTextWidth() + totalWidth, totalHeight + separatorY + paddingY); } +bool DeckViewCardContainer::sortCardsByName(DeckViewCard * c1, DeckViewCard * c2) +{ + if (c1 && c2) + return c1->getName() < c2->getName(); + return false; +} + void DeckViewCardContainer::rearrangeItems(const QList > &rowsAndCols) { currentRowsAndCols = rowsAndCols; @@ -244,6 +251,7 @@ void DeckViewCardContainer::rearrangeItems(const QList > &rowsAn QList cardTypeList = cardsByType.uniqueKeys(); QList row = cardsByType.values(cardTypeList[i]); + qSort( row.begin(), row.end(), DeckViewCardContainer::sortCardsByName); for (int j = 0; j < row.size(); ++j) { DeckViewCard *card = row[j]; card->setPos(x + (j % tempCols) * CARD_WIDTH, yUntilNow + (j / tempCols) * CARD_HEIGHT); diff --git a/cockatrice/src/deckview.h b/cockatrice/src/deckview.h index 91246eb2..080f80c1 100644 --- a/cockatrice/src/deckview.h +++ b/cockatrice/src/deckview.h @@ -46,6 +46,7 @@ class DeckViewCardContainer : public QGraphicsItem { private: static const int separatorY = 20; static const int paddingY = 10; + static bool sortCardsByName(DeckViewCard * c1, DeckViewCard * c2); QString name; QList cards; diff --git a/cockatrice/src/dlg_connect.cpp b/cockatrice/src/dlg_connect.cpp index d3bc4027..c97e4e6d 100644 --- a/cockatrice/src/dlg_connect.cpp +++ b/cockatrice/src/dlg_connect.cpp @@ -1,10 +1,14 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include #include #include "dlg_connect.h" @@ -14,8 +18,24 @@ DlgConnect::DlgConnect(QWidget *parent) QSettings settings; settings.beginGroup("server"); + previousHostButton = new QRadioButton(tr("Previous Host"), this); + + previousHosts = new QComboBox(this); + previousHosts->installEventFilter(new DeleteHighlightedItemWhenShiftDelPressedEventFilter); + QStringList previousHostList = settings.value("previoushosts").toStringList(); + if (previousHostList.isEmpty()) { + previousHostList << "cockatrice.woogerworks.com"; + previousHostList << "vps.poixen.com"; + previousHostList << "chickatrice.net"; + } + previousHosts->addItems(previousHostList); + previousHosts->setCurrentIndex(settings.value("previoushostindex").toInt()); + + newHostButton = new QRadioButton(tr("New Host"), this); + hostLabel = new QLabel(tr("&Host:")); - hostEdit = new QLineEdit(settings.value("hostname", "cockatrice.woogerworks.com").toString()); + hostEdit = new QLineEdit(); + hostEdit->setPlaceholderText(tr("Enter host name")); hostLabel->setBuddy(hostEdit); portLabel = new QLabel(tr("&Port:")); @@ -48,16 +68,19 @@ DlgConnect::DlgConnect(QWidget *parent) connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int))); QGridLayout *grid = new QGridLayout; - grid->addWidget(hostLabel, 0, 0); - grid->addWidget(hostEdit, 0, 1); - grid->addWidget(portLabel, 1, 0); - grid->addWidget(portEdit, 1, 1); - grid->addWidget(playernameLabel, 2, 0); - grid->addWidget(playernameEdit, 2, 1); - grid->addWidget(passwordLabel, 3, 0); - grid->addWidget(passwordEdit, 3, 1); - grid->addWidget(savePasswordCheckBox, 4, 0, 1, 2); - grid->addWidget(autoConnectCheckBox, 5, 0, 1, 2); + grid->addWidget(previousHostButton, 0, 1); + grid->addWidget(previousHosts, 1, 1); + grid->addWidget(newHostButton, 2, 1); + grid->addWidget(hostLabel, 3, 0); + grid->addWidget(hostEdit, 3, 1); + grid->addWidget(portLabel, 4, 0); + grid->addWidget(portEdit, 4, 1); + grid->addWidget(playernameLabel, 5, 0); + grid->addWidget(playernameEdit, 5, 1); + grid->addWidget(passwordLabel, 6, 0); + grid->addWidget(passwordEdit, 6, 1); + grid->addWidget(savePasswordCheckBox, 7, 0, 1, 2); + grid->addWidget(autoConnectCheckBox, 8, 0, 1, 2); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); @@ -71,8 +94,32 @@ DlgConnect::DlgConnect(QWidget *parent) setWindowTitle(tr("Connect to server")); setFixedHeight(sizeHint().height()); setMinimumWidth(300); + + connect(previousHostButton, SIGNAL(toggled(bool)), this, SLOT(previousHostSelected(bool))); + connect(newHostButton, SIGNAL(toggled(bool)), this, SLOT(newHostSelected(bool))); + + if (settings.value("previoushostlogin", 1).toInt()) + previousHostButton->setChecked(true); + else + newHostButton->setChecked(true); } + +void DlgConnect::previousHostSelected(bool state) { + if (state) { + hostEdit->setDisabled(true); + previousHosts->setDisabled(false); + } +} + +void DlgConnect::newHostSelected(bool state) { + if (state) { + hostEdit->setDisabled(false); + previousHosts->setDisabled(true); + } +} + + void DlgConnect::passwordSaved(int state) { Q_UNUSED(state); @@ -88,17 +135,34 @@ void DlgConnect::actOk() { QSettings settings; settings.beginGroup("server"); - settings.setValue("hostname", hostEdit->text()); settings.setValue("port", portEdit->text()); settings.setValue("playername", playernameEdit->text()); settings.setValue("password", savePasswordCheckBox->isChecked() ? passwordEdit->text() : QString()); settings.setValue("save_password", savePasswordCheckBox->isChecked() ? 1 : 0); settings.setValue("auto_connect", autoConnectCheckBox->isChecked() ? 1 : 0); + settings.setValue("previoushostlogin", previousHostButton->isChecked() ? 1 : 0); + + QStringList hostList; + if (newHostButton->isChecked()) + if (!hostEdit->text().trimmed().isEmpty()) + hostList << hostEdit->text(); + + for (int i = 0; i < previousHosts->count(); i++) + if(!previousHosts->itemText(i).trimmed().isEmpty()) + hostList << previousHosts->itemText(i); + + settings.setValue("previoushosts", hostList); + settings.setValue("previoushostindex", previousHosts->currentIndex()); settings.endGroup(); accept(); } + +QString DlgConnect::getHost() const { + return previousHostButton->isChecked() ? previousHosts->currentText() : hostEdit->text(); +} + void DlgConnect::actCancel() { QSettings settings; @@ -109,3 +173,17 @@ void DlgConnect::actCancel() reject(); } + + +bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Delete) { + QComboBox* combobox = reinterpret_cast(obj); + combobox->removeItem(combobox->currentIndex()); + return true; + } + } + return QObject::eventFilter(obj, event); +} diff --git a/cockatrice/src/dlg_connect.h b/cockatrice/src/dlg_connect.h index 6f135aa0..43cb319e 100644 --- a/cockatrice/src/dlg_connect.h +++ b/cockatrice/src/dlg_connect.h @@ -7,12 +7,22 @@ class QLabel; class QPushButton; class QCheckBox; +class QComboBox; +class QRadioButton; + +class DeleteHighlightedItemWhenShiftDelPressedEventFilter : public QObject +{ + Q_OBJECT +protected: + bool eventFilter(QObject *obj, QEvent *event); +}; + class DlgConnect : public QDialog { Q_OBJECT public: DlgConnect(QWidget *parent = 0); - QString getHost() const { return hostEdit->text(); } + QString getHost() const; int getPort() const { return portEdit->text().toInt(); } QString getPlayerName() const { return playernameEdit->text(); } QString getPassword() const { return passwordEdit->text(); } @@ -20,10 +30,14 @@ private slots: void actOk(); void actCancel(); void passwordSaved(int state); + void previousHostSelected(bool state); + void newHostSelected(bool state); private: QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel; QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit; QCheckBox *savePasswordCheckBox, *autoConnectCheckBox; + QComboBox *previousHosts; + QRadioButton *newHostButton, *previousHostButton; }; #endif diff --git a/cockatrice/src/dlg_create_token.cpp b/cockatrice/src/dlg_create_token.cpp index ff852c48..8210d950 100644 --- a/cockatrice/src/dlg_create_token.cpp +++ b/cockatrice/src/dlg_create_token.cpp @@ -10,10 +10,12 @@ #include #include #include + #include "decklist.h" #include "dlg_create_token.h" #include "carddatabasemodel.h" #include "main.h" +#include "settingscache.h" DlgCreateToken::DlgCreateToken(const QStringList &_predefinedTokens, QWidget *parent) : QDialog(parent), predefinedTokens(_predefinedTokens) @@ -136,7 +138,8 @@ void DlgCreateToken::tokenSelectionChanged(const QModelIndex ¤t, const QMo const QString cardColor = cardInfo->getColors().isEmpty() ? QString() : (cardInfo->getColors().size() > 1 ? QString("m") : cardInfo->getColors().first()); colorEdit->setCurrentIndex(colorEdit->findData(cardColor, Qt::UserRole, Qt::MatchFixedString)); ptEdit->setText(cardInfo->getPowTough()); - annotationEdit->setText(cardInfo->getText()); + if(settingsCache->getAnnotateTokens()) + annotationEdit->setText(cardInfo->getText()); } void DlgCreateToken::actChooseTokenFromAll(bool checked) diff --git a/cockatrice/src/dlg_creategame.cpp b/cockatrice/src/dlg_creategame.cpp index 72b26ee9..0f8cea4f 100644 --- a/cockatrice/src/dlg_creategame.cpp +++ b/cockatrice/src/dlg_creategame.cpp @@ -12,6 +12,7 @@ #include #include "dlg_creategame.h" #include "tab_room.h" +#include "settingscache.h" #include "pending_command.h" #include "pb/room_commands.pb.h" @@ -20,6 +21,7 @@ void DlgCreateGame::sharedCtor() { + rememberGameSettings = new QCheckBox(tr("Re&member settings")); descriptionLabel = new QLabel(tr("&Description:")); descriptionEdit = new QLineEdit; descriptionLabel->setBuddy(descriptionEdit); @@ -37,6 +39,7 @@ void DlgCreateGame::sharedCtor() generalGrid->addWidget(descriptionEdit, 0, 1); generalGrid->addWidget(maxPlayersLabel, 1, 0); generalGrid->addWidget(maxPlayersEdit, 1, 1); + generalGrid->addWidget(rememberGameSettings, 2, 0); QVBoxLayout *gameTypeLayout = new QVBoxLayout; QMapIterator gameTypeIterator(gameTypes); @@ -45,6 +48,7 @@ void DlgCreateGame::sharedCtor() QCheckBox *gameTypeCheckBox = new QCheckBox(gameTypeIterator.value()); gameTypeLayout->addWidget(gameTypeCheckBox); gameTypeCheckBoxes.insert(gameTypeIterator.key(), gameTypeCheckBox); + gameTypeCheckBoxes[gameTypeIterator.key()]->setChecked((settingsCache->getGameTypes().contains(gameTypeIterator.value() + ", ") ? true : false)); } QGroupBox *gameTypeGroupBox = new QGroupBox(tr("Game type")); gameTypeGroupBox->setLayout(gameTypeLayout); @@ -109,8 +113,31 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap &_gameType { sharedCtor(); + rememberGameSettings->setChecked(settingsCache->getRememberGameSettings()); + descriptionEdit->setText(settingsCache->getGameDescription()); + maxPlayersEdit->setValue(settingsCache->getMaxPlayers()); + onlyBuddiesCheckBox->setChecked(settingsCache->getOnlyBuddies()); + if (room && room->getUserInfo()->user_level() & ServerInfo_User::IsRegistered) + { + onlyRegisteredCheckBox->setChecked(settingsCache->getOnlyRegistered()); + } else { + onlyBuddiesCheckBox->setEnabled(false); + onlyRegisteredCheckBox->setEnabled(false); + } + spectatorsAllowedCheckBox->setChecked(settingsCache->getSpectatorsAllowed()); + spectatorsNeedPasswordCheckBox->setChecked(settingsCache->getSpectatorsNeedPassword()); + spectatorsCanTalkCheckBox->setChecked(settingsCache->getSpectatorsCanTalk()); + spectatorsSeeEverythingCheckBox->setChecked(settingsCache->getSpectatorsCanSeeEverything()); + + if (!rememberGameSettings->isChecked()){ + actReset(); + } + + clearButton = new QPushButton(tr("&Clear")); buttonBox->addButton(QDialogButtonBox::Cancel); + buttonBox->addButton(clearButton, QDialogButtonBox::ActionRole); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOK())); + connect(clearButton, SIGNAL(clicked()), this, SLOT(actReset())); setWindowTitle(tr("Create game")); } @@ -120,6 +147,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMapsetEnabled(false); descriptionEdit->setEnabled(false); maxPlayersEdit->setEnabled(false); passwordEdit->setEnabled(false); @@ -157,6 +185,30 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMapsetText(""); + maxPlayersEdit->setValue(2); + + passwordEdit->setText(""); + onlyBuddiesCheckBox->setChecked(false); + onlyRegisteredCheckBox->setChecked(true); + + spectatorsAllowedCheckBox->setChecked(true); + spectatorsNeedPasswordCheckBox->setChecked(false); + spectatorsCanTalkCheckBox->setChecked(false); + spectatorsSeeEverythingCheckBox->setChecked(false); + + QMapIterator gameTypeCheckBoxIterator(gameTypeCheckBoxes); + while (gameTypeCheckBoxIterator.hasNext()) { + gameTypeCheckBoxIterator.next(); + gameTypeCheckBoxIterator.value()->setChecked(false); + } + +descriptionEdit->setFocus(); +} + + void DlgCreateGame::actOK() { Command_CreateGame cmd; @@ -170,13 +222,28 @@ void DlgCreateGame::actOK() cmd.set_spectators_can_talk(spectatorsCanTalkCheckBox->isChecked()); cmd.set_spectators_see_everything(spectatorsSeeEverythingCheckBox->isChecked()); + QString gameTypes = QString(); QMapIterator gameTypeCheckBoxIterator(gameTypeCheckBoxes); while (gameTypeCheckBoxIterator.hasNext()) { gameTypeCheckBoxIterator.next(); - if (gameTypeCheckBoxIterator.value()->isChecked()) + if (gameTypeCheckBoxIterator.value()->isChecked()) { cmd.add_game_type_ids(gameTypeCheckBoxIterator.key()); + gameTypes += gameTypeCheckBoxIterator.value()->text() + ", "; + } } + settingsCache->setRememberGameSettings(rememberGameSettings->isChecked()); + if (rememberGameSettings->isChecked()){ + settingsCache->setGameDescription(descriptionEdit->text()); + settingsCache->setMaxPlayers(maxPlayersEdit->value()); + settingsCache->setOnlyBuddies(onlyBuddiesCheckBox->isChecked()); + settingsCache->setOnlyRegistered(onlyRegisteredCheckBox->isChecked()); + settingsCache->setSpectatorsAllowed(spectatorsAllowedCheckBox->isChecked()); + settingsCache->setSpectatorsNeedPassword(spectatorsNeedPasswordCheckBox->isChecked()); + settingsCache->setSpectatorsCanTalk(spectatorsCanTalkCheckBox->isChecked()); + settingsCache->setSpectatorsCanSeeEverything(spectatorsSeeEverythingCheckBox->isChecked()); + settingsCache->setGameTypes(gameTypes); + } PendingCommand *pend = room->prepareRoomCommand(cmd); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(checkResponse(Response))); room->sendRoomCommand(pend); diff --git a/cockatrice/src/dlg_creategame.h b/cockatrice/src/dlg_creategame.h index 032bfc6f..a0c9521c 100644 --- a/cockatrice/src/dlg_creategame.h +++ b/cockatrice/src/dlg_creategame.h @@ -23,6 +23,7 @@ public: DlgCreateGame(const ServerInfo_Game &game, const QMap &_gameTypes, QWidget *parent = 0); private slots: void actOK(); + void actReset(); void checkResponse(const Response &response); void spectatorsAllowedChanged(int state); private: @@ -37,6 +38,8 @@ private: QCheckBox *onlyBuddiesCheckBox, *onlyRegisteredCheckBox; QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox, *spectatorsSeeEverythingCheckBox; QDialogButtonBox *buttonBox; + QPushButton *clearButton; + QCheckBox *rememberGameSettings; void sharedCtor(); }; diff --git a/cockatrice/src/dlg_edit_avatar.cpp b/cockatrice/src/dlg_edit_avatar.cpp new file mode 100644 index 00000000..3c5fe1fa --- /dev/null +++ b/cockatrice/src/dlg_edit_avatar.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dlg_edit_avatar.h" + +DlgEditAvatar::DlgEditAvatar(QWidget *parent) + : QDialog(parent) +{ + imageLabel = new QLabel(tr("No image chosen.")); + imageLabel->setFixedSize(400, 200); + imageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + imageLabel->setStyleSheet("border: 1px solid #000"); + + textLabel = new QLabel(tr("To change your avatar, choose a new image.\nTo remove your current avatar, confirm without choosing a new image.")); + browseButton = new QPushButton(tr("Browse...")); + connect(browseButton, SIGNAL(clicked()), this, SLOT(actBrowse())); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(imageLabel, 0, 0, 1, 2); + grid->addWidget(textLabel, 1, 0); + grid->addWidget(browseButton, 1, 1); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel())); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(grid); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Change avatar")); + setFixedHeight(sizeHint().height()); + setMinimumWidth(300); +} + +void DlgEditAvatar::actOk() +{ + accept(); +} + +void DlgEditAvatar::actCancel() +{ + reject(); +} + +void DlgEditAvatar::actBrowse() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Open Image"), QDir::homePath(), tr("Image Files (*.png *.jpg *.bmp)")); + if(fileName.isEmpty()) + { + imageLabel->setText(tr("No image chosen.")); + return; + } + + QImage image; + QImageReader imgReader; + imgReader.setDecideFormatFromContent(true); + imgReader.setFileName(fileName); + if(!imgReader.read(&image)) + { + qDebug() << "Avatar image loading failed for file:" << fileName; + imageLabel->setText(tr("Invalid image chosen.")); + return; + } + imageLabel->setPixmap(QPixmap::fromImage(image).scaled(400, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation)); +} + +QByteArray DlgEditAvatar::getImage() +{ + const QPixmap *pix = imageLabel->pixmap(); + if(!pix || pix->isNull()) + return QByteArray(); + + QImage image = pix->toImage(); + if(image.isNull()) + return QByteArray(); + + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "JPG"); + return ba; +} diff --git a/cockatrice/src/dlg_edit_avatar.h b/cockatrice/src/dlg_edit_avatar.h new file mode 100644 index 00000000..0c34f0bb --- /dev/null +++ b/cockatrice/src/dlg_edit_avatar.h @@ -0,0 +1,26 @@ +#ifndef DLG_EDITAVATAR_H +#define DLG_EDITAVATAR_H + +#include +#include +#include + +class QLabel; +class QPushButton; +class QCheckBox; + +class DlgEditAvatar : public QDialog { + Q_OBJECT +public: + DlgEditAvatar(QWidget *parent = 0); + QByteArray getImage(); +private slots: + void actOk(); + void actCancel(); + void actBrowse(); +private: + QLabel *textLabel, *imageLabel; + QPushButton *browseButton; +}; + +#endif diff --git a/cockatrice/src/dlg_edit_password.cpp b/cockatrice/src/dlg_edit_password.cpp new file mode 100644 index 00000000..729b8974 --- /dev/null +++ b/cockatrice/src/dlg_edit_password.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + +#include "dlg_edit_password.h" + +DlgEditPassword::DlgEditPassword(QWidget *parent) + : QDialog(parent) +{ + QSettings settings; + settings.beginGroup("server"); + + oldPasswordLabel = new QLabel(tr("Old password:")); + oldPasswordEdit = new QLineEdit(); + + if(settings.value("save_password", 1).toInt()) + oldPasswordEdit->setText(settings.value("password").toString()); + + oldPasswordLabel->setBuddy(oldPasswordEdit); + oldPasswordEdit->setEchoMode(QLineEdit::Password); + + newPasswordLabel = new QLabel(tr("New password:")); + newPasswordEdit = new QLineEdit(); + newPasswordLabel->setBuddy(newPasswordLabel); + newPasswordEdit->setEchoMode(QLineEdit::Password); + + newPasswordLabel2 = new QLabel(tr("Confirm new password:")); + newPasswordEdit2 = new QLineEdit(); + newPasswordLabel2->setBuddy(newPasswordLabel2); + newPasswordEdit2->setEchoMode(QLineEdit::Password); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(oldPasswordLabel, 0, 0); + grid->addWidget(oldPasswordEdit, 0, 1); + grid->addWidget(newPasswordLabel, 1, 0); + grid->addWidget(newPasswordEdit, 1, 1); + grid->addWidget(newPasswordLabel2, 2, 0); + grid->addWidget(newPasswordEdit2, 2, 1); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel())); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(grid); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Change password")); + setFixedHeight(sizeHint().height()); + setMinimumWidth(300); +} + +void DlgEditPassword::actOk() +{ + if(newPasswordEdit->text() != newPasswordEdit2->text()) + { + QMessageBox::warning(this, tr("Error"), tr("The new passwords don't match.")); + return; + } + + QSettings settings; + settings.beginGroup("server"); + // always save the password so it will be picked up by the connect dialog + settings.setValue("password", newPasswordEdit->text()); + settings.endGroup(); + + accept(); +} + +void DlgEditPassword::actCancel() +{ + reject(); +} diff --git a/cockatrice/src/dlg_edit_password.h b/cockatrice/src/dlg_edit_password.h new file mode 100644 index 00000000..ba1bd202 --- /dev/null +++ b/cockatrice/src/dlg_edit_password.h @@ -0,0 +1,26 @@ +#ifndef DLG_EDITPASSWORD_H +#define DLG_EDITPASSWORD_H + +#include +#include +#include + +class QLabel; +class QPushButton; +class QCheckBox; + +class DlgEditPassword : public QDialog { + Q_OBJECT +public: + DlgEditPassword(QWidget *parent = 0); + QString getOldPassword() const { return oldPasswordEdit->text(); } + QString getNewPassword() const { return newPasswordEdit->text(); } +private slots: + void actOk(); + void actCancel(); +private: + QLabel *oldPasswordLabel, *newPasswordLabel, *newPasswordLabel2; + QLineEdit *oldPasswordEdit, *newPasswordEdit, *newPasswordEdit2; +}; + +#endif diff --git a/cockatrice/src/dlg_edit_user.cpp b/cockatrice/src/dlg_edit_user.cpp new file mode 100644 index 00000000..932dfa12 --- /dev/null +++ b/cockatrice/src/dlg_edit_user.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include + +#include "dlg_edit_user.h" +#include "settingscache.h" + +DlgEditUser::DlgEditUser(QWidget *parent, QString email, int gender, QString country, QString realName) + : QDialog(parent) +{ + emailLabel = new QLabel(tr("Email:")); + emailEdit = new QLineEdit(); + emailLabel->setBuddy(emailEdit); + emailEdit->setText(email); + + genderLabel = new QLabel(tr("Pronouns:")); + genderEdit = new QComboBox(); + genderLabel->setBuddy(genderEdit); + genderEdit->insertItem(0, QIcon(":/resources/genders/unknown.svg"), tr("Neutral")); + genderEdit->insertItem(1, QIcon(":/resources/genders/male.svg"), tr("Masculine")); + genderEdit->insertItem(2, QIcon(":/resources/genders/female.svg"), tr("Feminine")); + genderEdit->setCurrentIndex(gender + 1); + + countryLabel = new QLabel(tr("Country:")); + countryEdit = new QComboBox(); + countryLabel->setBuddy(countryEdit); + countryEdit->insertItem(0, tr("Undefined")); + countryEdit->setCurrentIndex(0); + + QStringList countries = settingsCache->getCountries(); + int i = 1; + foreach(QString c, countries) + { + countryEdit->addItem(QPixmap(":/resources/countries/" + c + ".svg"), c); + if (c == country) + countryEdit->setCurrentIndex(i); + + ++i; + } + + realnameLabel = new QLabel(tr("Real name:")); + realnameEdit = new QLineEdit(); + realnameLabel->setBuddy(realnameEdit); + realnameEdit->setText(realName); + + QGridLayout *grid = new QGridLayout; + grid->addWidget(emailLabel, 0, 0); + grid->addWidget(emailEdit, 0, 1); + grid->addWidget(genderLabel, 1, 0); + grid->addWidget(genderEdit, 1, 1); + grid->addWidget(countryLabel, 2, 0); + grid->addWidget(countryEdit, 2, 1); + grid->addWidget(realnameLabel, 3, 0); + grid->addWidget(realnameEdit, 3, 1); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(actCancel())); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(grid); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Edit user profile")); + setFixedHeight(sizeHint().height()); + setMinimumWidth(300); +} + +void DlgEditUser::actOk() +{ + accept(); +} + +void DlgEditUser::actCancel() +{ + reject(); +} diff --git a/cockatrice/src/dlg_edit_user.h b/cockatrice/src/dlg_edit_user.h new file mode 100644 index 00000000..d7d04b1b --- /dev/null +++ b/cockatrice/src/dlg_edit_user.h @@ -0,0 +1,29 @@ +#ifndef DLG_EDITUSER_H +#define DLG_EDITUSER_H + +#include +#include +#include + +class QLabel; +class QPushButton; +class QCheckBox; + +class DlgEditUser : public QDialog { + Q_OBJECT +public: + DlgEditUser(QWidget *parent = 0, QString email = QString(), int gender = -1, QString country = QString(), QString realName = QString()); + QString getEmail() const { return emailEdit->text(); } + int getGender() const { return genderEdit->currentIndex() - 1; } + QString getCountry() const { return countryEdit->currentIndex() == 0 ? "" : countryEdit->currentText(); } + QString getRealName() const { return realnameEdit->text(); } +private slots: + void actOk(); + void actCancel(); +private: + QLabel *emailLabel, *genderLabel, *countryLabel, *realnameLabel; + QLineEdit *emailEdit, *realnameEdit; + QComboBox *genderEdit, *countryEdit; +}; + +#endif diff --git a/cockatrice/src/dlg_load_deck_from_clipboard.cpp b/cockatrice/src/dlg_load_deck_from_clipboard.cpp index 3eb3428f..1c41cc1a 100644 --- a/cockatrice/src/dlg_load_deck_from_clipboard.cpp +++ b/cockatrice/src/dlg_load_deck_from_clipboard.cpp @@ -10,6 +10,7 @@ #include #include "dlg_load_deck_from_clipboard.h" #include "deck_loader.h" +#include "settingscache.h" DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) : QDialog(parent), deckList(0) @@ -17,7 +18,6 @@ DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) contentsEdit = new QPlainTextEdit; refreshButton = new QPushButton(tr("&Refresh")); - refreshButton->setShortcut(QKeySequence("F5")); connect(refreshButton, SIGNAL(clicked()), this, SLOT(actRefresh())); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -35,6 +35,8 @@ DlgLoadDeckFromClipboard::DlgLoadDeckFromClipboard(QWidget *parent) resize(500, 500); actRefresh(); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); } void DlgLoadDeckFromClipboard::actRefresh() @@ -42,13 +44,31 @@ void DlgLoadDeckFromClipboard::actRefresh() contentsEdit->setPlainText(QApplication::clipboard()->text()); } +void DlgLoadDeckFromClipboard::refreshShortcuts() +{ + refreshButton->setShortcut(settingsCache->shortcuts().getSingleShortcut("DlgLoadDeckFromClipboard/refreshButton")); +} + void DlgLoadDeckFromClipboard::actOK() { QString buffer = contentsEdit->toPlainText(); QTextStream stream(&buffer); DeckLoader *l = new DeckLoader; - if (l->loadFromStream_Plain(stream)) { + if (buffer.contains("")) + { + if (l->loadFromString_Native(buffer)) + { + deckList = l; + accept(); + } + else + { + QMessageBox::critical(this, tr("Error"), tr("Invalid deck list.")); + delete l; + } + } + else if (l->loadFromStream_Plain(stream)) { deckList = l; accept(); } else { diff --git a/cockatrice/src/dlg_load_deck_from_clipboard.h b/cockatrice/src/dlg_load_deck_from_clipboard.h index 375c713d..ec133a1b 100644 --- a/cockatrice/src/dlg_load_deck_from_clipboard.h +++ b/cockatrice/src/dlg_load_deck_from_clipboard.h @@ -12,6 +12,7 @@ class DlgLoadDeckFromClipboard : public QDialog { private slots: void actOK(); void actRefresh(); + void refreshShortcuts(); private: DeckLoader *deckList; public: diff --git a/cockatrice/src/dlg_register.cpp b/cockatrice/src/dlg_register.cpp index 52d5f070..5c48e460 100644 --- a/cockatrice/src/dlg_register.cpp +++ b/cockatrice/src/dlg_register.cpp @@ -4,9 +4,11 @@ #include #include #include +#include #include #include "dlg_register.h" +#include "settingscache.h" #include "pb/serverinfo_user.pb.h" DlgRegister::DlgRegister(QWidget *parent) @@ -32,16 +34,25 @@ DlgRegister::DlgRegister(QWidget *parent) passwordLabel->setBuddy(passwordEdit); passwordEdit->setEchoMode(QLineEdit::Password); + passwordConfirmationLabel = new QLabel(tr("Password (again):")); + passwordConfirmationEdit = new QLineEdit(); + passwordConfirmationLabel->setBuddy(passwordConfirmationEdit); + passwordConfirmationEdit->setEchoMode(QLineEdit::Password); + emailLabel = new QLabel(tr("Email:")); emailEdit = new QLineEdit(); emailLabel->setBuddy(emailEdit); - genderLabel = new QLabel(tr("Gender:")); + emailConfirmationLabel = new QLabel(tr("Email (again):")); + emailConfirmationEdit = new QLineEdit(); + emailConfirmationLabel->setBuddy(emailConfirmationEdit); + + genderLabel = new QLabel(tr("Pronouns:")); genderEdit = new QComboBox(); genderLabel->setBuddy(genderEdit); - genderEdit->insertItem(0, QIcon("theme:genders/unknown.svg"), tr("Undefined")); - genderEdit->insertItem(1, QIcon("theme:genders/male.svg"), tr("Male")); - genderEdit->insertItem(2, QIcon("theme:genders/female.svg"), tr("Female")); + genderEdit->insertItem(0, QIcon("theme:genders/unknown.svg"), tr("Neutral")); + genderEdit->insertItem(1, QIcon("theme:genders/male.svg"), tr("Masculine")); + genderEdit->insertItem(2, QIcon("theme:genders/female.svg"), tr("Feminine")); genderEdit->setCurrentIndex(0); countryLabel = new QLabel(tr("Country:")); @@ -298,6 +309,9 @@ DlgRegister::DlgRegister(QWidget *parent) countryEdit->addItem(QIcon("theme:countries/zm.svg"), "zm"); countryEdit->addItem(QIcon("theme:countries/zw.svg"), "zw"); countryEdit->setCurrentIndex(0); + QStringList countries = settingsCache->getCountries(); + foreach(QString c, countries) + countryEdit->addItem(QPixmap(":/resources/countries/" + c + ".svg"), c); realnameLabel = new QLabel(tr("Real name:")); realnameEdit = new QLineEdit(); @@ -312,14 +326,18 @@ DlgRegister::DlgRegister(QWidget *parent) grid->addWidget(playernameEdit, 2, 1); grid->addWidget(passwordLabel, 3, 0); grid->addWidget(passwordEdit, 3, 1); - grid->addWidget(emailLabel, 4, 0); - grid->addWidget(emailEdit, 4, 1); - grid->addWidget(genderLabel, 5, 0); - grid->addWidget(genderEdit, 5, 1); - grid->addWidget(countryLabel, 6, 0); - grid->addWidget(countryEdit, 6, 1); - grid->addWidget(realnameLabel, 7, 0); - grid->addWidget(realnameEdit, 7, 1); + grid->addWidget(passwordConfirmationLabel, 4, 0); + grid->addWidget(passwordConfirmationEdit, 4, 1); + grid->addWidget(emailLabel, 5, 0); + grid->addWidget(emailEdit, 5, 1); + grid->addWidget(emailConfirmationLabel, 6, 0); + grid->addWidget(emailConfirmationEdit, 6, 1); + grid->addWidget(genderLabel, 7, 0); + grid->addWidget(genderEdit, 7, 1); + grid->addWidget(countryLabel, 8, 0); + grid->addWidget(countryEdit, 8, 1); + grid->addWidget(realnameLabel, 9, 0); + grid->addWidget(realnameEdit, 9, 1); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk())); @@ -337,6 +355,17 @@ DlgRegister::DlgRegister(QWidget *parent) void DlgRegister::actOk() { + if (passwordEdit->text() != passwordConfirmationEdit->text()) + { + QMessageBox::critical(this, tr("Registration Warning"), tr("Your passwords do not match, please try again.")); + return; + } + else if (emailConfirmationEdit->text() != emailEdit->text()) + { + QMessageBox::critical(this, tr("Registration Warning"), tr("Your email addresses do not match, please try again.")); + return; + } + QSettings settings; settings.beginGroup("server"); settings.setValue("hostname", hostEdit->text()); diff --git a/cockatrice/src/dlg_register.h b/cockatrice/src/dlg_register.h index 3ee7b105..c86f1217 100644 --- a/cockatrice/src/dlg_register.h +++ b/cockatrice/src/dlg_register.h @@ -19,14 +19,14 @@ public: QString getPassword() const { return passwordEdit->text(); } QString getEmail() const { return emailEdit->text(); } int getGender() const { return genderEdit->currentIndex() - 1; } - QString getCountry() const { return genderEdit->currentIndex() == 0 ? "" : countryEdit->currentText(); } + QString getCountry() const { return countryEdit->currentIndex() == 0 ? "" : countryEdit->currentText(); } QString getRealName() const { return realnameEdit->text(); } private slots: void actOk(); void actCancel(); private: - QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel, *emailLabel, *genderLabel, *countryLabel, *realnameLabel; - QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit, *emailEdit, *realnameEdit; + QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel, *passwordConfirmationLabel, *emailLabel, *emailConfirmationLabel, *genderLabel, *countryLabel, *realnameLabel; + QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit, *passwordConfirmationEdit, *emailEdit, *emailConfirmationEdit, *realnameEdit; QComboBox *genderEdit, *countryEdit; }; diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 9428b6e1..837946dc 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -29,6 +29,9 @@ #include "thememanager.h" #include "priceupdater.h" #include "soundengine.h" +#include "sequenceEdit/shortcutstab.h" + +#define LINKING_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Custom-Download-URLs" GeneralSettingsPage::GeneralSettingsPage() { @@ -52,12 +55,17 @@ GeneralSettingsPage::GeneralSettingsPage() pixmapCacheEdit.setSuffix(" MB"); picDownloadHqCheckBox.setChecked(settingsCache->getPicDownloadHq()); picDownloadCheckBox.setChecked(settingsCache->getPicDownload()); + + highQualityURLEdit = new QLineEdit(settingsCache->getPicUrlHq()); + highQualityURLEdit->setEnabled(settingsCache->getPicDownloadHq()); connect(&clearDownloadedPicsButton, SIGNAL(clicked()), this, SLOT(clearDownloadedPicsButtonClicked())); connect(&languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); connect(&picDownloadCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownload(int))); connect(&picDownloadHqCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPicDownloadHq(int))); connect(&pixmapCacheEdit, SIGNAL(valueChanged(int)), settingsCache, SLOT(setPixmapCacheSize(int))); + connect(&picDownloadHqCheckBox, SIGNAL(clicked(bool)), this, SLOT(setEnabledStatus(bool))); + connect(highQualityURLEdit, SIGNAL(textChanged(QString)), settingsCache, SLOT(setPicUrlHq(QString))); QGridLayout *personalGrid = new QGridLayout; personalGrid->addWidget(&languageLabel, 0, 0); @@ -67,6 +75,12 @@ GeneralSettingsPage::GeneralSettingsPage() personalGrid->addWidget(&picDownloadCheckBox, 2, 0, 1, 2); personalGrid->addWidget(&picDownloadHqCheckBox, 3, 0, 1, 2); personalGrid->addWidget(&clearDownloadedPicsButton, 4, 0, 1, 1); + personalGrid->addWidget(&highQualityURLLabel, 5, 0, 1, 1); + personalGrid->addWidget(highQualityURLEdit, 5, 1, 1, 1); + personalGrid->addWidget(&highQualityURLLinkLabel, 6, 1, 1, 1); + + highQualityURLLinkLabel.setTextInteractionFlags(Qt::LinksAccessibleByMouse); + highQualityURLLinkLabel.setOpenExternalLinks(true); personalGroupBox = new QGroupBox; personalGroupBox->setLayout(personalGrid); @@ -222,7 +236,7 @@ void GeneralSettingsPage::retranslateUi() personalGroupBox->setTitle(tr("Personal settings")); languageLabel.setText(tr("Language:")); picDownloadCheckBox.setText(tr("Download card pictures on the fly")); - picDownloadHqCheckBox.setText(tr("Download high-quality card pictures")); + picDownloadHqCheckBox.setText(tr("Download card pictures from a custom URL")); pathsGroupBox->setTitle(tr("Paths")); deckPathLabel.setText(tr("Decks directory:")); replaysPathLabel.setText(tr("Replays directory:")); @@ -230,9 +244,16 @@ void GeneralSettingsPage::retranslateUi() cardDatabasePathLabel.setText(tr("Card database:")); tokenDatabasePathLabel.setText(tr("Token database:")); pixmapCacheLabel.setText(tr("Picture cache size:")); + highQualityURLLabel.setText(tr("Custom Card Download URL:")); + highQualityURLLinkLabel.setText(QString("%2").arg(LINKING_FAQ_URL).arg(tr("Linking FAQ"))); clearDownloadedPicsButton.setText(tr("Reset/Clear Downloaded Pictures")); } +void GeneralSettingsPage::setEnabledStatus(bool status) +{ + highQualityURLEdit->setEnabled(status); +} + AppearanceSettingsPage::AppearanceSettingsPage() { QString themeName = settingsCache->getThemeName(); @@ -344,12 +365,16 @@ UserInterfaceSettingsPage::UserInterfaceSettingsPage() playToStackCheckBox.setChecked(settingsCache->getPlayToStack()); connect(&playToStackCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPlayToStack(int))); - + + annotateTokensCheckBox.setChecked(settingsCache->getAnnotateTokens()); + connect(&annotateTokensCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setAnnotateTokens(int))); + QGridLayout *generalGrid = new QGridLayout; generalGrid->addWidget(¬ificationsEnabledCheckBox, 0, 0); generalGrid->addWidget(&specNotificationsEnabledCheckBox, 1, 0); generalGrid->addWidget(&doubleClickToPlayCheckBox, 2, 0); generalGrid->addWidget(&playToStackCheckBox, 3, 0); + generalGrid->addWidget(&annotateTokensCheckBox, 4, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -381,6 +406,7 @@ void UserInterfaceSettingsPage::retranslateUi() specNotificationsEnabledCheckBox.setText(tr("Notify in the taskbar for game events while you are spectating")); doubleClickToPlayCheckBox.setText(tr("&Double-click cards to play them (instead of single-click)")); playToStackCheckBox.setText(tr("&Play all nonlands onto the stack (not the battlefield) by default")); + annotateTokensCheckBox.setText(tr("Annotate card text on tokens")); animationGroupBox->setTitle(tr("Animation settings")); tapAnimationCheckBox.setText(tr("&Tap/untap animation")); } @@ -391,7 +417,7 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() //priceTagsCheckBox.setChecked(settingsCache->getPriceTagFeature()); //connect(&priceTagsCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPriceTagFeature(int))); - connect(this, SIGNAL(priceTagSourceChanged(int)), settingsCache, SLOT(setPriceTagSource(int))); + //connect(this, SIGNAL(priceTagSourceChanged(int)), settingsCache, SLOT(setPriceTagSource(int))); QGridLayout *generalGrid = new QGridLayout; //generalGrid->addWidget(&priceTagsCheckBox, 0, 0); @@ -428,6 +454,9 @@ MessagesSettingsPage::MessagesSettingsPage() { chatMentionCheckBox.setChecked(settingsCache->getChatMention()); connect(&chatMentionCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMention(int))); + + chatMentionCompleterCheckbox.setChecked(settingsCache->getChatMentionCompleter()); + connect(&chatMentionCompleterCheckbox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMentionCompleter(int))); ignoreUnregUsersMainChat.setChecked(settingsCache->getIgnoreUnregisteredUsers()); ignoreUnregUserMessages.setChecked(settingsCache->getIgnoreUnregisteredUserMessages()); @@ -437,6 +466,9 @@ MessagesSettingsPage::MessagesSettingsPage() invertMentionForeground.setChecked(settingsCache->getChatMentionForeground()); connect(&invertMentionForeground, SIGNAL(stateChanged(int)), this, SLOT(updateTextColor(int))); + invertHighlightForeground.setChecked(settingsCache->getChatHighlightForeground()); + connect(&invertHighlightForeground, SIGNAL(stateChanged(int)), this, SLOT(updateTextHighlightColor(int))); + mentionColor = new QLineEdit(); mentionColor->setText(settingsCache->getChatMentionColor()); updateMentionPreview(); @@ -448,17 +480,37 @@ MessagesSettingsPage::MessagesSettingsPage() mentionPopups.setChecked(settingsCache->getShowMentionPopup()); connect(&mentionPopups, SIGNAL(stateChanged(int)), settingsCache, SLOT(setShowMentionPopups(int))); + customAlertString = new QLineEdit(); + customAlertString->setPlaceholderText("Word1 Word2 Word3"); + customAlertString->setText(settingsCache->getHighlightWords()); + connect(customAlertString, SIGNAL(textChanged(QString)), settingsCache, SLOT(setHighlightWords(QString))); + QGridLayout *chatGrid = new QGridLayout; chatGrid->addWidget(&chatMentionCheckBox, 0, 0); chatGrid->addWidget(&invertMentionForeground, 0, 1); chatGrid->addWidget(mentionColor, 0, 2); - chatGrid->addWidget(&ignoreUnregUsersMainChat, 1, 0); + chatGrid->addWidget(&chatMentionCompleterCheckbox, 1, 0); + chatGrid->addWidget(&ignoreUnregUsersMainChat, 2, 0); chatGrid->addWidget(&hexLabel, 1, 2); - chatGrid->addWidget(&ignoreUnregUserMessages, 2, 0); - chatGrid->addWidget(&messagePopups, 3, 0); - chatGrid->addWidget(&mentionPopups, 4, 0); + chatGrid->addWidget(&ignoreUnregUserMessages, 3, 0); + chatGrid->addWidget(&messagePopups, 4, 0); + chatGrid->addWidget(&mentionPopups, 5, 0); chatGroupBox = new QGroupBox; chatGroupBox->setLayout(chatGrid); + + highlightColor = new QLineEdit(); + highlightColor->setText(settingsCache->getChatHighlightColor()); + updateHighlightPreview(); + connect(highlightColor, SIGNAL(textChanged(QString)), this, SLOT(updateHighlightColor(QString))); + + QGridLayout *highlightNotice = new QGridLayout; + highlightNotice->addWidget(highlightColor, 0, 2); + highlightNotice->addWidget(&invertHighlightForeground, 0, 1); + highlightNotice->addWidget(&hexHighlightLabel, 1, 2); + highlightNotice->addWidget(customAlertString, 0, 0); + highlightNotice->addWidget(&customAlertStringLabel, 1, 0); + highlightGroupBox = new QGroupBox; + highlightGroupBox->setLayout(highlightNotice); QSettings settings; messageList = new QListWidget; @@ -485,11 +537,12 @@ MessagesSettingsPage::MessagesSettingsPage() messageShortcuts = new QGroupBox; messageShortcuts->setLayout(messageListLayout); - + QVBoxLayout *mainLayout = new QVBoxLayout; - + mainLayout->addWidget(messageShortcuts); mainLayout->addWidget(chatGroupBox); + mainLayout->addWidget(highlightGroupBox); setLayout(mainLayout); @@ -505,16 +558,35 @@ void MessagesSettingsPage::updateColor(const QString &value) { } } +void MessagesSettingsPage::updateHighlightColor(const QString &value) { + QColor colorToSet; + colorToSet.setNamedColor("#" + value); + if (colorToSet.isValid()) { + settingsCache->setChatHighlightColor(value); + updateHighlightPreview(); + } +} + void MessagesSettingsPage::updateTextColor(int value) { settingsCache->setChatMentionForeground(value); updateMentionPreview(); } +void MessagesSettingsPage::updateTextHighlightColor(int value) { + settingsCache->setChatHighlightForeground(value); + updateHighlightPreview(); +} + void MessagesSettingsPage::updateMentionPreview() { mentionColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatMentionColor() + ";color: " + (settingsCache->getChatMentionForeground() ? "white" : "black") + ";}"); } +void MessagesSettingsPage::updateHighlightPreview() { + highlightColor->setStyleSheet("QLineEdit{background:#" + settingsCache->getChatHighlightColor() + + ";color: " + (settingsCache->getChatHighlightForeground() ? "white" : "black") + ";}"); +} + void MessagesSettingsPage::storeSettings() { QSettings settings; @@ -545,15 +617,19 @@ void MessagesSettingsPage::actRemove() void MessagesSettingsPage::retranslateUi() { chatGroupBox->setTitle(tr("Chat settings")); + highlightGroupBox->setTitle(tr("Custom alert words")); chatMentionCheckBox.setText(tr("Enable chat mentions")); + chatMentionCompleterCheckbox.setText(tr("Enable mention completer")); messageShortcuts->setTitle(tr("In-game message macros")); - ignoreUnregUsersMainChat.setText(tr("Ignore unregistered users in main chat")); - ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users.")); - ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users.")); + ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users")); + ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users")); invertMentionForeground.setText(tr("Invert text color")); - messagePopups.setText(tr("Enable desktop notifications for private messages.")); + invertHighlightForeground.setText(tr("Invert text color")); + messagePopups.setText(tr("Enable desktop notifications for private messages")); mentionPopups.setText(tr("Enable desktop notification for mentions.")); hexLabel.setText(tr("(Color is hexadecimal)")); + hexHighlightLabel.setText(tr("(Color is hexadecimal)")); + customAlertStringLabel.setText(tr("Separate words with a space, alphanumeric characters only")); } @@ -668,6 +744,7 @@ DlgSettings::DlgSettings(QWidget *parent) pagesWidget->addWidget(new DeckEditorSettingsPage); pagesWidget->addWidget(new MessagesSettingsPage); pagesWidget->addWidget(new SoundSettingsPage); + pagesWidget->addWidget(new ShortcutsTab); createIcons(); contentsWidget->setCurrentRow(0); @@ -722,6 +799,11 @@ void DlgSettings::createIcons() soundButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); soundButton->setIcon(QIcon("theme:config/sound.svg")); + shortcutsButton = new QListWidgetItem(contentsWidget); + shortcutsButton->setTextAlignment(Qt::AlignHCenter); + shortcutsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + shortcutsButton->setIcon(QIcon(":/resources/icon_config_shorcuts.svg")); + connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem *))); } @@ -831,6 +913,7 @@ void DlgSettings::retranslateUi() deckEditorButton->setText(tr("Deck Editor")); messagesButton->setText(tr("Chat")); soundButton->setText(tr("Sound")); + shortcutsButton->setText(tr("Shortcuts")); for (int i = 0; i < pagesWidget->count(); i++) dynamic_cast(pagesWidget->widget(i))->retranslateUi(); diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index c4222066..2bceda71 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -44,6 +44,7 @@ private slots: void cardDatabasePathButtonClicked(); void tokenDatabasePathButtonClicked(); void languageBoxChanged(int index); + void setEnabledStatus(bool); private: QStringList findQmFiles(); QString languageName(const QString &qmFile); @@ -52,6 +53,7 @@ private: QLineEdit *picsPathEdit; QLineEdit *cardDatabasePathEdit; QLineEdit *tokenDatabasePathEdit; + QLineEdit *highQualityURLEdit; QSpinBox pixmapCacheEdit; QGroupBox *personalGroupBox; QGroupBox *pathsGroupBox; @@ -65,6 +67,8 @@ private: QLabel picsPathLabel; QLabel cardDatabasePathLabel; QLabel tokenDatabasePathLabel; + QLabel highQualityURLLabel; + QLabel highQualityURLLinkLabel; QPushButton clearDownloadedPicsButton; }; @@ -100,6 +104,7 @@ private: QCheckBox specNotificationsEnabledCheckBox; QCheckBox doubleClickToPlayCheckBox; QCheckBox playToStackCheckBox; + QCheckBox annotateTokensCheckBox; QCheckBox tapAnimationCheckBox; QGroupBox *generalGroupBox; QGroupBox *animationGroupBox; @@ -132,24 +137,34 @@ private slots: void actAdd(); void actRemove(); void updateColor(const QString &value); + void updateHighlightColor(const QString &value); void updateTextColor(int value); + void updateTextHighlightColor(int value); private: QListWidget *messageList; QAction *aAdd; QAction *aRemove; QCheckBox chatMentionCheckBox; + QCheckBox chatMentionCompleterCheckbox; QCheckBox invertMentionForeground; + QCheckBox invertHighlightForeground; QCheckBox ignoreUnregUsersMainChat; QCheckBox ignoreUnregUserMessages; QCheckBox messagePopups; QCheckBox mentionPopups; QGroupBox *chatGroupBox; + QGroupBox *highlightGroupBox; QGroupBox *messageShortcuts; QLineEdit *mentionColor; + QLineEdit *highlightColor; + QLineEdit *customAlertString; QLabel hexLabel; + QLabel hexHighlightLabel; + QLabel customAlertStringLabel; void storeSettings(); void updateMentionPreview(); + void updateHighlightPreview(); }; class SoundSettingsPage : public AbstractSettingsPage { @@ -186,6 +201,7 @@ private: QListWidget *contentsWidget; QStackedWidget *pagesWidget; QListWidgetItem *generalButton, *appearanceButton, *userInterfaceButton, *deckEditorButton, *messagesButton, *soundButton; + QListWidgetItem *shortcutsButton; void createIcons(); void retranslateUi(); protected: diff --git a/cockatrice/src/filterbuilder.cpp b/cockatrice/src/filterbuilder.cpp index ccf885d0..a07e40c5 100644 --- a/cockatrice/src/filterbuilder.cpp +++ b/cockatrice/src/filterbuilder.cpp @@ -11,6 +11,7 @@ FilterBuilder::FilterBuilder(QWidget *parent) : QWidget(parent) { filterCombo = new QComboBox; + filterCombo->setObjectName("filterCombo"); for (int i = 0; i < CardFilter::AttrEnd; i++) filterCombo->addItem( tr(CardFilter::attrName(static_cast(i))), @@ -18,6 +19,7 @@ FilterBuilder::FilterBuilder(QWidget *parent) ); typeCombo = new QComboBox; + typeCombo->setObjectName("typeCombo"); for (int i = 0; i < CardFilter::TypeEnd; i++) typeCombo->addItem( tr(CardFilter::typeName(static_cast(i))), @@ -25,12 +27,15 @@ FilterBuilder::FilterBuilder(QWidget *parent) ); QPushButton *ok = new QPushButton(QIcon("theme:icons/increment.svg"), QString()); + ok->setObjectName("ok"); ok->setMaximumSize(20, 20); edit = new QLineEdit; + edit->setObjectName("edit"); edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); QGridLayout *layout = new QGridLayout; + layout->setObjectName("layout"); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(typeCombo, 0, 0, 1, 2); diff --git a/cockatrice/src/gamescene.cpp b/cockatrice/src/gamescene.cpp index 7f2110cd..23057db3 100644 --- a/cockatrice/src/gamescene.cpp +++ b/cockatrice/src/gamescene.cpp @@ -11,9 +11,13 @@ #include #include #include +#include GameScene::GameScene(PhasesToolbar *_phasesToolbar, QObject *parent) - : QGraphicsScene(parent), phasesToolbar(_phasesToolbar), viewSize(QSize()) + : QGraphicsScene(parent), + phasesToolbar(_phasesToolbar), + viewSize(QSize()), + playerRotation(0) { animationTimer = new QBasicTimer; addItem(phasesToolbar); @@ -35,7 +39,7 @@ void GameScene::retranslateUi() void GameScene::addPlayer(Player *player) { - qDebug("GameScene::addPlayer"); + qDebug() << "GameScene::addPlayer name=" << player->getName(); players << player; addItem(player); connect(player, SIGNAL(sizeChanged()), this, SLOT(rearrange())); @@ -44,39 +48,63 @@ void GameScene::addPlayer(Player *player) void GameScene::removePlayer(Player *player) { - qDebug("GameScene::removePlayer"); + qDebug() << "GameScene::removePlayer name=" << player->getName(); players.removeAt(players.indexOf(player)); removeItem(player); rearrange(); } +void GameScene::adjustPlayerRotation(int rotationAdjustment) +{ + playerRotation += rotationAdjustment; + rearrange(); +} + void GameScene::rearrange() { playersByColumn.clear(); + // Create the list of players playing, noting the first player's index. QList playersPlaying; - int firstPlayer = -1; - for (int i = 0; i < players.size(); ++i) - if (!players[i]->getConceded()) { - playersPlaying.append(players[i]); - if ((firstPlayer == -1) && (players[i]->getLocal())) - firstPlayer = playersPlaying.size() - 1; + int firstPlayerIndex = 0; + bool firstPlayerFound = false; + QListIterator playersIter(players); + while (playersIter.hasNext()) { + Player *p = playersIter.next(); + if (!p->getConceded()) { + playersPlaying.append(p); + if (!firstPlayerFound && (p->getLocal())) { + firstPlayerIndex = playersPlaying.size() - 1; + firstPlayerFound = true; + } } - if (firstPlayer == -1) - firstPlayer = 0; + } + + // Rotate the players playing list so that first player is first, then + // adjust by the additional rotation setting. + if (!playersPlaying.isEmpty()) { + int totalRotation = firstPlayerIndex + playerRotation; + while (totalRotation < 0) + totalRotation += playersPlaying.size(); + for (int i = 0; i < totalRotation; ++i) { + playersPlaying.append(playersPlaying.takeFirst()); + } + } + const int playersCount = playersPlaying.size(); const int columns = playersCount < settingsCache->getMinPlayersForMultiColumnLayout() ? 1 : 2; const int rows = ceil((qreal) playersCount / columns); qreal sceneHeight = 0, sceneWidth = -playerAreaSpacing; QList columnWidth; - int firstPlayerOfColumn = firstPlayer; + + QListIterator playersPlayingIter(playersPlaying); for (int col = 0; col < columns; ++col) { playersByColumn.append(QList()); columnWidth.append(0); qreal thisColumnHeight = -playerAreaSpacing; const int rowsInColumn = rows - (playersCount % columns) * col; // only correct for max. 2 cols for (int j = 0; j < rowsInColumn; ++j) { - Player *player = playersPlaying[(firstPlayerOfColumn + j) % playersCount]; + Player *player = playersPlayingIter.next(); if (col == 0) playersByColumn[col].prepend(player); else @@ -88,8 +116,6 @@ void GameScene::rearrange() if (thisColumnHeight > sceneHeight) sceneHeight = thisColumnHeight; sceneWidth += columnWidth[col] + playerAreaSpacing; - - firstPlayerOfColumn += rowsInColumn; } phasesToolbar->setHeight(sceneHeight); @@ -202,14 +228,14 @@ void GameScene::processViewSizeChange(const QSize &newSize) setSceneRect(0, 0, newWidth, sceneRect().height()); qreal extraWidthPerColumn = (newWidth - minWidth) / playersByColumn.size(); - for (int col = 0; col < playersByColumn.size(); ++col) + qreal newx = phasesToolbar->getWidth(); + for (int col = 0; col < playersByColumn.size(); ++col) { for (int row = 0; row < playersByColumn[col].size(); ++row){ playersByColumn[col][row]->processSceneSizeChange(minWidthByColumn[col] + extraWidthPerColumn); - if (col == 0) - playersByColumn[col][row]->setPos(phasesToolbar->getWidth(), playersByColumn[col][row]->y()); - else - playersByColumn[col][row]->setPos(phasesToolbar->getWidth() + (newWidth - phasesToolbar->getWidth()) / 2, playersByColumn[col][row]->y()); + playersByColumn[col][row]->setPos(newx, playersByColumn[col][row]->y()); } + newx += minWidthByColumn[col] + extraWidthPerColumn; + } } void GameScene::updateHover(const QPointF &scenePos) @@ -295,4 +321,4 @@ void GameScene::resizeRubberBand(const QPointF &cursorPoint) void GameScene::stopRubberBand() { emit sigStopRubberBand(); -} \ No newline at end of file +} diff --git a/cockatrice/src/gamescene.h b/cockatrice/src/gamescene.h index b542f0ec..6ac014cd 100644 --- a/cockatrice/src/gamescene.h +++ b/cockatrice/src/gamescene.h @@ -28,6 +28,7 @@ private: QPointer hoveredCard; QBasicTimer *animationTimer; QSet cardsToAnimate; + int playerRotation; void updateHover(const QPointF &scenePos); public: GameScene(PhasesToolbar *_phasesToolbar, QObject *parent = 0); @@ -51,6 +52,7 @@ public slots: void removePlayer(Player *player); void clearViews(); void closeMostRecentZoneView(); + void adjustPlayerRotation(int rotationAdjustment); void rearrange(); protected: bool event(QEvent *event); diff --git a/cockatrice/src/gameselector.cpp b/cockatrice/src/gameselector.cpp index 44ff8013..6c58b842 100644 --- a/cockatrice/src/gameselector.cpp +++ b/cockatrice/src/gameselector.cpp @@ -157,12 +157,11 @@ void GameSelector::checkResponse(const Response &response) void GameSelector::actJoin() { - bool spectator = sender() == spectateButton; - QModelIndex ind = gameListView->currentIndex(); if (!ind.isValid()) return; const ServerInfo_Game &game = gameListModel->getGame(ind.data(Qt::UserRole).toInt()); + bool spectator = sender() == spectateButton || game.player_count() == game.max_players(); bool overrideRestrictions = !tabSupervisor->getAdminLocked(); QString password; if (game.with_password() && !(spectator && !game.spectators_need_password()) && !overrideRestrictions) { diff --git a/cockatrice/src/gameview.cpp b/cockatrice/src/gameview.cpp index 1c4a6984..2c5c6163 100644 --- a/cockatrice/src/gameview.cpp +++ b/cockatrice/src/gameview.cpp @@ -1,5 +1,6 @@ #include "gameview.h" #include "gamescene.h" +#include "settingscache.h" #include #include #include @@ -19,10 +20,11 @@ GameView::GameView(QGraphicsScene *scene, QWidget *parent) connect(scene, SIGNAL(sigStopRubberBand()), this, SLOT(stopRubberBand())); aCloseMostRecentZoneView = new QAction(this); - aCloseMostRecentZoneView->setShortcut(QKeySequence("Esc")); + connect(aCloseMostRecentZoneView, SIGNAL(triggered()), scene, SLOT(closeMostRecentZoneView())); addAction(aCloseMostRecentZoneView); - + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); rubberBand = new QRubberBand(QRubberBand::Rectangle, this); } @@ -59,3 +61,8 @@ void GameView::stopRubberBand() { rubberBand->hide(); } + +void GameView::refreshShortcuts() +{ + aCloseMostRecentZoneView->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aCloseMostRecentZoneView")); +} diff --git a/cockatrice/src/gameview.h b/cockatrice/src/gameview.h index e678f293..c5fdfe30 100644 --- a/cockatrice/src/gameview.h +++ b/cockatrice/src/gameview.h @@ -17,6 +17,7 @@ private slots: void startRubberBand(const QPointF &selectionOrigin); void resizeRubberBand(const QPointF &cursorPoint); void stopRubberBand(); + void refreshShortcuts(); public slots: void updateSceneRect(const QRectF &rect); public: diff --git a/cockatrice/src/lineeditcompleter.cpp b/cockatrice/src/lineeditcompleter.cpp new file mode 100644 index 00000000..6860295d --- /dev/null +++ b/cockatrice/src/lineeditcompleter.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lineeditcompleter.h" + +LineEditCompleter::LineEditCompleter(QWidget *parent) +: QLineEdit(parent) +{ +} + +void LineEditCompleter::focusOutEvent(QFocusEvent * e){ + QLineEdit::focusOutEvent(e); + if (c->popup()->isVisible()){ + //Remove Popup + c->popup()->hide(); + //Truncate the line to last space or whole string + QString textValue = text(); + int lastIndex = textValue.length(); + int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; + int leftShift = qMin(lastIndex, lastWordStartIndex); + setText(textValue.left(leftShift)); + //Insert highlighted line from popup + insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); + //Set focus back to the textbox since tab was pressed + setFocus(); + } +} + +void LineEditCompleter::keyPressEvent(QKeyEvent * event) +{ + switch (event->key()){ + case Qt::Key_Return: + case Qt::Key_Enter: + case Qt::Key_Escape: + if (c->popup()->isVisible()){ + event->ignore(); + //Remove Popup + c->popup()->hide(); + //Truncate the line to last space or whole string + QString textValue = text(); + int lastIndexof = qMax(0, textValue.lastIndexOf(" ")); + QString finalString = textValue.left(lastIndexof); + //Add a space if there's a word + if (finalString != "") + finalString += " "; + setText(finalString); + return; + } + break; + case Qt::Key_Space: + if (c->popup()->isVisible()){ + event->ignore(); + //Remove Popup + c->popup()->hide(); + //Truncate the line to last space or whole string + QString textValue = text(); + int lastIndex = textValue.length(); + int lastWordStartIndex = textValue.lastIndexOf(" ") + 1; + int leftShift = qMin(lastIndex, lastWordStartIndex); + setText(textValue.left(leftShift)); + //Insert highlighted line from popup + insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " "); + return; + } + break; + default: + break; + } + + QLineEdit::keyPressEvent(event); + QString textValue = text(); + //Wait until the first character after @ + if (!c || text().right(1).contains("@")) + return; + + //Set new completion prefix + c->setCompletionPrefix(cursorWord(text())); + if (c->completionPrefix().length() < 1){ + c->popup()->hide(); + return; + } + + //Draw completion box + QRect cr = cursorRect(); + cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width()); + c->complete(cr); + + //Select first item in the completion popup + QItemSelectionModel* sm = new QItemSelectionModel(c->completionModel()); + c->popup()->setSelectionModel(sm); + sm->select(c->completionModel()->index(0, 0), QItemSelectionModel::ClearAndSelect); + sm->setCurrentIndex(c->completionModel()->index(0, 0), QItemSelectionModel::NoUpdate); +} + +QString LineEditCompleter::cursorWord(const QString &line) const +{ + return line.mid(line.left(cursorPosition()).lastIndexOf(" ") + 1, + cursorPosition() - line.left(cursorPosition()).lastIndexOf(" ") - 1); +} + +void LineEditCompleter::insertCompletion(QString arg) +{ + QString s_arg = arg + " "; + setText(text().replace(text().left(cursorPosition()).lastIndexOf(" ") + 1, + cursorPosition() - text().left(cursorPosition()).lastIndexOf(" ") - 1, s_arg)); +} + +void LineEditCompleter::setCompleter(QCompleter* completer) +{ + c = completer; + c->setWidget(this); + connect(c, SIGNAL(activated(QString)), this, SLOT(insertCompletion(QString))); +} + +void LineEditCompleter::setCompletionList(QStringList completionList) +{ + if (!c || c->popup()->isVisible()) + return; + + QStringListModel *model; + model = (QStringListModel*)(c->model()); + if (model == NULL) + model = new QStringListModel(); + model->setStringList(completionList); +} \ No newline at end of file diff --git a/cockatrice/src/lineeditcompleter.h b/cockatrice/src/lineeditcompleter.h new file mode 100644 index 00000000..673a78d2 --- /dev/null +++ b/cockatrice/src/lineeditcompleter.h @@ -0,0 +1,26 @@ +#ifndef LINEEDITCOMPLETER_H +#define LINEEDITCOMPLETER_H + +#include +#include +#include +#include + +class LineEditCompleter : public QLineEdit +{ + Q_OBJECT +private: + QString cursorWord(const QString& line) const; + QCompleter* c; +private slots: + void insertCompletion(QString); +protected: + void keyPressEvent(QKeyEvent * event); + void focusOutEvent(QFocusEvent * e); +public: + explicit LineEditCompleter(QWidget *parent = 0); + void setCompleter(QCompleter*); + void setCompletionList(QStringList); +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/localclient.cpp b/cockatrice/src/localclient.cpp index 3ad7c809..8dd15cd6 100644 --- a/cockatrice/src/localclient.cpp +++ b/cockatrice/src/localclient.cpp @@ -3,13 +3,14 @@ #include "pb/session_commands.pb.h" -LocalClient::LocalClient(LocalServerInterface *_lsi, const QString &_playerName, QObject *parent) +LocalClient::LocalClient(LocalServerInterface *_lsi, const QString &_playerName, const QString &_clientId, QObject *parent) : AbstractClient(parent), lsi(_lsi) { connect(lsi, SIGNAL(itemToClient(const ServerMessage &)), this, SLOT(itemFromServer(const ServerMessage &))); Command_Login loginCmd; loginCmd.set_user_name(_playerName.toStdString()); + loginCmd.set_clientid(_clientId.toStdString()); sendCommand(prepareSessionCommand(loginCmd)); Command_JoinRoom joinCmd; diff --git a/cockatrice/src/localclient.h b/cockatrice/src/localclient.h index febacada..c0769a60 100644 --- a/cockatrice/src/localclient.h +++ b/cockatrice/src/localclient.h @@ -10,7 +10,7 @@ class LocalClient : public AbstractClient { private: LocalServerInterface *lsi; public: - LocalClient(LocalServerInterface *_lsi, const QString &_playerName, QObject *parent = 0); + LocalClient(LocalServerInterface *_lsi, const QString &_playerName, const QString &_clientId, QObject *parent = 0); ~LocalClient(); void sendCommandContainer(const CommandContainer &cont); diff --git a/cockatrice/src/localserver.cpp b/cockatrice/src/localserver.cpp index a7eda3d1..88c93404 100644 --- a/cockatrice/src/localserver.cpp +++ b/cockatrice/src/localserver.cpp @@ -6,7 +6,7 @@ LocalServer::LocalServer(QObject *parent) : Server(false, parent) { setDatabaseInterface(new LocalServer_DatabaseInterface(this)); - addRoom(new Server_Room(0, QString(), QString(), false, QString(), QStringList(), this)); + addRoom(new Server_Room(0, QString(), QString(), QString(), false, QString(), QStringList(), this)); } LocalServer::~LocalServer() @@ -33,7 +33,7 @@ ServerInfo_User LocalServer_DatabaseInterface::getUserData(const QString &name, return result; } -AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, QString & /* reasonStr */, int & /* secondsLeft */) +AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, const QString & /* clientId */, QString & /* reasonStr */, int & /* secondsLeft */) { return UnknownUser; } diff --git a/cockatrice/src/localserver.h b/cockatrice/src/localserver.h index f47187f2..221318c7 100644 --- a/cockatrice/src/localserver.h +++ b/cockatrice/src/localserver.h @@ -24,9 +24,10 @@ protected: ServerInfo_User getUserData(const QString &name, bool withId = false); public: LocalServer_DatabaseInterface(LocalServer *_localServer); - AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft); + AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft); int getNextGameId() { return localServer->getNextLocalGameId(); } int getNextReplayId() { return -1; } + int getActiveUserCount() { return 0; } }; #endif diff --git a/cockatrice/src/main.cpp b/cockatrice/src/main.cpp index 2dadfb62..d691659a 100644 --- a/cockatrice/src/main.cpp +++ b/cockatrice/src/main.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** * Copyright (C) 2008 by Max-Wilhelm Bruker * * brukie@gmx.net * * * @@ -32,6 +32,8 @@ #include #include #include +#include "QtNetwork/QNetworkInterface" +#include #include "main.h" #include "window_main.h" @@ -98,6 +100,19 @@ bool settingsValid() !settingsCache->getPicsPath().isEmpty(); } +QString const generateClientID() +{ + QString macList; + foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces()) + { + if (interface.hardwareAddress() != "") + if (interface.hardwareAddress() != "00:00:00:00:00:00:00:E0") + macList += interface.hardwareAddress() + "."; + } + QString strClientID = QCryptographicHash::hash(macList.toUtf8(), QCryptographicHash::Sha1).toHex().right(15); + return strClientID; +} + int main(int argc, char *argv[]) { QApplication app(argc, argv); @@ -206,6 +221,9 @@ int main(int argc, char *argv[]) QIcon icon("theme:cockatrice.svg"); ui.setWindowIcon(icon); + + settingsCache->setClientID(generateClientID()); + qDebug() << "ClientID In Cache: " << settingsCache->getClientID(); ui.show(); qDebug("main(): ui.show() finished"); diff --git a/cockatrice/src/main.h b/cockatrice/src/main.h index dc10de84..71feb9a7 100644 --- a/cockatrice/src/main.h +++ b/cockatrice/src/main.h @@ -7,7 +7,6 @@ class QSystemTrayIcon; class SoundEngine; extern CardDatabase *db; - extern QSystemTrayIcon *trayIcon; extern QTranslator *translator; extern const QString translationPrefix; @@ -15,6 +14,8 @@ extern QString translationPath; void installNewTranslator(); +QString const generateClientID(); + bool settingsValid(); #endif diff --git a/cockatrice/src/messagelogwidget.cpp b/cockatrice/src/messagelogwidget.cpp index 6b89c7e6..7b76d8b9 100644 --- a/cockatrice/src/messagelogwidget.cpp +++ b/cockatrice/src/messagelogwidget.cpp @@ -76,7 +76,7 @@ void MessageLogWidget::logGameClosed() void MessageLogWidget::logKicked() { - appendHtmlServerMessage(tr("You have been kicked out of the game.")); + appendHtmlServerMessage(tr("You have been kicked out of the game."), true); } void MessageLogWidget::logJoinSpectator(QString name) @@ -224,20 +224,7 @@ QPair MessageLogWidget::getFromStr(CardZone *zone, QString car else if (startName == "hand") fromStr = isFemale(zone->getPlayer()) ? tr(" from her hand") : tr(" from his hand"); else if (startName == "deck") { - if (position >= zone->getCards().size() - 1) { - if (cardName.isEmpty()) { - if (ownerChange) - cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getName()); - else - cardName = isFemale(zone->getPlayer()) ? tr("the bottom card of her library") : tr("the bottom card of his library"); - cardNameContainsStartZone = true; - } else { - if (ownerChange) - fromStr = tr(" from the bottom of %1's library").arg(zone->getPlayer()->getName()); - else - fromStr = isFemale(zone->getPlayer()) ? tr(" from the bottom of her library") : tr(" from the bottom of his library"); - } - } else if (position == 0) { + if (position == 0) { if (cardName.isEmpty()) { if (ownerChange) cardName = tr("the top card of %1's library").arg(zone->getPlayer()->getName()); @@ -250,6 +237,19 @@ QPair MessageLogWidget::getFromStr(CardZone *zone, QString car else fromStr = isFemale(zone->getPlayer()) ? tr(" from the top of her library") : tr(" from the top of his library"); } + } else if (position >= zone->getCards().size() - 1) { + if (cardName.isEmpty()) { + if (ownerChange) + cardName = tr("the bottom card of %1's library").arg(zone->getPlayer()->getName()); + else + cardName = isFemale(zone->getPlayer()) ? tr("the bottom card of her library") : tr("the bottom card of his library"); + cardNameContainsStartZone = true; + } else { + if (ownerChange) + fromStr = tr(" from the bottom of %1's library").arg(zone->getPlayer()->getName()); + else + fromStr = isFemale(zone->getPlayer()) ? tr(" from the bottom of her library") : tr(" from the bottom of his library"); + } } else { if (ownerChange) fromStr = tr(" from %1's library").arg(zone->getPlayer()->getName()); diff --git a/cockatrice/src/pixmapgenerator.cpp b/cockatrice/src/pixmapgenerator.cpp index 1df69988..dd324037 100644 --- a/cockatrice/src/pixmapgenerator.cpp +++ b/cockatrice/src/pixmapgenerator.cpp @@ -119,7 +119,7 @@ QPixmap CountryPixmapGenerator::generatePixmap(int height, const QString &countr if (pmCache.contains(key)) return pmCache.value(key); - QSvgRenderer svg(QString("theme:countries/" + countryCode + ".svg")); + QSvgRenderer svg(QString("theme:countries/" + countryCode.toLower() + ".svg")); int width = (int) round(height * (double) svg.defaultSize().width() / (double) svg.defaultSize().height()); QPixmap pixmap(width, height); pixmap.fill(Qt::transparent); diff --git a/cockatrice/src/player.cpp b/cockatrice/src/player.cpp index 667fbab9..e1d68aa2 100644 --- a/cockatrice/src/player.cpp +++ b/cockatrice/src/player.cpp @@ -96,6 +96,7 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, TabGame *_pare shortcutsActive(false), defaultNumberTopCards(1), lastTokenDestroy(true), + lastTokenTableRow(0), id(_id), active(false), local(_local), @@ -408,11 +409,14 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, TabGame *_pare aMoveToBottomLibrary = new QAction(this); aMoveToBottomLibrary->setData(cmMoveToBottomLibrary); aMoveToGraveyard = new QAction(this); + aMoveToHand = new QAction(this); + aMoveToHand->setData(cmMoveToHand); aMoveToGraveyard->setData(cmMoveToGraveyard); aMoveToExile = new QAction(this); aMoveToExile->setData(cmMoveToExile); connect(aMoveToTopLibrary, SIGNAL(triggered()), this, SLOT(cardMenuAction())); connect(aMoveToBottomLibrary, SIGNAL(triggered()), this, SLOT(cardMenuAction())); + connect(aMoveToHand, SIGNAL(triggered()), this, SLOT(cardMenuAction())); connect(aMoveToGraveyard, SIGNAL(triggered()), this, SLOT(cardMenuAction())); connect(aMoveToExile, SIGNAL(triggered()), this, SLOT(cardMenuAction())); @@ -444,6 +448,8 @@ Player::Player(const ServerInfo_User &info, int _id, bool _local, TabGame *_pare rearrangeZones(); retranslateUi(); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); } Player::~Player() @@ -606,6 +612,7 @@ void Player::retranslateUi() { aViewGraveyard->setText(tr("&View graveyard")); aViewRfg->setText(tr("&View exile")); + playerMenu->setTitle(tr("Player \"%1\"").arg(QString::fromStdString(userInfo->name()))); graveMenu->setTitle(tr("&Graveyard")); rfgMenu->setTitle(tr("&Exile")); @@ -670,53 +677,49 @@ void Player::retranslateUi() for (int i = 0; i < allPlayersActions.size(); ++i) allPlayersActions[i]->setText(tr("&All players")); } - + aPlay->setText(tr("&Play")); aHide->setText(tr("&Hide")); aPlayFacedown->setText(tr("Play &Face Down")); - aTap->setText(tr("&Tap")); aUntap->setText(tr("&Untap")); aDoesntUntap->setText(tr("Toggle &normal untapping")); aFlip->setText(tr("&Flip")); aPeek->setText(tr("&Peek at card face")); aClone->setText(tr("&Clone")); - aClone->setShortcut(QKeySequence("Ctrl+J")); aAttach->setText(tr("Attac&h to card...")); - aAttach->setShortcut(QKeySequence("Ctrl+A")); aUnattach->setText(tr("Unattac&h")); aDrawArrow->setText(tr("&Draw arrow...")); aIncP->setText(tr("&Increase power")); - aIncP->setShortcut(QKeySequence("Ctrl++")); aDecP->setText(tr("&Decrease power")); - aDecP->setShortcut(QKeySequence("Ctrl+-")); aIncT->setText(tr("I&ncrease toughness")); - aIncT->setShortcut(QKeySequence("Alt++")); aDecT->setText(tr("D&ecrease toughness")); - aDecT->setShortcut(QKeySequence("Alt+-")); aIncPT->setText(tr("In&crease power and toughness")); - aIncPT->setShortcut(QKeySequence("Ctrl+Alt++")); aDecPT->setText(tr("Dec&rease power and toughness")); - aDecPT->setShortcut(QKeySequence("Ctrl+Alt+-")); aSetPT->setText(tr("Set &power and toughness...")); - aSetPT->setShortcut(QKeySequence("Ctrl+P")); aSetAnnotation->setText(tr("&Set annotation...")); + QStringList counterColors; counterColors.append(tr("Red")); counterColors.append(tr("Yellow")); counterColors.append(tr("Green")); - for (int i = 0; i < aAddCounter.size(); ++i) + + for (int i = 0; i < aAddCounter.size(); ++i){ aAddCounter[i]->setText(tr("&Add counter (%1)").arg(counterColors[i])); - for (int i = 0; i < aRemoveCounter.size(); ++i) + } + for (int i = 0; i < aRemoveCounter.size(); ++i){ aRemoveCounter[i]->setText(tr("&Remove counter (%1)").arg(counterColors[i])); - for (int i = 0; i < aSetCounter.size(); ++i) + } + for (int i = 0; i < aSetCounter.size(); ++i){ aSetCounter[i]->setText(tr("&Set counters (%1)...").arg(counterColors[i])); + } + aMoveToTopLibrary->setText(tr("&Top of library")); aMoveToBottomLibrary->setText(tr("&Bottom of library")); + aMoveToHand->setText(tr("&Hand")); aMoveToGraveyard->setText(tr("&Graveyard")); - aMoveToGraveyard->setShortcut(QKeySequence("Ctrl+Del")); aMoveToExile->setText(tr("&Exile")); - + QMapIterator zoneIterator(zones); while (zoneIterator.hasNext()) zoneIterator.next().value()->retranslateUi(); @@ -726,24 +729,73 @@ void Player::setShortcutsActive() { shortcutsActive = true; - aViewSideboard->setShortcut(QKeySequence("Ctrl+F3")); - aViewLibrary->setShortcut(QKeySequence("F3")); - aViewTopCards->setShortcut(QKeySequence("Ctrl+W")); - aViewGraveyard->setShortcut(QKeySequence("F4")); - aDrawCard->setShortcut(QKeySequence("Ctrl+D")); - aDrawCards->setShortcut(QKeySequence("Ctrl+E")); - aUndoDraw->setShortcut(QKeySequence("Ctrl+Shift+D")); - aMulligan->setShortcut(QKeySequence("Ctrl+M")); - aShuffle->setShortcut(QKeySequence("Ctrl+S")); - aUntapAll->setShortcut(QKeySequence("Ctrl+U")); - aRollDie->setShortcut(QKeySequence("Ctrl+I")); - aCreateToken->setShortcut(QKeySequence("Ctrl+T")); - aCreateAnotherToken->setShortcut(QKeySequence("Ctrl+G")); - aAlwaysRevealTopCard->setShortcut(QKeySequence("Ctrl+N")); + aPlay->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aPlay")); + aTap->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aTap")); + aUntap->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aUntap")); + aDoesntUntap->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDoesntUntap")); + aFlip->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aFlip")); + aPeek->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aPeek")); + aClone->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aClone")); + aAttach->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aAttach")); + aUnattach->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aUnattach")); + aDrawArrow->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDrawArrow")); + aIncP->setShortcuts(settingsCache->shortcuts().getShortcut("Player/IncP")); + aDecP->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDecP")); + aIncT->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aIncT")); + aDecT->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDecT")); + aIncPT->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aIncPT")); + aDecPT->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aDecPT")); + aSetPT->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSetPT")); + aSetAnnotation->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aSetAnnotation")); + aMoveToTopLibrary->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aMoveToTopLibrary")); + aMoveToBottomLibrary->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aMoveToBottomLibrary")); + aMoveToHand->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aMoveToHand")); + aMoveToGraveyard->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aMoveToGraveyard")); + aMoveToExile->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aMoveToExile")); + + QList addCCShortCuts; + addCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aCCRed")); + addCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aCCYellow")); + addCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aCCGreen")); + + QList removeCCShortCuts; + removeCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aRCRed")); + removeCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aRCYellow")); + removeCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aRCGreen")); + + QList setCCShortCuts; + setCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aSCRed")); + setCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aSCYellow")); + setCCShortCuts.append(settingsCache->shortcuts().getSingleShortcut("Player/aSCGreen")); + + for (int i = 0; i < aAddCounter.size(); ++i){ + aAddCounter[i]->setShortcut(addCCShortCuts.at(i)); + } + for (int i = 0; i < aRemoveCounter.size(); ++i){ + aRemoveCounter[i]->setShortcut(removeCCShortCuts.at(i)); + } + for (int i = 0; i < aSetCounter.size(); ++i){ + aSetCounter[i]->setShortcut(setCCShortCuts.at(i)); + } QMapIterator counterIterator(counters); while (counterIterator.hasNext()) counterIterator.next().value()->setShortcutsActive(); + + aViewSideboard->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aViewSideboard")); + aViewLibrary->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aViewLibrary")); + aViewTopCards->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aViewTopCards")); + aViewGraveyard->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aViewGraveyard")); + aDrawCard->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aDrawCard")); + aDrawCards->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aDrawCards")); + aUndoDraw->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aUndoDraw")); + aMulligan->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aMulligan")); + aShuffle->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aShuffle")); + aUntapAll->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aUntapAll")); + aRollDie->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aRollDie")); + aCreateToken->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aCreateToken")); + aCreateAnotherToken->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aCreateAnotherToken")); + aAlwaysRevealTopCard->setShortcut(settingsCache->shortcuts().getSingleShortcut("Player/aAlwaysRevealTopCard")); } void Player::setShortcutsInactive() @@ -777,10 +829,12 @@ void Player::initSayMenu() QSettings settings; settings.beginGroup("messages"); int count = settings.value("count", 0).toInt(); + for (int i = 0; i < count; i++) { QAction *newAction = new QAction(settings.value(QString("msg%1").arg(i)).toString(), this); - if (i <= 10) - newAction->setShortcut(QKeySequence(QString("Ctrl+%1").arg((i + 1) % 10))); + if (i <= 10){ + newAction->setShortcut(QKeySequence("Ctrl+" + QString::number((i + 1) % 10))); + } connect(newAction, SIGNAL(triggered()), this, SLOT(actSayMessage())); sayMenu->addAction(newAction); } @@ -794,13 +848,16 @@ void Player::setDeck(const DeckLoader &_deck) createPredefinedTokenMenu->clear(); predefinedTokens.clear(); InnerDecklistNode *tokenZone = dynamic_cast(deck->getRoot()->findChild("tokens")); + if (tokenZone) for (int i = 0; i < tokenZone->size(); ++i) { const QString tokenName = tokenZone->at(i)->getName(); predefinedTokens.append(tokenName); QAction *a = createPredefinedTokenMenu->addAction(tokenName); if (i < 10) + { a->setShortcut(QKeySequence("Alt+" + QString::number((i + 1) % 10))); + } connect(a, SIGNAL(triggered()), this, SLOT(actCreatePredefinedToken())); } } @@ -1029,6 +1086,7 @@ void Player::actCreateToken() lastTokenPT = dlg.getPT(); if (CardInfo *correctedCard = db->getCardBySimpleName(lastTokenName, false)) { lastTokenName = correctedCard->getName(); + lastTokenTableRow = table->clampValidTableRow(2 - correctedCard->getTableRow()); if (lastTokenPT.isEmpty()) lastTokenPT = correctedCard->getPowTough(); } @@ -1050,7 +1108,7 @@ void Player::actCreateAnotherToken() cmd.set_annotation(lastTokenAnnotation.toStdString()); cmd.set_destroy_on_zone_change(lastTokenDestroy); cmd.set_x(-1); - cmd.set_y(0); + cmd.set_y(lastTokenTableRow); sendGameCommand(cmd); } @@ -1063,13 +1121,39 @@ void Player::actCreatePredefinedToken() lastTokenName = cardInfo->getName(); lastTokenColor = cardInfo->getColors().isEmpty() ? QString() : cardInfo->getColors().first().toLower(); lastTokenPT = cardInfo->getPowTough(); - lastTokenAnnotation = cardInfo->getText(); + lastTokenAnnotation = settingsCache->getAnnotateTokens() ? cardInfo->getText() : ""; + lastTokenTableRow = table->clampValidTableRow(2 - cardInfo->getTableRow()); lastTokenDestroy = true; aCreateAnotherToken->setEnabled(true); actCreateAnotherToken(); } +void Player::actCreateRelatedCard() +{ + // get the clicked card + CardItem * sourceCard = game->getActiveCard(); + if(!sourceCard) + return; + + // get the target card name + QAction *action = static_cast(sender()); + CardInfo *cardInfo = db->getCard(action->text()); + + // create the token for the related card + Command_CreateToken cmd; + cmd.set_zone("table"); + cmd.set_card_name(cardInfo->getName().toStdString()); + cmd.set_color(cardInfo->getColors().isEmpty() ? QString().toStdString() : cardInfo->getColors().first().toLower().toStdString()); + cmd.set_pt(cardInfo->getPowTough().toStdString()); + cmd.set_annotation(settingsCache->getAnnotateTokens() ? cardInfo->getText().toStdString() : QString().toStdString()); + cmd.set_destroy_on_zone_change(true); + cmd.set_target_zone(sourceCard->getZone()->getName().toStdString()); + cmd.set_target_card_id(sourceCard->getId()); + + sendGameCommand(cmd); +} + void Player::actSayMessage() { QAction *a = qobject_cast(sender()); @@ -1627,7 +1711,7 @@ void Player::playCard(CardItem *c, bool faceDown, bool tapped) cmd.set_y(0); } else { int tableRow = faceDown ? 2 : ci->getTableRow(); - QPoint gridPoint = QPoint(-1, 2 - tableRow); + QPoint gridPoint = QPoint(-1, table->clampValidTableRow(2 - tableRow)); cardToMove->set_face_down(faceDown); cardToMove->set_pt(ci->getPowTough().toStdString()); cardToMove->set_tapped(faceDown ? false : tapped); @@ -1926,6 +2010,18 @@ void Player::cardMenuAction() commandList.append(cmd); break; } + case cmMoveToHand: { + Command_MoveCard *cmd = new Command_MoveCard; + cmd->set_start_player_id(startPlayerId); + cmd->set_start_zone(startZone.toStdString()); + cmd->mutable_cards_to_move()->CopyFrom(idList); + cmd->set_target_player_id(getId()); + cmd->set_target_zone("hand"); + cmd->set_x(0); + cmd->set_y(0); + commandList.append(cmd); + break; + } case cmMoveToGraveyard: { Command_MoveCard *cmd = new Command_MoveCard; cmd->set_start_player_id(startPlayerId); @@ -2188,6 +2284,12 @@ void Player::actPlayFacedown() playCard(game->getActiveCard(), true, false); } +void Player::refreshShortcuts() +{ + if(shortcutsActive) + setShortcutsActive(); +} + void Player::updateCardMenu(CardItem *card) { QMenu *cardMenu = card->getCardMenu(); @@ -2215,6 +2317,8 @@ void Player::updateCardMenu(CardItem *card) moveMenu->addAction(aMoveToTopLibrary); moveMenu->addAction(aMoveToBottomLibrary); moveMenu->addSeparator(); + moveMenu->addAction(aMoveToHand); + moveMenu->addSeparator(); moveMenu->addAction(aMoveToGraveyard); moveMenu->addSeparator(); moveMenu->addAction(aMoveToExile); @@ -2241,6 +2345,17 @@ void Player::updateCardMenu(CardItem *card) cardMenu->addAction(aFlip); if (card->getFaceDown()) cardMenu->addAction(aPeek); + + QStringList relatedCards = card->getInfo()->getRelatedCards(); + if(relatedCards.size()) + { + QMenu * createRelatedCardMenu = cardMenu->addMenu(tr("Cr&eate related card")); + + for (int i = 0; i < relatedCards.size(); ++i) { + QAction *a = createRelatedCardMenu->addAction(relatedCards.at(i)); + connect(a, SIGNAL(triggered()), this, SLOT(actCreateRelatedCard())); + } + } cardMenu->addSeparator(); cardMenu->addAction(aAttach); if (card->getAttachedTo()) @@ -2326,6 +2441,7 @@ void Player::setMirrored(bool _mirrored) void Player::processSceneSizeChange(int newPlayerWidth) { + // Extend table (and hand, if horizontal) to accomodate the new player width. qreal tableWidth = newPlayerWidth - CARD_HEIGHT - 15 - counterAreaWidth - stack->boundingRect().width(); if (!settingsCache->getHorizontalHand()) tableWidth -= hand->boundingRect().width(); diff --git a/cockatrice/src/player.h b/cockatrice/src/player.h index 0925d039..db13d7f8 100644 --- a/cockatrice/src/player.h +++ b/cockatrice/src/player.h @@ -140,6 +140,7 @@ private slots: void actOpenDeckInDeckEditor(); void actCreatePredefinedToken(); + void actCreateRelatedCard(); void cardMenuAction(); void actCardCounterTrigger(); void actAttach(); @@ -157,6 +158,7 @@ private slots: void actPlay(); void actHide(); void actPlayFacedown(); + void refreshShortcuts(); private: TabGame *game; @@ -177,12 +179,13 @@ private: QAction *aPlay, *aPlayFacedown, *aHide, *aTap, *aUntap, *aDoesntUntap, *aAttach, *aUnattach, *aDrawArrow, *aSetPT, *aIncP, *aDecP, *aIncT, *aDecT, *aIncPT, *aDecPT, *aSetAnnotation, *aFlip, *aPeek, *aClone, - *aMoveToTopLibrary, *aMoveToBottomLibrary, *aMoveToGraveyard, *aMoveToExile; + *aMoveToTopLibrary, *aMoveToBottomLibrary, *aMoveToHand, *aMoveToGraveyard, *aMoveToExile; bool shortcutsActive; int defaultNumberTopCards; QString lastTokenName, lastTokenColor, lastTokenPT, lastTokenAnnotation; bool lastTokenDestroy; + int lastTokenTableRow; ServerInfo_User *userInfo; int id; bool active; @@ -238,7 +241,7 @@ private: void eventChangeZoneProperties(const Event_ChangeZoneProperties &event); public: static const int counterAreaWidth = 55; - enum CardMenuActionType { cmTap, cmUntap, cmDoesntUntap, cmFlip, cmPeek, cmClone, cmMoveToTopLibrary, cmMoveToBottomLibrary, cmMoveToGraveyard, cmMoveToExile }; + enum CardMenuActionType { cmTap, cmUntap, cmDoesntUntap, cmFlip, cmPeek, cmClone, cmMoveToTopLibrary, cmMoveToBottomLibrary, cmMoveToHand, cmMoveToGraveyard, cmMoveToExile }; enum { Type = typeOther }; int type() const { return Type; } diff --git a/cockatrice/src/playerlistwidget.cpp b/cockatrice/src/playerlistwidget.cpp index fb729e5c..1d780ce3 100644 --- a/cockatrice/src/playerlistwidget.cpp +++ b/cockatrice/src/playerlistwidget.cpp @@ -6,7 +6,6 @@ #include "tab_supervisor.h" #include "tab_userlists.h" #include "userlist.h" -#include "userinfobox.h" #include "user_context_menu.h" #include #include @@ -67,7 +66,7 @@ PlayerListWidget::PlayerListWidget(TabSupervisor *_tabSupervisor, AbstractClient } else userContextMenu = 0; - setMinimumHeight(60); + setMinimumHeight(40); setIconSize(QSize(20, 15)); setColumnCount(6); setColumnWidth(0, 20); diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp index 3a597589..e149082b 100644 --- a/cockatrice/src/remoteclient.cpp +++ b/cockatrice/src/remoteclient.cpp @@ -1,7 +1,7 @@ #include #include #include "remoteclient.h" - +#include "settingscache.h" #include "pending_command.h" #include "pb/commands.pb.h" #include "pb/session_commands.pb.h" @@ -10,14 +10,19 @@ #include "pb/response_activate.pb.h" #include "pb/server_message.pb.h" #include "pb/event_server_identification.pb.h" +#include "settingscache.h" +#include "main.h" +#include "version_string.h" static const unsigned int protocolVersion = 14; RemoteClient::RemoteClient(QObject *parent) : AbstractClient(parent), timeRunning(0), lastDataReceived(0), messageInProgress(false), handshakeStarted(false), messageLength(0) { + + int keepalive = settingsCache->getKeepAlive(); timer = new QTimer(this); - timer->setInterval(1000); + timer->setInterval(keepalive * 1000); connect(timer, SIGNAL(timeout()), this, SLOT(ping())); socket = new QTcpSocket(this); @@ -75,6 +80,7 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica cmdRegister.set_gender((ServerInfo_User_Gender) gender); cmdRegister.set_country(country.toStdString()); cmdRegister.set_real_name(realName.toStdString()); + cmdRegister.set_clientid(settingsCache->getClientID().toStdString()); PendingCommand *pend = prepareSessionCommand(cmdRegister); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(registerResponse(Response))); @@ -102,11 +108,12 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica void RemoteClient::doLogin() { setStatus(StatusLoggingIn); - + Command_Login cmdLogin; cmdLogin.set_user_name(userName.toStdString()); cmdLogin.set_password(password.toStdString()); - + cmdLogin.set_clientid(settingsCache->getClientID().toStdString()); + cmdLogin.set_clientver(VERSION_STRING); PendingCommand *pend = prepareSessionCommand(cmdLogin); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(loginResponse(Response))); sendCommand(pend); @@ -123,12 +130,12 @@ void RemoteClient::loginResponse(const Response &response) if (response.response_code() == Response::RespOk) { setStatus(StatusLoggedIn); emit userInfoChanged(resp.user_info()); - + QList buddyList; for (int i = resp.buddy_list_size() - 1; i >= 0; --i) buddyList.append(resp.buddy_list(i)); emit buddyListReceived(buddyList); - + QList ignoreList; for (int i = resp.ignore_list_size() - 1; i >= 0; --i) ignoreList.append(resp.ignore_list(i)); @@ -177,7 +184,7 @@ void RemoteClient::readData() QByteArray data = socket->readAll(); inputBuffer.append(data); - + do { if (!messageInProgress) { if (inputBuffer.size() >= 4) { @@ -202,7 +209,7 @@ void RemoteClient::readData() } if (inputBuffer.size() < messageLength) return; - + ServerMessage newServerMessage; newServerMessage.ParseFromArray(inputBuffer.data(), messageLength); #ifdef QT_DEBUG @@ -210,9 +217,9 @@ void RemoteClient::readData() #endif inputBuffer.remove(0, messageLength); messageInProgress = false; - + processProtocolItem(newServerMessage); - + if (getStatus() == StatusDisconnecting) // use thread-safe getter doDisconnectFromServer(); } while (!inputBuffer.isEmpty()); @@ -231,16 +238,17 @@ void RemoteClient::sendCommandContainer(const CommandContainer &cont) buf.data()[2] = (unsigned char) (size >> 8); buf.data()[1] = (unsigned char) (size >> 16); buf.data()[0] = (unsigned char) (size >> 24); - + socket->write(buf); } void RemoteClient::doConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password) { doDisconnectFromServer(); - + userName = _userName; password = _password; + QString clientid = settingsCache->getClientID(); lastHostname = hostname; lastPort = port; @@ -251,7 +259,7 @@ void RemoteClient::doConnectToServer(const QString &hostname, unsigned int port, void RemoteClient::doRegisterToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password, const QString &_email, const int _gender, const QString &_country, const QString &_realname) { doDisconnectFromServer(); - + userName = _userName; password = _password; email = _email; @@ -268,7 +276,7 @@ void RemoteClient::doRegisterToServer(const QString &hostname, unsigned int port void RemoteClient::doActivateToServer(const QString &_token) { doDisconnectFromServer(); - + token = _token; socket->connectToHost(lastHostname, lastPort); @@ -278,7 +286,7 @@ void RemoteClient::doActivateToServer(const QString &_token) void RemoteClient::doDisconnectFromServer() { timer->stop(); - + messageInProgress = false; handshakeStarted = false; messageLength = 0; @@ -289,7 +297,7 @@ void RemoteClient::doDisconnectFromServer() response.set_response_code(Response::RespNotConnected); response.set_cmd_id(pc[i]->getCommandContainer().cmd_id()); pc[i]->processResponse(response); - + delete pc[i]; } pendingCommands.clear(); @@ -309,9 +317,10 @@ void RemoteClient::ping() } } + int keepalive = settingsCache->getKeepAlive(); int maxTime = timeRunning - lastDataReceived; emit maxPingTime(maxTime, maxTimeout); - if (maxTime >= maxTimeout) { + if (maxTime >= (keepalive * maxTimeout)) { disconnectFromServer(); emit serverTimeout(); } else { diff --git a/cockatrice/src/sequenceEdit/sequenceedit.cpp b/cockatrice/src/sequenceEdit/sequenceedit.cpp new file mode 100644 index 00000000..1056b576 --- /dev/null +++ b/cockatrice/src/sequenceEdit/sequenceedit.cpp @@ -0,0 +1,155 @@ +#include "sequenceedit.h" +#include "../settingscache.h" +#include +#include +#include +#include +#include +#include + +SequenceEdit::SequenceEdit(QString _shorcutName, QWidget *parent) : QWidget(parent) +{ + shorcutName = _shorcutName; + currentKey = 0; + maxKeys = 4; + keys = 0; + valid = false; + + lineEdit = new QLineEdit(this); + clearButton = new QPushButton("", this); + defaultButton = new QPushButton("", this); + + lineEdit->setMinimumWidth(70); + clearButton->setMaximumWidth(lineEdit->height()); + defaultButton->setMaximumWidth(lineEdit->height()); + clearButton->setMaximumHeight(lineEdit->height()); + defaultButton->setMaximumHeight(lineEdit->height()); + + clearButton->setIcon(QIcon(":/resources/icon_clearsearch.svg")); + defaultButton->setIcon(QIcon(":/resources/icon_update.png")); + + clearButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + defaultButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setContentsMargins(0,0,0,0); + layout->setSpacing(1); + layout->addWidget(lineEdit); + layout->addWidget(clearButton); + layout->addWidget(defaultButton); + + connect(clearButton,SIGNAL(clicked()),this,SLOT(removeLastShortcut())); + connect(defaultButton,SIGNAL(clicked()),this,SLOT(restoreDefault())); + lineEdit->installEventFilter(this); + + lineEdit->setText(settingsCache->shortcuts().getShortcutString(shorcutName)); +} + +QString SequenceEdit::getSecuence() +{ + return lineEdit->text(); +} + +void SequenceEdit::removeLastShortcut() +{ + QString secuences = lineEdit->text(); + if(!secuences.isEmpty()) + { + if(secuences.lastIndexOf(";") > 0){ + QString valid = secuences.left(secuences.lastIndexOf(";")); + lineEdit->setText(valid); + } + else + lineEdit->clear(); + updateSettings(); + } +} + +void SequenceEdit::restoreDefault() +{ + lineEdit->setText(settingsCache->shortcuts().getDefaultShortcutString(shorcutName)); + updateSettings(); +} + +bool SequenceEdit::eventFilter(QObject *, QEvent * event) +{ + if(event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) + { + QKeyEvent * keyEvent = (QKeyEvent *) event; + + if(event->type() == QEvent::KeyPress && !keyEvent->isAutoRepeat()) + processKey(keyEvent); + else if (event->type() == QEvent::KeyRelease && !keyEvent->isAutoRepeat()) + finishShortcut(); + return true; + } + return false; +} + +void SequenceEdit::processKey(QKeyEvent* e) +{ + int key = e->key(); + if(key != Qt::Key_Control && key != Qt::Key_Shift + && key != Qt::Key_Meta && key != Qt::Key_Alt) + { + valid = true; + key |= translateModifiers(e->modifiers(), e->text()); + } + keys = key; + currentKey++; + if(currentKey >= key) + finishShortcut(); +} + +int SequenceEdit::translateModifiers(Qt::KeyboardModifiers state, const QString &text) +{ + int result = 0; + // The shift modifier only counts when it is not used to type a symbol + // that is only reachable using the shift key anyway + if ((state & Qt::ShiftModifier) && (text.isEmpty() || + !text.at(0).isPrint() || + text.at(0).isLetterOrNumber() || + text.at(0).isSpace())) + result |= Qt::SHIFT; + + if (state & Qt::ControlModifier) + result |= Qt::CTRL; + if (state & Qt::MetaModifier) + result |= Qt::META; + if (state & Qt::AltModifier) + result |= Qt::ALT; + return result; +} + +void SequenceEdit::finishShortcut() +{ + QKeySequence secuence(keys); + if(!secuence.isEmpty() && valid) + { + QString secuenceString = secuence.toString(); + if(settingsCache->shortcuts().isValid(shorcutName,secuenceString)) + { + if(!lineEdit->text().isEmpty()) + { + if(lineEdit->text().contains(secuenceString)) + return; + lineEdit->setText(lineEdit->text() + ";"); + } + lineEdit->setText(lineEdit->text() + secuenceString); + } + else + { + QToolTip::showText(lineEdit->mapToGlobal(QPoint()), tr("Shortcut already in use")); + } + } + currentKey = 0; + keys = 0; + valid = false; + updateSettings(); +} + +void SequenceEdit::updateSettings() +{ + settingsCache->shortcuts().setShortcuts(shorcutName,lineEdit->text()); +} + diff --git a/cockatrice/src/sequenceEdit/sequenceedit.h b/cockatrice/src/sequenceEdit/sequenceedit.h new file mode 100644 index 00000000..0a016c2c --- /dev/null +++ b/cockatrice/src/sequenceEdit/sequenceedit.h @@ -0,0 +1,40 @@ +#ifndef SECUENCEEDIT_H +#define SECUENCEEDIT_H + +#include +#include + +class QLineEdit; +class QPushButton; +class QEvent; + +class SequenceEdit : public QWidget +{ + Q_OBJECT +public: + SequenceEdit(QString _shorcutName, QWidget *parent = 0); + QString getSecuence(); +signals: + +private slots: + void removeLastShortcut(); + void restoreDefault(); +protected: + bool eventFilter(QObject *, QEvent *event); +private: + QString shorcutName; + QLineEdit *lineEdit; + QPushButton *clearButton; + QPushButton *defaultButton; + int keys; + int currentKey; + int maxKeys; + bool valid; + + void processKey(QKeyEvent *e); + int translateModifiers(Qt::KeyboardModifiers state, const QString &text); + void finishShortcut(); + void updateSettings(); +}; + +#endif // SECUENCEEDIT_H diff --git a/cockatrice/src/sequenceEdit/shortcutstab.cpp b/cockatrice/src/sequenceEdit/shortcutstab.cpp new file mode 100644 index 00000000..b28a1466 --- /dev/null +++ b/cockatrice/src/sequenceEdit/shortcutstab.cpp @@ -0,0 +1,18 @@ +#include "shortcutstab.h" +#include "ui_shortcutstab.h" + +ShortcutsTab::ShortcutsTab() : + ui(new Ui::shortcutsTab) +{ + ui->setupUi(this); +} + +void ShortcutsTab::retranslateUi() +{ + ui->retranslateUi(this); +} + +ShortcutsTab::~ShortcutsTab() +{ + delete ui; +} diff --git a/cockatrice/src/sequenceEdit/shortcutstab.h b/cockatrice/src/sequenceEdit/shortcutstab.h new file mode 100644 index 00000000..c2e88d39 --- /dev/null +++ b/cockatrice/src/sequenceEdit/shortcutstab.h @@ -0,0 +1,25 @@ +#ifndef SHORTCUTSTAB_H +#define SHORTCUTSTAB_H + +#include + +#include "../dlg_settings.h" + +namespace Ui { +class shortcutsTab; +} + +class ShortcutsTab : public AbstractSettingsPage +{ + Q_OBJECT + +public: + ShortcutsTab(); + void retranslateUi(); + ~ShortcutsTab(); + +private: + Ui::shortcutsTab *ui; +}; + +#endif // SHORTCUTSTAB_H diff --git a/cockatrice/src/sequenceEdit/ui_shortcutstab.h b/cockatrice/src/sequenceEdit/ui_shortcutstab.h new file mode 100644 index 00000000..e4f7f884 --- /dev/null +++ b/cockatrice/src/sequenceEdit/ui_shortcutstab.h @@ -0,0 +1,1570 @@ +#ifndef UI_SHORTCUTSTAB_H +#define UI_SHORTCUTSTAB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sequenceedit.h" + +QT_BEGIN_NAMESPACE + +class Ui_shortcutsTab +{ +public: + QGridLayout *gridLayout_9; + QTabWidget *tabWidget; + QWidget *tab; + QGridLayout *gridLayout_3; + QGroupBox *groupBox; + QGridLayout *gridLayout_2; + QLabel *lbl_MainWindow_aDeckEditor; + SequenceEdit *MainWindow_aDeckEditor; + QLabel *lbl_MainWindow_aSinglePlayer; + SequenceEdit *MainWindow_aSinglePlayer; + QLabel *lbl_MainWindow_aWatchReplay; + QLabel *lbl_MainWindow_aConnect; + SequenceEdit *MainWindow_aConnect; + QLabel *lbl_MainWindow_aRegister; + QLabel *lbl_MainWindow_aFullScreen; + SequenceEdit *MainWindow_aFullScreen; + QLabel *lbl_MainWindow_aSettings; + SequenceEdit *MainWindow_aRegister; + QLabel *lbl_MainWindow_aCheckCardUpdates; + SequenceEdit *MainWindow_aSettings; + SequenceEdit *MainWindow_aCheckCardUpdates; + SequenceEdit *MainWindow_aWatchReplay; + SequenceEdit *MainWindow_aDisconnect; + QLabel *lbl_MainWindow_aDisconnect; + QLabel *lbl_MainWindow_aExit; + SequenceEdit *MainWindow_aExit; + QGroupBox *groupBox_2; + QGridLayout *gridLayout; + QLabel *lbl_TabDeckEditor_aAnalyzeDeck; + SequenceEdit *TabDeckEditor_aAnalyzeDeck; + QLabel *lbl_TabDeckEditor_aLoadDeckFromClipboard; + SequenceEdit *TabDeckEditor_aLoadDeckFromClipboard; + QLabel *lbl_TabDeckEditor_aClearFilterAll; + SequenceEdit *TabDeckEditor_aClearFilterAll; + QLabel *lbl_TabDeckEditor_aNewDeck; + SequenceEdit *TabDeckEditor_aNewDeck; + QLabel *lbl_TabDeckEditor_aClearFilterOne; + SequenceEdit *TabDeckEditor_aClearFilterOne; + QLabel *lbl_TabDeckEditor_aOpenCustomFolder; + SequenceEdit *TabDeckEditor_aOpenCustomFolder; + QLabel *lbl_TabDeckEditor_aClose; + SequenceEdit *TabDeckEditor_aClose; + QLabel *lbl_TabDeckEditor_aPrintDeck; + SequenceEdit *TabDeckEditor_aPrintDeck; + QLabel *lbl_TabDeckEditor_aEditSets; + SequenceEdit *TabDeckEditor_aEditSets; + QLabel *lbl_TabDeckEditor_aRemoveCard; + SequenceEdit *TabDeckEditor_aRemoveCard; + QLabel *lbl_TabDeckEditor_aEditTokens; + SequenceEdit *TabDeckEditor_aEditTokens; + QLabel *lbl_TabDeckEditor_aResetLayout; + SequenceEdit *TabDeckEditor_aResetLayout; + QLabel *lbl_TabDeckEditor_aIncrement; + SequenceEdit *TabDeckEditor_aIncrement; + QLabel *lbl_TabDeckEditor_aSaveDeck; + SequenceEdit *TabDeckEditor_aSaveDeck; + QLabel *lbl_TabDeckEditor_aDecrement; + SequenceEdit *TabDeckEditor_aDecrement; + QLabel *lbl_TabDeckEditor_aSaveDeckAs; + SequenceEdit *TabDeckEditor_aSaveDeckAs; + QLabel *lbl_TabDeckEditor_aLoadDeck; + SequenceEdit *TabDeckEditor_aLoadDeck; + QLabel *lbl_TabDeckEditor_aSaveDeckToClipboard; + SequenceEdit *TabDeckEditor_aSaveDeckToClipboard; + QGroupBox *groupBox_3; + QHBoxLayout *horizontalLayout; + QGroupBox *groupBox_4; + QGridLayout *gridLayout_4; + QLabel *lbl_abstractCounter_sSet; + SequenceEdit *abstractCounter_aSet; + QLabel *lbl_abstractCounter_aInc; + SequenceEdit *abstractCounter_Inc; + QLabel *lbl_abstractCounter_aDec; + SequenceEdit *abstractCounter_aDec; + QGroupBox *groupBox_5; + QGridLayout *gridLayout_6; + QLabel *lbl_Player_aSCRed; + SequenceEdit *Player_aSCRed; + QLabel *lbl_Player_aCCRed; + SequenceEdit *Player_aCCRed; + QLabel *lbl_Player_aRCRed; + SequenceEdit *Player_aRCRed; + QGroupBox *groupBox_6; + QGridLayout *gridLayout_7; + QLabel *lbl_Player_aSCGreen; + SequenceEdit *Player_aSCGreen; + QLabel *lbl_Player_aCCGreen; + SequenceEdit *Player_aCCGreen; + QLabel *lbl_Player_aRCGreen; + SequenceEdit *Player_aRCGreen; + QGroupBox *groupBox_7; + QGridLayout *gridLayout_8; + QLabel *lbl_Player_aSCYellow; + SequenceEdit *Player_aSCYellow; + QLabel *lbl_Player_aCCYellow; + SequenceEdit *Player_aCCYellow; + QLabel *lbl_Player_aRCYellow; + SequenceEdit *Player_aRCYellow; + QSpacerItem *verticalSpacer; + QWidget *tab_2; + QGridLayout *gridLayout_17; + QGroupBox *groupBox_9; + QVBoxLayout *verticalLayout; + QGroupBox *groupBox_12; + QGridLayout *gridLayout_12; + SequenceEdit *Player_aDecPT; + SequenceEdit *Player_aIncPT; + QLabel *lbl_Player_aIncPT; + QLabel *lbl_Player_aDecPT; + SequenceEdit *Player_aSetPT; + QLabel *lbl_Player_aSetPT; + QGroupBox *groupBox_11; + QGridLayout *gridLayout_11; + QLabel *lbl_Player_aDecT; + SequenceEdit *Player_aDecT; + QLabel *lbl_Player_aIncT; + SequenceEdit *Player_aIncT; + QGroupBox *groupBox_10; + QGridLayout *gridLayout_10; + QLabel *lbl_Player_aDecP; + SequenceEdit *Player_aDecP; + SequenceEdit *Player_IncP; + QLabel *lbl_Player_IncP; + QGroupBox *groupBox_8; + QGridLayout *gridLayout_5; + QLabel *lbl_TabGame_phase0; + SequenceEdit *TabGame_phase0; + QLabel *lbl_TabGame_phase1; + SequenceEdit *TabGame_phase1; + QLabel *lbl_TabGame_phase2; + SequenceEdit *TabGame_phase2; + QLabel *lbl_TabGame_phase3; + SequenceEdit *TabGame_phase3; + QLabel *lbl_TabGame_phase4; + SequenceEdit *TabGame_phase4; + QLabel *lbl_TabGame_phase5; + SequenceEdit *TabGame_phase5; + QLabel *lbl_TabGame_phase6; + QLabel *lbl_TabGame_phase7; + SequenceEdit *TabGame_phase6; + QLabel *lbl_TabGame_phase8; + SequenceEdit *TabGame_phase7; + QLabel *lbl_TabGame_phase9; + SequenceEdit *TabGame_phase8; + QLabel *lbl_TabGame_phase10; + SequenceEdit *TabGame_phase9; + QLabel *lbl_TabGame_aNextPhase; + SequenceEdit *TabGame_phase10; + QLabel *lbl_TabGame_aNextTurn; + SequenceEdit *TabGame_aNextPhase; + SequenceEdit *TabGame_aNextTurn; + QGroupBox *groupBox_13; + QGridLayout *gridLayout_13; + QLabel *lbl_Player_aTap; + SequenceEdit *Player_aTap; + QLabel *lbl_Player_aUntap; + SequenceEdit *Player_aUntap; + QLabel *lbl_Player_aUntapAll; + SequenceEdit *Player_aUntapAll; + QLabel *lbl_Player_aDoesntUntap; + SequenceEdit *Player_aDoesntUntap; + QLabel *lbl_Player_aFlip; + SequenceEdit *Player_aFlip; + QLabel *lbl_Player_aPeek; + SequenceEdit *Player_aPeek; + QLabel *lbl_Player_aPlay; + SequenceEdit *Player_aPlay; + QLabel *lbl_Player_aAttach; + SequenceEdit *Player_aAttach; + QLabel *lbl_Player_aUnattach; + SequenceEdit *Player_aUnattach; + QLabel *lbl_Player_aClone; + SequenceEdit *Player_aClone; + QLabel *lbl_Player_aCreateToken; + SequenceEdit *Player_aCreateToken; + QLabel *lbl_Player_aCreateAnotherToken; + SequenceEdit *Player_aCreateAnotherToken; + QLabel *lbl_Player_aSetAnnotation; + SequenceEdit *Player_aSetAnnotation; + QSpacerItem *verticalSpacer_2; + QWidget *tab_3; + QGridLayout *gridLayout_20; + QGroupBox *groupBox_15; + QGridLayout *gridLayout_15; + QLabel *lbl_Player_aMoveToBottomLibrary; + SequenceEdit *Player_aMoveToBottomLibrary; + QLabel *lbl_Player_aMoveToTopLibrary; + SequenceEdit *Player_aMoveToTopLibrary; + QLabel *lbl_Player_aMoveToGraveyard; + SequenceEdit *Player_aMoveToGraveyard; + QLabel *lbl_Player_aMoveToExile; + SequenceEdit *Player_aMoveToExile; + QLabel *lbl_Player_aMoveToHand; + SequenceEdit *Player_aMoveToHand; + QGroupBox *groupBox_16; + QGridLayout *gridLayout_16; + QLabel *lbl_Player_aViewGraveyard; + SequenceEdit *Player_aViewGraveyard; + QLabel *lbl_Player_aViewLibrary; + SequenceEdit *Player_aViewLibrary; + QLabel *lbl_Player_aViewTopCards; + SequenceEdit *Player_aViewTopCards; + QLabel *lbl_Player_aViewSideboard; + SequenceEdit *Player_aViewSideboard; + QLabel *lbl_Player_aViewRfg; + SequenceEdit *Player_aViewRfg; + QLabel *lbl_GameView_aCloseMostRecentZoneView; + SequenceEdit *GameView_aCloseMostRecentZoneView; + QGroupBox *groupBox_17; + QGridLayout *gridLayout_18; + SequenceEdit *DeckViewContainer_loadRemoteButton; + SequenceEdit *DeckViewContainer_loadLocalButton; + QLabel *lbl_DeckViewContainer_loadRemoteButton; + QLabel *lbl_DeckViewContainer_loadLocalButton; + QGroupBox *groupBox_18; + QGridLayout *gridLayout_19; + QLabel *lbl_Player_aDrawArrow; + SequenceEdit *Player_aDrawArrow; + QLabel *lbl_TabGame_aLeaveGame; + SequenceEdit *TabGame_aLeaveGame; + QLabel *lbl_TabGame_aRemoveLocalArrows; + SequenceEdit *TabGame_aRemoveLocalArrows; + QLabel *lbl_TabGame_aConcede; + SequenceEdit *TabGame_aConcede; + QLabel *lbl_Player_aRollDie; + SequenceEdit *Player_aRollDie; + QLabel *lbl_TabGame_aRotateViewCW; + SequenceEdit *TabGame_aRotateViewCW; + QLabel *lbl_Player_aShuffle; + SequenceEdit *Player_aShuffle; + QLabel *lbl_TabGame_aRotateViewCCW; + SequenceEdit *TabGame_aRotateViewCCW; + QGroupBox *groupBox_14; + QGridLayout *gridLayout_14; + QLabel *lbl_Player_aMulligan; + SequenceEdit *Player_aMulligan; + QLabel *lbl_Player_aDrawCard; + SequenceEdit *Player_aDrawCard; + QLabel *lbl_Player_aDrawCards; + SequenceEdit *Player_aDrawCards; + QLabel *lbl_Player_aUndoDraw; + SequenceEdit *Player_aUndoDraw; + QLabel *lbl_Player_aAlwaysRevealTopCard; + SequenceEdit *Player_aAlwaysRevealTopCard; + QSpacerItem *verticalSpacer_3; + QWidget * tab_4; + + void setupUi(QWidget *shortcutsTab) + { + if (shortcutsTab->objectName().isEmpty()) + shortcutsTab->setObjectName("shortcutsTab"); + shortcutsTab->resize(819, 477); + gridLayout_9 = new QGridLayout(shortcutsTab); + gridLayout_9->setObjectName("gridLayout_9"); + tabWidget = new QTabWidget(shortcutsTab); + tabWidget->setObjectName("tabWidget"); + tab = new QWidget(); + tab->setObjectName("tab"); + gridLayout_3 = new QGridLayout(tab); + gridLayout_3->setObjectName("gridLayout_3"); + groupBox = new QGroupBox(tab); + groupBox->setObjectName("groupBox"); + gridLayout_2 = new QGridLayout(groupBox); + gridLayout_2->setObjectName("gridLayout_2"); + lbl_MainWindow_aDeckEditor = new QLabel(groupBox); + lbl_MainWindow_aDeckEditor->setObjectName("lbl_MainWindow_aDeckEditor"); + + gridLayout_2->addWidget(lbl_MainWindow_aDeckEditor, 1, 0, 1, 1); + + MainWindow_aDeckEditor = new SequenceEdit("MainWindow/aDeckEditor",groupBox); + MainWindow_aDeckEditor->setObjectName("MainWindow_aDeckEditor"); + + gridLayout_2->addWidget(MainWindow_aDeckEditor, 1, 1, 1, 2); + + lbl_MainWindow_aSinglePlayer = new QLabel(groupBox); + lbl_MainWindow_aSinglePlayer->setObjectName("lbl_MainWindow_aSinglePlayer"); + + gridLayout_2->addWidget(lbl_MainWindow_aSinglePlayer, 2, 0, 1, 1); + + MainWindow_aSinglePlayer = new SequenceEdit("MainWindow/aSinglePlayer",groupBox); + MainWindow_aSinglePlayer->setObjectName("MainWindow_aSinglePlayer"); + + gridLayout_2->addWidget(MainWindow_aSinglePlayer, 2, 1, 1, 2); + + lbl_MainWindow_aWatchReplay = new QLabel(groupBox); + lbl_MainWindow_aWatchReplay->setObjectName("lbl_MainWindow_aWatchReplay"); + + gridLayout_2->addWidget(lbl_MainWindow_aWatchReplay, 4, 0, 1, 1); + + lbl_MainWindow_aConnect = new QLabel(groupBox); + lbl_MainWindow_aConnect->setObjectName("lbl_MainWindow_aConnect"); + + gridLayout_2->addWidget(lbl_MainWindow_aConnect, 6, 0, 1, 1); + + MainWindow_aConnect = new SequenceEdit("MainWindow/aConnect",groupBox); + MainWindow_aConnect->setObjectName("MainWindow_aConnect"); + + gridLayout_2->addWidget(MainWindow_aConnect, 6, 1, 1, 2); + + lbl_MainWindow_aRegister = new QLabel(groupBox); + lbl_MainWindow_aRegister->setObjectName("lbl_MainWindow_aRegister"); + + gridLayout_2->addWidget(lbl_MainWindow_aRegister, 10, 0, 1, 1); + + lbl_MainWindow_aFullScreen = new QLabel(groupBox); + lbl_MainWindow_aFullScreen->setObjectName("lbl_MainWindow_aFullScreen"); + + gridLayout_2->addWidget(lbl_MainWindow_aFullScreen, 9, 0, 1, 1); + + MainWindow_aFullScreen = new SequenceEdit("MainWindow/aFullScreen",groupBox); + MainWindow_aFullScreen->setObjectName("MainWindow_aFullScreen"); + + gridLayout_2->addWidget(MainWindow_aFullScreen, 9, 1, 1, 2); + + lbl_MainWindow_aSettings = new QLabel(groupBox); + lbl_MainWindow_aSettings->setObjectName("lbl_MainWindow_aSettings"); + + gridLayout_2->addWidget(lbl_MainWindow_aSettings, 11, 0, 1, 1); + + MainWindow_aRegister = new SequenceEdit("MainWindow/aRegister",groupBox); + MainWindow_aRegister->setObjectName("MainWindow_aRegister"); + + gridLayout_2->addWidget(MainWindow_aRegister, 10, 1, 1, 2); + + lbl_MainWindow_aCheckCardUpdates = new QLabel(groupBox); + lbl_MainWindow_aCheckCardUpdates->setObjectName("lbl_MainWindow_aCheckCardUpdates"); + + gridLayout_2->addWidget(lbl_MainWindow_aCheckCardUpdates, 0, 0, 1, 1); + + MainWindow_aSettings = new SequenceEdit("MainWindow/aSettings",groupBox); + MainWindow_aSettings->setObjectName("MainWindow_aSettings"); + + gridLayout_2->addWidget(MainWindow_aSettings, 11, 1, 1, 2); + + MainWindow_aCheckCardUpdates = new SequenceEdit("MainWindow/aCheckCardUpdates",groupBox); + MainWindow_aCheckCardUpdates->setObjectName("MainWindow_aCheckCardUpdates"); + + gridLayout_2->addWidget(MainWindow_aCheckCardUpdates, 0, 1, 1, 2); + + MainWindow_aWatchReplay = new SequenceEdit("MainWindow/aWatchReplay",groupBox); + MainWindow_aWatchReplay->setObjectName("MainWindow_aWatchReplay"); + + gridLayout_2->addWidget(MainWindow_aWatchReplay, 4, 1, 1, 2); + + MainWindow_aDisconnect = new SequenceEdit("MainWindow/aDisconnect",groupBox); + MainWindow_aDisconnect->setObjectName("MainWindow_aDisconnect"); + + gridLayout_2->addWidget(MainWindow_aDisconnect, 7, 1, 1, 2); + + lbl_MainWindow_aDisconnect = new QLabel(groupBox); + lbl_MainWindow_aDisconnect->setObjectName("lbl_MainWindow_aDisconnect"); + + gridLayout_2->addWidget(lbl_MainWindow_aDisconnect, 7, 0, 1, 1); + + lbl_MainWindow_aExit = new QLabel(groupBox); + lbl_MainWindow_aExit->setObjectName("lbl_MainWindow_aExit"); + + gridLayout_2->addWidget(lbl_MainWindow_aExit, 8, 0, 1, 1); + + MainWindow_aExit = new SequenceEdit("MainWindow/aExit",groupBox); + MainWindow_aExit->setObjectName("MainWindow_aExit"); + + gridLayout_2->addWidget(MainWindow_aExit, 8, 1, 1, 2); + + gridLayout_3->addWidget(groupBox, 0, 0, 1, 1); + + groupBox_2 = new QGroupBox(tab); + groupBox_2->setObjectName("groupBox_2"); + gridLayout = new QGridLayout(groupBox_2); + gridLayout->setObjectName("gridLayout"); + lbl_TabDeckEditor_aAnalyzeDeck = new QLabel(groupBox_2); + lbl_TabDeckEditor_aAnalyzeDeck->setObjectName("lbl_TabDeckEditor_aAnalyzeDeck"); + + gridLayout->addWidget(lbl_TabDeckEditor_aAnalyzeDeck, 0, 0, 1, 1); + + TabDeckEditor_aAnalyzeDeck = new SequenceEdit("TabDeckEditor/aAnalyzeDeck",groupBox_2); + TabDeckEditor_aAnalyzeDeck->setObjectName("TabDeckEditor_aAnalyzeDeck"); + + gridLayout->addWidget(TabDeckEditor_aAnalyzeDeck, 0, 1, 1, 1); + + lbl_TabDeckEditor_aLoadDeckFromClipboard = new QLabel(groupBox_2); + lbl_TabDeckEditor_aLoadDeckFromClipboard->setObjectName("lbl_TabDeckEditor_aLoadDeckFromClipboard"); + + gridLayout->addWidget(lbl_TabDeckEditor_aLoadDeckFromClipboard, 0, 2, 1, 1); + + TabDeckEditor_aLoadDeckFromClipboard = new SequenceEdit("TabDeckEditor/aLoadDeckFromClipboard",groupBox_2); + TabDeckEditor_aLoadDeckFromClipboard->setObjectName("TabDeckEditor_aLoadDeckFromClipboard"); + + gridLayout->addWidget(TabDeckEditor_aLoadDeckFromClipboard, 0, 3, 1, 1); + + lbl_TabDeckEditor_aClearFilterAll = new QLabel(groupBox_2); + lbl_TabDeckEditor_aClearFilterAll->setObjectName("lbl_TabDeckEditor_aClearFilterAll"); + + gridLayout->addWidget(lbl_TabDeckEditor_aClearFilterAll, 1, 0, 1, 1); + + TabDeckEditor_aClearFilterAll = new SequenceEdit("TabDeckEditor/aClearFilterAll",groupBox_2); + TabDeckEditor_aClearFilterAll->setObjectName("TabDeckEditor_aClearFilterAll"); + + gridLayout->addWidget(TabDeckEditor_aClearFilterAll, 1, 1, 1, 1); + + lbl_TabDeckEditor_aNewDeck = new QLabel(groupBox_2); + lbl_TabDeckEditor_aNewDeck->setObjectName("lbl_TabDeckEditor_aNewDeck"); + + gridLayout->addWidget(lbl_TabDeckEditor_aNewDeck, 1, 2, 1, 1); + + TabDeckEditor_aNewDeck = new SequenceEdit("TabDeckEditor/aNewDeck",groupBox_2); + TabDeckEditor_aNewDeck->setObjectName("TabDeckEditor_aNewDeck"); + + gridLayout->addWidget(TabDeckEditor_aNewDeck, 1, 3, 1, 1); + + lbl_TabDeckEditor_aClearFilterOne = new QLabel(groupBox_2); + lbl_TabDeckEditor_aClearFilterOne->setObjectName("lbl_TabDeckEditor_aClearFilterOne"); + + gridLayout->addWidget(lbl_TabDeckEditor_aClearFilterOne, 2, 0, 1, 1); + + TabDeckEditor_aClearFilterOne = new SequenceEdit("TabDeckEditor/aClearFilterOne",groupBox_2); + TabDeckEditor_aClearFilterOne->setObjectName("TabDeckEditor_aClearFilterOne"); + + gridLayout->addWidget(TabDeckEditor_aClearFilterOne, 2, 1, 1, 1); + + lbl_TabDeckEditor_aOpenCustomFolder = new QLabel(groupBox_2); + lbl_TabDeckEditor_aOpenCustomFolder->setObjectName("lbl_TabDeckEditor_aOpenCustomFolder"); + + gridLayout->addWidget(lbl_TabDeckEditor_aOpenCustomFolder, 2, 2, 1, 1); + + TabDeckEditor_aOpenCustomFolder = new SequenceEdit("TabDeckEditor/aOpenCustomFolder",groupBox_2); + TabDeckEditor_aOpenCustomFolder->setObjectName("TabDeckEditor_aOpenCustomFolder"); + + gridLayout->addWidget(TabDeckEditor_aOpenCustomFolder, 2, 3, 1, 1); + + lbl_TabDeckEditor_aClose = new QLabel(groupBox_2); + lbl_TabDeckEditor_aClose->setObjectName("lbl_TabDeckEditor_aClose"); + + gridLayout->addWidget(lbl_TabDeckEditor_aClose, 3, 0, 1, 1); + + TabDeckEditor_aClose = new SequenceEdit("TabDeckEditor/aClose",groupBox_2); + TabDeckEditor_aClose->setObjectName("TabDeckEditor_aClose"); + + gridLayout->addWidget(TabDeckEditor_aClose, 3, 1, 1, 1); + + lbl_TabDeckEditor_aPrintDeck = new QLabel(groupBox_2); + lbl_TabDeckEditor_aPrintDeck->setObjectName("lbl_TabDeckEditor_aPrintDeck"); + + gridLayout->addWidget(lbl_TabDeckEditor_aPrintDeck, 3, 2, 1, 1); + + TabDeckEditor_aPrintDeck = new SequenceEdit("TabDeckEditor/aPrintDeck",groupBox_2); + TabDeckEditor_aPrintDeck->setObjectName("TabDeckEditor_aPrintDeck"); + + gridLayout->addWidget(TabDeckEditor_aPrintDeck, 3, 3, 1, 1); + + lbl_TabDeckEditor_aEditSets = new QLabel(groupBox_2); + lbl_TabDeckEditor_aEditSets->setObjectName("lbl_TabDeckEditor_aEditSets"); + + gridLayout->addWidget(lbl_TabDeckEditor_aEditSets, 4, 0, 1, 1); + + TabDeckEditor_aEditSets = new SequenceEdit("TabDeckEditor/aEditSets",groupBox_2); + TabDeckEditor_aEditSets->setObjectName("TabDeckEditor_aEditSets"); + + gridLayout->addWidget(TabDeckEditor_aEditSets, 4, 1, 1, 1); + + lbl_TabDeckEditor_aRemoveCard = new QLabel(groupBox_2); + lbl_TabDeckEditor_aRemoveCard->setObjectName("lbl_TabDeckEditor_aRemoveCard"); + + gridLayout->addWidget(lbl_TabDeckEditor_aRemoveCard, 4, 2, 1, 1); + + TabDeckEditor_aRemoveCard = new SequenceEdit("TabDeckEditor/aRemoveCard",groupBox_2); + TabDeckEditor_aRemoveCard->setObjectName("TabDeckEditor_aRemoveCard"); + + gridLayout->addWidget(TabDeckEditor_aRemoveCard, 4, 3, 1, 1); + + lbl_TabDeckEditor_aEditTokens = new QLabel(groupBox_2); + lbl_TabDeckEditor_aEditTokens->setObjectName("lbl_TabDeckEditor_aEditTokens"); + + gridLayout->addWidget(lbl_TabDeckEditor_aEditTokens, 5, 0, 1, 1); + + TabDeckEditor_aEditTokens = new SequenceEdit("TabDeckEditor/aEditTokens",groupBox_2); + TabDeckEditor_aEditTokens->setObjectName("TabDeckEditor_aEditTokens"); + + gridLayout->addWidget(TabDeckEditor_aEditTokens, 5, 1, 1, 1); + + lbl_TabDeckEditor_aResetLayout = new QLabel(groupBox_2); + lbl_TabDeckEditor_aResetLayout->setObjectName("lbl_TabDeckEditor_aResetLayout"); + + gridLayout->addWidget(lbl_TabDeckEditor_aResetLayout, 5, 2, 1, 1); + + TabDeckEditor_aResetLayout = new SequenceEdit("TabDeckEditor/aResetLayout",groupBox_2); + TabDeckEditor_aResetLayout->setObjectName("TabDeckEditor_aResetLayout"); + + gridLayout->addWidget(TabDeckEditor_aResetLayout, 5, 3, 1, 1); + + lbl_TabDeckEditor_aIncrement = new QLabel(groupBox_2); + lbl_TabDeckEditor_aIncrement->setObjectName("lbl_TabDeckEditor_aIncrement"); + + gridLayout->addWidget(lbl_TabDeckEditor_aIncrement, 6, 0, 1, 1); + + TabDeckEditor_aIncrement = new SequenceEdit("TabDeckEditor/aIncrement",groupBox_2); + TabDeckEditor_aIncrement->setObjectName("TabDeckEditor_aIncrement"); + + gridLayout->addWidget(TabDeckEditor_aIncrement, 6, 1, 1, 1); + + lbl_TabDeckEditor_aSaveDeck = new QLabel(groupBox_2); + lbl_TabDeckEditor_aSaveDeck->setObjectName("lbl_TabDeckEditor_aSaveDeck"); + + gridLayout->addWidget(lbl_TabDeckEditor_aSaveDeck, 6, 2, 1, 1); + + TabDeckEditor_aSaveDeck = new SequenceEdit("TabDeckEditor/aSaveDeck",groupBox_2); + TabDeckEditor_aSaveDeck->setObjectName("TabDeckEditor_aSaveDeck"); + + gridLayout->addWidget(TabDeckEditor_aSaveDeck, 6, 3, 1, 1); + + lbl_TabDeckEditor_aDecrement = new QLabel(groupBox_2); + lbl_TabDeckEditor_aDecrement->setObjectName("lbl_TabDeckEditor_aDecrement"); + + gridLayout->addWidget(lbl_TabDeckEditor_aDecrement, 7, 0, 1, 1); + + TabDeckEditor_aDecrement = new SequenceEdit("TabDeckEditor/aDecrement",groupBox_2); + TabDeckEditor_aDecrement->setObjectName("TabDeckEditor_aDecrement"); + + gridLayout->addWidget(TabDeckEditor_aDecrement, 7, 1, 1, 1); + + lbl_TabDeckEditor_aSaveDeckAs = new QLabel(groupBox_2); + lbl_TabDeckEditor_aSaveDeckAs->setObjectName("lbl_TabDeckEditor_aSaveDeckAs"); + + gridLayout->addWidget(lbl_TabDeckEditor_aSaveDeckAs, 7, 2, 1, 1); + + TabDeckEditor_aSaveDeckAs = new SequenceEdit("TabDeckEditor/aSaveDeckAs",groupBox_2); + TabDeckEditor_aSaveDeckAs->setObjectName("TabDeckEditor_aSaveDeckAs"); + + gridLayout->addWidget(TabDeckEditor_aSaveDeckAs, 7, 3, 1, 1); + + lbl_TabDeckEditor_aLoadDeck = new QLabel(groupBox_2); + lbl_TabDeckEditor_aLoadDeck->setObjectName("lbl_TabDeckEditor_aLoadDeck"); + + gridLayout->addWidget(lbl_TabDeckEditor_aLoadDeck, 8, 0, 1, 1); + + TabDeckEditor_aLoadDeck = new SequenceEdit("TabDeckEditor/aLoadDeck",groupBox_2); + TabDeckEditor_aLoadDeck->setObjectName("TabDeckEditor_aLoadDeck"); + + gridLayout->addWidget(TabDeckEditor_aLoadDeck, 8, 1, 1, 1); + + lbl_TabDeckEditor_aSaveDeckToClipboard = new QLabel(groupBox_2); + lbl_TabDeckEditor_aSaveDeckToClipboard->setObjectName("lbl_TabDeckEditor_aSaveDeckToClipboard"); + + gridLayout->addWidget(lbl_TabDeckEditor_aSaveDeckToClipboard, 8, 2, 1, 1); + + TabDeckEditor_aSaveDeckToClipboard = new SequenceEdit("TabDeckEditor/aSaveDeckToClipboard",groupBox_2); + TabDeckEditor_aSaveDeckToClipboard->setObjectName("TabDeckEditor_aSaveDeckToClipboard"); + + gridLayout->addWidget(TabDeckEditor_aSaveDeckToClipboard, 8, 3, 1, 1); + + gridLayout_3->addWidget(groupBox_2, 0, 1, 1, 1); + + groupBox_3 = new QGroupBox(tab); + groupBox_3->setObjectName("groupBox_3"); + horizontalLayout = new QHBoxLayout(groupBox_3); + horizontalLayout->setObjectName("horizontalLayout"); + groupBox_4 = new QGroupBox(groupBox_3); + groupBox_4->setObjectName("groupBox_4"); + gridLayout_4 = new QGridLayout(groupBox_4); + gridLayout_4->setObjectName("gridLayout_4"); + lbl_abstractCounter_sSet = new QLabel(groupBox_4); + lbl_abstractCounter_sSet->setObjectName("lbl_abstractCounter_sSet"); + + gridLayout_4->addWidget(lbl_abstractCounter_sSet, 0, 0, 1, 1); + + abstractCounter_aSet = new SequenceEdit("Player/aSet",groupBox_4); + abstractCounter_aSet->setObjectName("abstractCounter_aSet"); + + gridLayout_4->addWidget(abstractCounter_aSet, 0, 1, 1, 1); + + lbl_abstractCounter_aInc = new QLabel(groupBox_4); + lbl_abstractCounter_aInc->setObjectName("lbl_abstractCounter_aInc"); + + gridLayout_4->addWidget(lbl_abstractCounter_aInc, 1, 0, 1, 1); + + abstractCounter_Inc = new SequenceEdit("Player/Inc",groupBox_4); + abstractCounter_Inc->setObjectName("abstractCounter_Inc"); + + gridLayout_4->addWidget(abstractCounter_Inc, 1, 1, 1, 1); + + lbl_abstractCounter_aDec = new QLabel(groupBox_4); + lbl_abstractCounter_aDec->setObjectName("lbl_abstractCounter_aDec"); + + gridLayout_4->addWidget(lbl_abstractCounter_aDec, 2, 0, 1, 1); + + abstractCounter_aDec = new SequenceEdit("Player/aDec",groupBox_4); + abstractCounter_aDec->setObjectName("abstractCounter_aDec"); + + gridLayout_4->addWidget(abstractCounter_aDec, 2, 1, 1, 1); + + horizontalLayout->addWidget(groupBox_4); + + groupBox_5 = new QGroupBox(groupBox_3); + groupBox_5->setObjectName("groupBox_5"); + gridLayout_6 = new QGridLayout(groupBox_5); + gridLayout_6->setObjectName("gridLayout_6"); + lbl_Player_aSCRed = new QLabel(groupBox_5); + lbl_Player_aSCRed->setObjectName("lbl_Player_aSCRed"); + + gridLayout_6->addWidget(lbl_Player_aSCRed, 0, 0, 1, 1); + + Player_aSCRed = new SequenceEdit("Player/aSCRed",groupBox_5); + Player_aSCRed->setObjectName("Player_aSCRed"); + + gridLayout_6->addWidget(Player_aSCRed, 0, 1, 1, 1); + + lbl_Player_aCCRed = new QLabel(groupBox_5); + lbl_Player_aCCRed->setObjectName("lbl_Player_aCCRed"); + + gridLayout_6->addWidget(lbl_Player_aCCRed, 1, 0, 1, 1); + + Player_aCCRed = new SequenceEdit("Player/aCCRed",groupBox_5); + Player_aCCRed->setObjectName("Player_aCCRed"); + + gridLayout_6->addWidget(Player_aCCRed, 1, 1, 1, 1); + + lbl_Player_aRCRed = new QLabel(groupBox_5); + lbl_Player_aRCRed->setObjectName("lbl_Player_aRCRed"); + + gridLayout_6->addWidget(lbl_Player_aRCRed, 2, 0, 1, 1); + + Player_aRCRed = new SequenceEdit("Player/aRCRed",groupBox_5); + Player_aRCRed->setObjectName("Player_aRCRed"); + + gridLayout_6->addWidget(Player_aRCRed, 2, 1, 1, 1); + + horizontalLayout->addWidget(groupBox_5); + + groupBox_6 = new QGroupBox(groupBox_3); + groupBox_6->setObjectName("groupBox_6"); + gridLayout_7 = new QGridLayout(groupBox_6); + gridLayout_7->setObjectName("gridLayout_7"); + lbl_Player_aSCGreen = new QLabel(groupBox_6); + lbl_Player_aSCGreen->setObjectName("lbl_Player_aSCGreen"); + + gridLayout_7->addWidget(lbl_Player_aSCGreen, 0, 0, 1, 1); + + Player_aSCGreen = new SequenceEdit("Player/aSCGreen",groupBox_6); + Player_aSCGreen->setObjectName("Player_aSCGreen"); + + gridLayout_7->addWidget(Player_aSCGreen, 0, 1, 1, 1); + + lbl_Player_aCCGreen = new QLabel(groupBox_6); + lbl_Player_aCCGreen->setObjectName("lbl_Player_aCCGreen"); + + gridLayout_7->addWidget(lbl_Player_aCCGreen, 1, 0, 1, 1); + + Player_aCCGreen = new SequenceEdit("Player/aCCGreen",groupBox_6); + Player_aCCGreen->setObjectName("Player_aCCGreen"); + + gridLayout_7->addWidget(Player_aCCGreen, 1, 1, 1, 1); + + lbl_Player_aRCGreen = new QLabel(groupBox_6); + lbl_Player_aRCGreen->setObjectName("lbl_Player_aRCGreen"); + + gridLayout_7->addWidget(lbl_Player_aRCGreen, 2, 0, 1, 1); + + Player_aRCGreen = new SequenceEdit("Player/aRCGreen",groupBox_6); + Player_aRCGreen->setObjectName("Player_aRCGreen"); + + gridLayout_7->addWidget(Player_aRCGreen, 2, 1, 1, 1); + + horizontalLayout->addWidget(groupBox_6); + + groupBox_7 = new QGroupBox(groupBox_3); + groupBox_7->setObjectName("groupBox_7"); + gridLayout_8 = new QGridLayout(groupBox_7); + gridLayout_8->setObjectName("gridLayout_8"); + lbl_Player_aSCYellow = new QLabel(groupBox_7); + lbl_Player_aSCYellow->setObjectName("lbl_Player_aSCYellow"); + + gridLayout_8->addWidget(lbl_Player_aSCYellow, 0, 0, 1, 1); + + Player_aSCYellow = new SequenceEdit("Player/aSCYellow",groupBox_7); + Player_aSCYellow->setObjectName("Player_aSCYellow"); + + gridLayout_8->addWidget(Player_aSCYellow, 0, 1, 1, 1); + + lbl_Player_aCCYellow = new QLabel(groupBox_7); + lbl_Player_aCCYellow->setObjectName("lbl_Player_aCCYellow"); + + gridLayout_8->addWidget(lbl_Player_aCCYellow, 1, 0, 1, 1); + + Player_aCCYellow = new SequenceEdit("Player/aCCYellow",groupBox_7); + Player_aCCYellow->setObjectName("Player_aCCYellow"); + + gridLayout_8->addWidget(Player_aCCYellow, 1, 1, 1, 1); + + lbl_Player_aRCYellow = new QLabel(groupBox_7); + lbl_Player_aRCYellow->setObjectName("lbl_Player_aRCYellow"); + + gridLayout_8->addWidget(lbl_Player_aRCYellow, 2, 0, 1, 1); + + Player_aRCYellow = new SequenceEdit("Player/aRCYellow",groupBox_7); + Player_aRCYellow->setObjectName("Player_aRCYellow"); + + gridLayout_8->addWidget(Player_aRCYellow, 2, 1, 1, 1); + + horizontalLayout->addWidget(groupBox_7); + + verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + gridLayout_3->addItem(verticalSpacer, 2, 0, 1, 1); + + tabWidget->addTab(tab, QString()); + tab_2 = new QWidget(); + tab_2->setObjectName("tab_2"); + gridLayout_17 = new QGridLayout(tab_2); + gridLayout_17->setObjectName("gridLayout_17"); + groupBox_9 = new QGroupBox(tab_2); + groupBox_9->setObjectName("groupBox_9"); + verticalLayout = new QVBoxLayout(groupBox_9); + verticalLayout->setObjectName("verticalLayout"); + groupBox_12 = new QGroupBox(groupBox_9); + groupBox_12->setObjectName("groupBox_12"); + gridLayout_12 = new QGridLayout(groupBox_12); + gridLayout_12->setObjectName("gridLayout_12"); + Player_aDecPT = new SequenceEdit("Player/aDecPT",groupBox_12); + Player_aDecPT->setObjectName("Player_aDecPT"); + + gridLayout_12->addWidget(Player_aDecPT, 2, 1, 1, 1); + + Player_aIncPT = new SequenceEdit("Player/aIncPT",groupBox_12); + Player_aIncPT->setObjectName("Player_aIncPT"); + + gridLayout_12->addWidget(Player_aIncPT, 1, 1, 1, 1); + + lbl_Player_aIncPT = new QLabel(groupBox_12); + lbl_Player_aIncPT->setObjectName("lbl_Player_aIncPT"); + + gridLayout_12->addWidget(lbl_Player_aIncPT, 1, 0, 1, 1); + + lbl_Player_aDecPT = new QLabel(groupBox_12); + lbl_Player_aDecPT->setObjectName("lbl_Player_aDecPT"); + + gridLayout_12->addWidget(lbl_Player_aDecPT, 2, 0, 1, 1); + + Player_aSetPT = new SequenceEdit("Player/aSetPT",groupBox_12); + Player_aSetPT->setObjectName("Player_aSetPT"); + + gridLayout_12->addWidget(Player_aSetPT, 0, 1, 1, 1); + + lbl_Player_aSetPT = new QLabel(groupBox_12); + lbl_Player_aSetPT->setObjectName("lbl_Player_aSetPT"); + + gridLayout_12->addWidget(lbl_Player_aSetPT, 0, 0, 1, 1); + + verticalLayout->addWidget(groupBox_12); + + groupBox_11 = new QGroupBox(groupBox_9); + groupBox_11->setObjectName("groupBox_11"); + gridLayout_11 = new QGridLayout(groupBox_11); + gridLayout_11->setObjectName("gridLayout_11"); + lbl_Player_aDecT = new QLabel(groupBox_11); + lbl_Player_aDecT->setObjectName("lbl_Player_aDecT"); + + gridLayout_11->addWidget(lbl_Player_aDecT, 1, 0, 1, 1); + + Player_aDecT = new SequenceEdit("Player/aDecT",groupBox_11); + Player_aDecT->setObjectName("Player_aDecT"); + + gridLayout_11->addWidget(Player_aDecT, 1, 1, 1, 1); + + lbl_Player_aIncT = new QLabel(groupBox_11); + lbl_Player_aIncT->setObjectName("lbl_Player_aIncT"); + + gridLayout_11->addWidget(lbl_Player_aIncT, 0, 0, 1, 1); + + Player_aIncT = new SequenceEdit("Player/aIncT",groupBox_11); + Player_aIncT->setObjectName("Player_aIncT"); + + gridLayout_11->addWidget(Player_aIncT, 0, 1, 1, 1); + + verticalLayout->addWidget(groupBox_11); + + groupBox_10 = new QGroupBox(groupBox_9); + groupBox_10->setObjectName("groupBox_10"); + gridLayout_10 = new QGridLayout(groupBox_10); + gridLayout_10->setObjectName("gridLayout_10"); + lbl_Player_aDecP = new QLabel(groupBox_10); + lbl_Player_aDecP->setObjectName("lbl_Player_aDecP"); + + gridLayout_10->addWidget(lbl_Player_aDecP, 1, 0, 1, 1); + + Player_aDecP = new SequenceEdit("Player/aDecP",groupBox_10); + Player_aDecP->setObjectName("Player_aDecP"); + + gridLayout_10->addWidget(Player_aDecP, 1, 1, 1, 1); + + Player_IncP = new SequenceEdit("Player/IncP",groupBox_10); + Player_IncP->setObjectName("Player_IncP"); + + gridLayout_10->addWidget(Player_IncP, 0, 1, 1, 1); + + lbl_Player_IncP = new QLabel(groupBox_10); + lbl_Player_IncP->setObjectName("lbl_Player_IncP"); + + gridLayout_10->addWidget(lbl_Player_IncP, 0, 0, 1, 1); + + verticalLayout->addWidget(groupBox_10); + + gridLayout_17->addWidget(groupBox_9, 0, 1, 1, 1); + + groupBox_8 = new QGroupBox(tab_2); + groupBox_8->setObjectName("groupBox_8"); + gridLayout_5 = new QGridLayout(groupBox_8); + gridLayout_5->setObjectName("gridLayout_5"); + lbl_TabGame_phase0 = new QLabel(groupBox_8); + lbl_TabGame_phase0->setObjectName("lbl_TabGame_phase0"); + + gridLayout_5->addWidget(lbl_TabGame_phase0, 0, 0, 1, 1); + + TabGame_phase0 = new SequenceEdit("Player/phase0",groupBox_8); + TabGame_phase0->setObjectName("TabGame_phase0"); + + gridLayout_5->addWidget(TabGame_phase0, 0, 1, 1, 1); + + lbl_TabGame_phase1 = new QLabel(groupBox_8); + lbl_TabGame_phase1->setObjectName("lbl_TabGame_phase1"); + + gridLayout_5->addWidget(lbl_TabGame_phase1, 1, 0, 1, 1); + + TabGame_phase1 = new SequenceEdit("Player/phase1",groupBox_8); + TabGame_phase1->setObjectName("TabGame_phase1"); + + gridLayout_5->addWidget(TabGame_phase1, 1, 1, 1, 1); + + lbl_TabGame_phase2 = new QLabel(groupBox_8); + lbl_TabGame_phase2->setObjectName("lbl_TabGame_phase2"); + + gridLayout_5->addWidget(lbl_TabGame_phase2, 2, 0, 1, 1); + + TabGame_phase2 = new SequenceEdit("Player/phase2",groupBox_8); + TabGame_phase2->setObjectName("TabGame_phase2"); + + gridLayout_5->addWidget(TabGame_phase2, 2, 1, 1, 1); + + lbl_TabGame_phase3 = new QLabel(groupBox_8); + lbl_TabGame_phase3->setObjectName("lbl_TabGame_phase3"); + + gridLayout_5->addWidget(lbl_TabGame_phase3, 3, 0, 1, 1); + + TabGame_phase3 = new SequenceEdit("Player/phase3",groupBox_8); + TabGame_phase3->setObjectName("TabGame_phase3"); + + gridLayout_5->addWidget(TabGame_phase3, 3, 1, 1, 1); + + lbl_TabGame_phase4 = new QLabel(groupBox_8); + lbl_TabGame_phase4->setObjectName("lbl_TabGame_phase4"); + + gridLayout_5->addWidget(lbl_TabGame_phase4, 4, 0, 1, 1); + + TabGame_phase4 = new SequenceEdit("Player/phase4",groupBox_8); + TabGame_phase4->setObjectName("TabGame_phase4"); + + gridLayout_5->addWidget(TabGame_phase4, 4, 1, 1, 1); + + lbl_TabGame_phase5 = new QLabel(groupBox_8); + lbl_TabGame_phase5->setObjectName("lbl_TabGame_phase5"); + + gridLayout_5->addWidget(lbl_TabGame_phase5, 5, 0, 1, 1); + + TabGame_phase5 = new SequenceEdit("Player/phase5",groupBox_8); + TabGame_phase5->setObjectName("TabGame_phase5"); + + gridLayout_5->addWidget(TabGame_phase5, 5, 1, 1, 1); + + lbl_TabGame_phase6 = new QLabel(groupBox_8); + lbl_TabGame_phase6->setObjectName("lbl_TabGame_phase6"); + + gridLayout_5->addWidget(lbl_TabGame_phase6, 6, 0, 1, 1); + + lbl_TabGame_phase7 = new QLabel(groupBox_8); + lbl_TabGame_phase7->setObjectName("lbl_TabGame_phase7"); + + gridLayout_5->addWidget(lbl_TabGame_phase7, 7, 0, 1, 1); + + TabGame_phase6 = new SequenceEdit("Player/phase6",groupBox_8); + TabGame_phase6->setObjectName("TabGame_phase6"); + + gridLayout_5->addWidget(TabGame_phase6, 6, 1, 1, 1); + + lbl_TabGame_phase8 = new QLabel(groupBox_8); + lbl_TabGame_phase8->setObjectName("lbl_TabGame_phase8"); + + gridLayout_5->addWidget(lbl_TabGame_phase8, 8, 0, 1, 1); + + TabGame_phase7 = new SequenceEdit("Player/phase7",groupBox_8); + TabGame_phase7->setObjectName("TabGame_phase7"); + + gridLayout_5->addWidget(TabGame_phase7, 7, 1, 1, 1); + + lbl_TabGame_phase9 = new QLabel(groupBox_8); + lbl_TabGame_phase9->setObjectName("lbl_TabGame_phase9"); + + gridLayout_5->addWidget(lbl_TabGame_phase9, 9, 0, 1, 1); + + TabGame_phase8 = new SequenceEdit("Player/phase8",groupBox_8); + TabGame_phase8->setObjectName("TabGame_phase8"); + + gridLayout_5->addWidget(TabGame_phase8, 8, 1, 1, 1); + + lbl_TabGame_phase10 = new QLabel(groupBox_8); + lbl_TabGame_phase10->setObjectName("lbl_TabGame_phase10"); + + gridLayout_5->addWidget(lbl_TabGame_phase10, 10, 0, 1, 1); + + TabGame_phase9 = new SequenceEdit("Player/phase9",groupBox_8); + TabGame_phase9->setObjectName("TabGame_phase9"); + + gridLayout_5->addWidget(TabGame_phase9, 9, 1, 1, 1); + + lbl_TabGame_aNextPhase = new QLabel(groupBox_8); + lbl_TabGame_aNextPhase->setObjectName("lbl_TabGame_aNextPhase"); + + gridLayout_5->addWidget(lbl_TabGame_aNextPhase, 11, 0, 1, 1); + + TabGame_phase10 = new SequenceEdit("Player/phase10",groupBox_8); + TabGame_phase10->setObjectName("TabGame_phase10"); + + gridLayout_5->addWidget(TabGame_phase10, 10, 1, 1, 1); + + lbl_TabGame_aNextTurn = new QLabel(groupBox_8); + lbl_TabGame_aNextTurn->setObjectName("lbl_TabGame_aNextTurn"); + + gridLayout_5->addWidget(lbl_TabGame_aNextTurn, 12, 0, 1, 1); + + TabGame_aNextPhase = new SequenceEdit("Player/aNextPhase",groupBox_8); + TabGame_aNextPhase->setObjectName("TabGame_aNextPhase"); + + gridLayout_5->addWidget(TabGame_aNextPhase, 11, 1, 1, 1); + + TabGame_aNextTurn = new SequenceEdit("Player/aNextTurn",groupBox_8); + TabGame_aNextTurn->setObjectName("TabGame_aNextTurn"); + + gridLayout_5->addWidget(TabGame_aNextTurn, 12, 1, 1, 1); + + gridLayout_17->addWidget(groupBox_8, 0, 0, 1, 1); + + groupBox_13 = new QGroupBox(tab_2); + groupBox_13->setObjectName("groupBox_13"); + gridLayout_13 = new QGridLayout(groupBox_13); + gridLayout_13->setObjectName("gridLayout_13"); + lbl_Player_aTap = new QLabel(groupBox_13); + lbl_Player_aTap->setObjectName("lbl_Player_aTap"); + + gridLayout_13->addWidget(lbl_Player_aTap, 0, 0, 1, 1); + + Player_aTap = new SequenceEdit("Player/aTap",groupBox_13); + Player_aTap->setObjectName("Player_aTap"); + + gridLayout_13->addWidget(Player_aTap, 0, 1, 1, 1); + + lbl_Player_aUntap = new QLabel(groupBox_13); + lbl_Player_aUntap->setObjectName("lbl_Player_aUntap"); + + gridLayout_13->addWidget(lbl_Player_aUntap, 1, 0, 1, 1); + + Player_aUntap = new SequenceEdit("Player/aUntap",groupBox_13); + Player_aUntap->setObjectName("Player_aUntap"); + + gridLayout_13->addWidget(Player_aUntap, 1, 1, 1, 1); + + lbl_Player_aUntapAll = new QLabel(groupBox_13); + lbl_Player_aUntapAll->setObjectName("lbl_Player_aUntapAll"); + + gridLayout_13->addWidget(lbl_Player_aUntapAll, 2, 0, 1, 1); + + Player_aUntapAll = new SequenceEdit("Player/aUntapAll",groupBox_13); + Player_aUntapAll->setObjectName("Player_aUntapAll"); + + gridLayout_13->addWidget(Player_aUntapAll, 2, 1, 1, 1); + + lbl_Player_aDoesntUntap = new QLabel(groupBox_13); + lbl_Player_aDoesntUntap->setObjectName("lbl_Player_aDoesntUntap"); + + gridLayout_13->addWidget(lbl_Player_aDoesntUntap, 3, 0, 1, 1); + + Player_aDoesntUntap = new SequenceEdit("Player/aDoesntUntap",groupBox_13); + Player_aDoesntUntap->setObjectName("Player_aDoesntUntap"); + + gridLayout_13->addWidget(Player_aDoesntUntap, 3, 1, 1, 1); + + lbl_Player_aFlip = new QLabel(groupBox_13); + lbl_Player_aFlip->setObjectName("lbl_Player_aFlip"); + + gridLayout_13->addWidget(lbl_Player_aFlip, 4, 0, 1, 1); + + Player_aFlip = new SequenceEdit("Player/aFlip",groupBox_13); + Player_aFlip->setObjectName("Player_aFlip"); + + gridLayout_13->addWidget(Player_aFlip, 4, 1, 1, 1); + + lbl_Player_aPeek = new QLabel(groupBox_13); + lbl_Player_aPeek->setObjectName("lbl_Player_aPeek"); + + gridLayout_13->addWidget(lbl_Player_aPeek, 5, 0, 1, 1); + + Player_aPeek = new SequenceEdit("Player/aPeek",groupBox_13); + Player_aPeek->setObjectName("Player_aPeek"); + + gridLayout_13->addWidget(Player_aPeek, 5, 1, 1, 1); + + lbl_Player_aPlay = new QLabel(groupBox_13); + lbl_Player_aPlay->setObjectName("lbl_Player_aPlay"); + + gridLayout_13->addWidget(lbl_Player_aPlay, 6, 0, 1, 1); + + Player_aPlay = new SequenceEdit("Player/aPlay",groupBox_13); + Player_aPlay->setObjectName("Player_aPlay"); + + gridLayout_13->addWidget(Player_aPlay, 6, 1, 1, 1); + + lbl_Player_aAttach = new QLabel(groupBox_13); + lbl_Player_aAttach->setObjectName("lbl_Player_aAttach"); + + gridLayout_13->addWidget(lbl_Player_aAttach, 7, 0, 1, 1); + + Player_aAttach = new SequenceEdit("Player/aAttach",groupBox_13); + Player_aAttach->setObjectName("Player_aAttach"); + + gridLayout_13->addWidget(Player_aAttach, 7, 1, 1, 1); + + lbl_Player_aUnattach = new QLabel(groupBox_13); + lbl_Player_aUnattach->setObjectName("lbl_Player_aUnattach"); + + gridLayout_13->addWidget(lbl_Player_aUnattach, 8, 0, 1, 1); + + Player_aUnattach = new SequenceEdit("Player/aUnattach",groupBox_13); + Player_aUnattach->setObjectName("Player_aUnattach"); + + gridLayout_13->addWidget(Player_aUnattach, 8, 1, 1, 1); + + lbl_Player_aClone = new QLabel(groupBox_13); + lbl_Player_aClone->setObjectName("lbl_Player_aClone"); + + gridLayout_13->addWidget(lbl_Player_aClone, 9, 0, 1, 1); + + Player_aClone = new SequenceEdit("Player/aClone",groupBox_13); + Player_aClone->setObjectName("Player_aClone"); + + gridLayout_13->addWidget(Player_aClone, 9, 1, 1, 1); + + lbl_Player_aCreateToken = new QLabel(groupBox_13); + lbl_Player_aCreateToken->setObjectName("lbl_Player_aCreateToken"); + + gridLayout_13->addWidget(lbl_Player_aCreateToken, 10, 0, 1, 1); + + Player_aCreateToken = new SequenceEdit("Player/aCreateToken",groupBox_13); + Player_aCreateToken->setObjectName("Player_aCreateToken"); + + gridLayout_13->addWidget(Player_aCreateToken, 10, 1, 1, 1); + + lbl_Player_aCreateAnotherToken = new QLabel(groupBox_13); + lbl_Player_aCreateAnotherToken->setObjectName("lbl_Player_aCreateAnotherToken"); + + gridLayout_13->addWidget(lbl_Player_aCreateAnotherToken, 11, 0, 1, 1); + + Player_aCreateAnotherToken = new SequenceEdit("Player/aCreateAnotherToken",groupBox_13); + Player_aCreateAnotherToken->setObjectName("Player_aCreateAnotherToken"); + + gridLayout_13->addWidget(Player_aCreateAnotherToken, 11, 1, 1, 1); + + lbl_Player_aSetAnnotation = new QLabel(groupBox_13); + lbl_Player_aSetAnnotation->setObjectName("lbl_Player_aSetAnnotation"); + + gridLayout_13->addWidget(lbl_Player_aSetAnnotation, 12, 0, 1, 1); + + Player_aSetAnnotation = new SequenceEdit("Player/aSetAnnotation",groupBox_13); + Player_aSetAnnotation->setObjectName("Player_aSetAnnotation"); + + gridLayout_13->addWidget(Player_aSetAnnotation, 12, 1, 1, 1); + + gridLayout_17->addWidget(groupBox_13, 0, 2, 1, 1); + + verticalSpacer_2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + gridLayout_17->addItem(verticalSpacer_2, 1, 1, 1, 1); + + tabWidget->addTab(tab_2, QString()); + tab_3 = new QWidget(); + tab_3->setObjectName("tab_3"); + gridLayout_20 = new QGridLayout(tab_3); + gridLayout_20->setObjectName("gridLayout_20"); + groupBox_15 = new QGroupBox(tab_3); + groupBox_15->setObjectName("groupBox_15"); + gridLayout_15 = new QGridLayout(groupBox_15); + gridLayout_15->setObjectName("gridLayout_15"); + lbl_Player_aMoveToBottomLibrary = new QLabel(groupBox_15); + lbl_Player_aMoveToBottomLibrary->setObjectName("lbl_Player_aMoveToBottomLibrary"); + + gridLayout_15->addWidget(lbl_Player_aMoveToBottomLibrary, 0, 0, 1, 1); + + Player_aMoveToBottomLibrary = new SequenceEdit("Player/aMoveToBottomLibrary",groupBox_15); + Player_aMoveToBottomLibrary->setObjectName("Player_aMoveToBottomLibrary"); + + gridLayout_15->addWidget(Player_aMoveToBottomLibrary, 0, 1, 1, 1); + + lbl_Player_aMoveToTopLibrary = new QLabel(groupBox_15); + lbl_Player_aMoveToTopLibrary->setObjectName("lbl_Player_aMoveToTopLibrary"); + + gridLayout_15->addWidget(lbl_Player_aMoveToTopLibrary, 1, 0, 1, 1); + + Player_aMoveToTopLibrary = new SequenceEdit("Player/aMoveToTopLibrary",groupBox_15); + Player_aMoveToTopLibrary->setObjectName("Player_aMoveToTopLibrary"); + + gridLayout_15->addWidget(Player_aMoveToTopLibrary, 1, 1, 1, 1); + + lbl_Player_aMoveToGraveyard = new QLabel(groupBox_15); + lbl_Player_aMoveToGraveyard->setObjectName("lbl_Player_aMoveToGraveyard"); + + gridLayout_15->addWidget(lbl_Player_aMoveToGraveyard, 2, 0, 1, 1); + + Player_aMoveToGraveyard = new SequenceEdit("Player/aMoveToGraveyard",groupBox_15); + Player_aMoveToGraveyard->setObjectName("Player_aMoveToGraveyard"); + + gridLayout_15->addWidget(Player_aMoveToGraveyard, 2, 1, 1, 1); + + lbl_Player_aMoveToExile = new QLabel(groupBox_15); + lbl_Player_aMoveToExile->setObjectName("lbl_Player_aMoveToExile"); + + gridLayout_15->addWidget(lbl_Player_aMoveToExile, 3, 0, 1, 1); + + Player_aMoveToExile = new SequenceEdit("Player/aMoveToExile",groupBox_15); + Player_aMoveToExile->setObjectName("Player_aMoveToExile"); + + gridLayout_15->addWidget(Player_aMoveToExile, 3, 1, 1, 1); + + lbl_Player_aMoveToHand = new QLabel(groupBox_15); + lbl_Player_aMoveToHand->setObjectName("lbl_Player_aMoveToHand"); + + gridLayout_15->addWidget(lbl_Player_aMoveToHand, 4, 0, 1, 1); + + Player_aMoveToHand = new SequenceEdit("Player/aMoveToHand",groupBox_15); + Player_aMoveToHand->setObjectName("Player_aMoveToHand"); + + gridLayout_15->addWidget(Player_aMoveToHand, 4, 1, 1, 1); + + gridLayout_20->addWidget(groupBox_15, 0, 1, 1, 1); + + groupBox_16 = new QGroupBox(tab_3); + groupBox_16->setObjectName("groupBox_16"); + gridLayout_16 = new QGridLayout(groupBox_16); + gridLayout_16->setObjectName("gridLayout_16"); + lbl_Player_aViewGraveyard = new QLabel(groupBox_16); + lbl_Player_aViewGraveyard->setObjectName("lbl_Player_aViewGraveyard"); + + gridLayout_16->addWidget(lbl_Player_aViewGraveyard, 0, 0, 1, 1); + + Player_aViewGraveyard = new SequenceEdit("Player/aViewGraveyard",groupBox_16); + Player_aViewGraveyard->setObjectName("Player_aViewGraveyard"); + + gridLayout_16->addWidget(Player_aViewGraveyard, 0, 1, 1, 1); + + lbl_Player_aViewLibrary = new QLabel(groupBox_16); + lbl_Player_aViewLibrary->setObjectName("lbl_Player_aViewLibrary"); + + gridLayout_16->addWidget(lbl_Player_aViewLibrary, 1, 0, 1, 1); + + Player_aViewLibrary = new SequenceEdit("Player/aViewLibrary",groupBox_16); + Player_aViewLibrary->setObjectName("Player_aViewLibrary"); + + gridLayout_16->addWidget(Player_aViewLibrary, 1, 1, 1, 1); + + lbl_Player_aViewTopCards = new QLabel(groupBox_16); + lbl_Player_aViewTopCards->setObjectName("lbl_Player_aViewTopCards"); + + gridLayout_16->addWidget(lbl_Player_aViewTopCards, 2, 0, 1, 1); + + Player_aViewTopCards = new SequenceEdit("Player/aViewTopCards",groupBox_16); + Player_aViewTopCards->setObjectName("Player_aViewTopCards"); + + gridLayout_16->addWidget(Player_aViewTopCards, 2, 1, 1, 1); + + lbl_Player_aViewSideboard = new QLabel(groupBox_16); + lbl_Player_aViewSideboard->setObjectName("lbl_Player_aViewSideboard"); + + gridLayout_16->addWidget(lbl_Player_aViewSideboard, 3, 0, 1, 1); + + Player_aViewSideboard = new SequenceEdit("Player/aViewSideboard",groupBox_16); + Player_aViewSideboard->setObjectName("Player_aViewSideboard"); + + gridLayout_16->addWidget(Player_aViewSideboard, 3, 1, 1, 1); + + lbl_Player_aViewRfg = new QLabel(groupBox_16); + lbl_Player_aViewRfg->setObjectName("lbl_Player_aViewRfg"); + + gridLayout_16->addWidget(lbl_Player_aViewRfg, 4, 0, 1, 1); + + Player_aViewRfg = new SequenceEdit("Player/aViewRfg",groupBox_16); + Player_aViewRfg->setObjectName("Player_aViewRfg"); + + gridLayout_16->addWidget(Player_aViewRfg, 4, 1, 1, 1); + + lbl_GameView_aCloseMostRecentZoneView = new QLabel(groupBox_16); + lbl_GameView_aCloseMostRecentZoneView->setObjectName("lbl_GameView_aCloseMostRecentZoneView"); + + gridLayout_16->addWidget(lbl_GameView_aCloseMostRecentZoneView, 5, 0, 1, 1); + + GameView_aCloseMostRecentZoneView = new SequenceEdit("Player/aCloseMostRecentZoneView",groupBox_16); + GameView_aCloseMostRecentZoneView->setObjectName("GameView_aCloseMostRecentZoneView"); + + gridLayout_16->addWidget(GameView_aCloseMostRecentZoneView, 5, 1, 1, 1); + + gridLayout_20->addWidget(groupBox_16, 0, 2, 1, 1); + + groupBox_17 = new QGroupBox(tab_3); + groupBox_17->setObjectName("groupBox_17"); + gridLayout_18 = new QGridLayout(groupBox_17); + gridLayout_18->setObjectName("gridLayout_18"); + DeckViewContainer_loadRemoteButton = new SequenceEdit("DeckViewContainer/loadRemoteButton",groupBox_17); + DeckViewContainer_loadRemoteButton->setObjectName("DeckViewContainer_loadRemoteButton"); + + gridLayout_18->addWidget(DeckViewContainer_loadRemoteButton, 2, 1, 1, 1); + + DeckViewContainer_loadLocalButton = new SequenceEdit("DeckViewContainer/loadLocalButton",groupBox_17); + DeckViewContainer_loadLocalButton->setObjectName("DeckViewContainer_loadLocalButton"); + + gridLayout_18->addWidget(DeckViewContainer_loadLocalButton, 0, 1, 1, 1); + + lbl_DeckViewContainer_loadRemoteButton = new QLabel(groupBox_17); + lbl_DeckViewContainer_loadRemoteButton->setObjectName("lbl_DeckViewContainer_loadRemoteButton"); + + gridLayout_18->addWidget(lbl_DeckViewContainer_loadRemoteButton, 2, 0, 1, 1); + + lbl_DeckViewContainer_loadLocalButton = new QLabel(groupBox_17); + lbl_DeckViewContainer_loadLocalButton->setObjectName("lbl_DeckViewContainer_loadLocalButton"); + + gridLayout_18->addWidget(lbl_DeckViewContainer_loadLocalButton, 0, 0, 1, 1); + + gridLayout_20->addWidget(groupBox_17, 1, 0, 1, 1); + + groupBox_18 = new QGroupBox(tab_3); + groupBox_18->setObjectName("groupBox_18"); + gridLayout_19 = new QGridLayout(groupBox_18); + gridLayout_19->setObjectName("gridLayout_19"); + lbl_Player_aDrawArrow = new QLabel(groupBox_18); + lbl_Player_aDrawArrow->setObjectName("lbl_Player_aDrawArrow"); + + gridLayout_19->addWidget(lbl_Player_aDrawArrow, 0, 0, 1, 1); + + Player_aDrawArrow = new SequenceEdit("Player/aDrawArrow",groupBox_18); + Player_aDrawArrow->setObjectName("Player_aDrawArrow"); + + gridLayout_19->addWidget(Player_aDrawArrow, 0, 1, 1, 1); + + lbl_TabGame_aLeaveGame = new QLabel(groupBox_18); + lbl_TabGame_aLeaveGame->setObjectName("lbl_TabGame_aLeaveGame"); + + gridLayout_19->addWidget(lbl_TabGame_aLeaveGame, 0, 2, 1, 1); + + TabGame_aLeaveGame = new SequenceEdit("Player/aLeaveGame",groupBox_18); + TabGame_aLeaveGame->setObjectName("TabGame_aLeaveGame"); + + gridLayout_19->addWidget(TabGame_aLeaveGame, 0, 3, 1, 1); + + lbl_TabGame_aRemoveLocalArrows = new QLabel(groupBox_18); + lbl_TabGame_aRemoveLocalArrows->setObjectName("lbl_TabGame_aRemoveLocalArrows"); + + gridLayout_19->addWidget(lbl_TabGame_aRemoveLocalArrows, 1, 0, 1, 1); + + TabGame_aRemoveLocalArrows = new SequenceEdit("Player/aRemoveLocalArrows",groupBox_18); + TabGame_aRemoveLocalArrows->setObjectName("TabGame_aRemoveLocalArrows"); + + gridLayout_19->addWidget(TabGame_aRemoveLocalArrows, 1, 1, 1, 1); + + lbl_TabGame_aConcede = new QLabel(groupBox_18); + lbl_TabGame_aConcede->setObjectName("lbl_TabGame_aConcede"); + + gridLayout_19->addWidget(lbl_TabGame_aConcede, 1, 2, 1, 1); + + TabGame_aConcede = new SequenceEdit("Player/aConcede",groupBox_18); + TabGame_aConcede->setObjectName("TabGame_aConcede"); + + gridLayout_19->addWidget(TabGame_aConcede, 1, 3, 1, 1); + + lbl_Player_aRollDie = new QLabel(groupBox_18); + lbl_Player_aRollDie->setObjectName("lbl_Player_aRollDie"); + + gridLayout_19->addWidget(lbl_Player_aRollDie, 2, 0, 1, 1); + + Player_aRollDie = new SequenceEdit("Player/aRollDie",groupBox_18); + Player_aRollDie->setObjectName("Player_aRollDie"); + + gridLayout_19->addWidget(Player_aRollDie, 2, 1, 1, 1); + + lbl_TabGame_aRotateViewCW = new QLabel(groupBox_18); + lbl_TabGame_aRotateViewCW->setObjectName("lbl_TabGame_aRotateViewCW"); + + gridLayout_19->addWidget(lbl_TabGame_aRotateViewCW, 2, 2, 1, 1); + + TabGame_aRotateViewCW = new SequenceEdit("Player/aRotateViewCW",groupBox_18); + TabGame_aRotateViewCW->setObjectName("TabGame_aRotateViewCW"); + + gridLayout_19->addWidget(TabGame_aRotateViewCW, 2, 3, 1, 1); + + lbl_Player_aShuffle = new QLabel(groupBox_18); + lbl_Player_aShuffle->setObjectName("lbl_Player_aShuffle"); + + gridLayout_19->addWidget(lbl_Player_aShuffle, 3, 0, 1, 1); + + Player_aShuffle = new SequenceEdit("Player/aShuffle",groupBox_18); + Player_aShuffle->setObjectName("Player_aShuffle"); + + gridLayout_19->addWidget(Player_aShuffle, 3, 1, 1, 1); + + lbl_TabGame_aRotateViewCCW = new QLabel(groupBox_18); + lbl_TabGame_aRotateViewCCW->setObjectName("lbl_TabGame_aRotateViewCCW"); + + gridLayout_19->addWidget(lbl_TabGame_aRotateViewCCW, 3, 2, 1, 1); + + TabGame_aRotateViewCCW = new SequenceEdit("Player/aRotateViewCCW",groupBox_18); + TabGame_aRotateViewCCW->setObjectName("TabGame_aRotateViewCCW"); + + gridLayout_19->addWidget(TabGame_aRotateViewCCW, 3, 3, 1, 1); + + gridLayout_20->addWidget(groupBox_18, 1, 1, 1, 2); + + groupBox_14 = new QGroupBox(tab_3); + groupBox_14->setObjectName("groupBox_14"); + gridLayout_14 = new QGridLayout(groupBox_14); + gridLayout_14->setObjectName("gridLayout_14"); + lbl_Player_aMulligan = new QLabel(groupBox_14); + lbl_Player_aMulligan->setObjectName("lbl_Player_aMulligan"); + + gridLayout_14->addWidget(lbl_Player_aMulligan, 4, 0, 1, 1); + + Player_aMulligan = new SequenceEdit("Player/aMulligan",groupBox_14); + Player_aMulligan->setObjectName("Player_aMulligan"); + + gridLayout_14->addWidget(Player_aMulligan, 4, 1, 1, 1); + + lbl_Player_aDrawCard = new QLabel(groupBox_14); + lbl_Player_aDrawCard->setObjectName("lbl_Player_aDrawCard"); + + gridLayout_14->addWidget(lbl_Player_aDrawCard, 0, 0, 1, 1); + + Player_aDrawCard = new SequenceEdit("Player/aDrawCard",groupBox_14); + Player_aDrawCard->setObjectName("Player_aDrawCard"); + + gridLayout_14->addWidget(Player_aDrawCard, 0, 1, 1, 1); + + lbl_Player_aDrawCards = new QLabel(groupBox_14); + lbl_Player_aDrawCards->setObjectName("lbl_Player_aDrawCards"); + + gridLayout_14->addWidget(lbl_Player_aDrawCards, 1, 0, 1, 1); + + Player_aDrawCards = new SequenceEdit("Player/aDrawCards",groupBox_14); + Player_aDrawCards->setObjectName("Player_aDrawCards"); + + gridLayout_14->addWidget(Player_aDrawCards, 1, 1, 1, 1); + + lbl_Player_aUndoDraw = new QLabel(groupBox_14); + lbl_Player_aUndoDraw->setObjectName("lbl_Player_aUndoDraw"); + + gridLayout_14->addWidget(lbl_Player_aUndoDraw, 2, 0, 1, 1); + + Player_aUndoDraw = new SequenceEdit("Player/aUndoDraw",groupBox_14); + Player_aUndoDraw->setObjectName("Player_aUndoDraw"); + + gridLayout_14->addWidget(Player_aUndoDraw, 2, 1, 1, 1); + + lbl_Player_aAlwaysRevealTopCard = new QLabel(groupBox_14); + lbl_Player_aAlwaysRevealTopCard->setObjectName("lbl_Player_aAlwaysRevealTopCard"); + + gridLayout_14->addWidget(lbl_Player_aAlwaysRevealTopCard, 3, 0, 1, 1); + + Player_aAlwaysRevealTopCard = new SequenceEdit("Player/aAlwaysRevealTopCard",groupBox_14); + Player_aAlwaysRevealTopCard->setObjectName("Player_aAlwaysRevealTopCard"); + + gridLayout_14->addWidget(Player_aAlwaysRevealTopCard, 3, 1, 1, 1); + gridLayout_20->addWidget(groupBox_14, 0, 0, 1, 1); + verticalSpacer_3 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout_20->addItem(verticalSpacer_3, 2, 1, 1, 1); + tabWidget->addTab(tab_3, QString()); + tab_4 = new QWidget(tabWidget); + QGridLayout* grid = new QGridLayout(tab_4); + grid->addWidget(groupBox_3); + grid->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding),1,0); + + tabWidget->addTab(tab_4, QString()); + gridLayout_9->addWidget(tabWidget, 0, 0, 1, 1); + tabWidget->setCurrentIndex(0); + + grid->setSpacing(3); + gridLayout->setSpacing(3); + gridLayout_2->setSpacing(3); + gridLayout_3->setSpacing(3); + gridLayout_4->setSpacing(3); + gridLayout_5->setSpacing(3); + gridLayout_6->setSpacing(3); + gridLayout_7->setSpacing(3); + gridLayout_8->setSpacing(3); + gridLayout_9->setSpacing(3); + gridLayout_10->setSpacing(3); + gridLayout_11->setSpacing(3); + gridLayout_12->setSpacing(3); + gridLayout_13->setSpacing(3); + gridLayout_14->setSpacing(3); + gridLayout_15->setSpacing(3); + gridLayout_16->setSpacing(3); + gridLayout_17->setSpacing(3); + gridLayout_18->setSpacing(3); + gridLayout_19->setSpacing(3); + gridLayout_20->setSpacing(3); + + verticalLayout->setSpacing(3); + horizontalLayout->setSpacing(3); + QMetaObject::connectSlotsByName(shortcutsTab); + retranslateUi(shortcutsTab); + } // setupUi + + void retranslateUi(QWidget *shortcutsTab) + { + shortcutsTab->setWindowTitle(QApplication::translate("shortcutsTab", "Form", 0)); + groupBox->setTitle(QApplication::translate("shortcutsTab", "Main Window", 0)); + lbl_MainWindow_aDeckEditor->setText(QApplication::translate("shortcutsTab", "Deck editor", 0)); + lbl_MainWindow_aSinglePlayer->setText(QApplication::translate("shortcutsTab", "Local gameplay", 0)); + lbl_MainWindow_aWatchReplay->setText(QApplication::translate("shortcutsTab", "Watch replay", 0)); + lbl_MainWindow_aConnect->setText(QApplication::translate("shortcutsTab", "Connect", 0)); + lbl_MainWindow_aRegister->setText(QApplication::translate("shortcutsTab", "Register", 0)); + lbl_MainWindow_aFullScreen->setText(QApplication::translate("shortcutsTab", "Full screen", 0)); + lbl_MainWindow_aSettings->setText(QApplication::translate("shortcutsTab", "Settings", 0)); + lbl_MainWindow_aCheckCardUpdates->setText(QApplication::translate("shortcutsTab", "Check for card updates", 0)); + lbl_MainWindow_aDisconnect->setText(QApplication::translate("shortcutsTab", "Disconnect", 0)); + lbl_MainWindow_aExit->setText(QApplication::translate("shortcutsTab", "Exit", 0)); + groupBox_2->setTitle(QApplication::translate("shortcutsTab", "Deck Editor", 0)); + lbl_TabDeckEditor_aAnalyzeDeck->setText(QApplication::translate("shortcutsTab", "Analyze deck", 0)); + lbl_TabDeckEditor_aLoadDeckFromClipboard->setText(QApplication::translate("shortcutsTab", "Load deck (clipboard)", 0)); + lbl_TabDeckEditor_aClearFilterAll->setText(QApplication::translate("shortcutsTab", "Clerar all filters", 0)); + lbl_TabDeckEditor_aNewDeck->setText(QApplication::translate("shortcutsTab", "New deck", 0)); + lbl_TabDeckEditor_aClearFilterOne->setText(QApplication::translate("shortcutsTab", "Clear one filter", 0)); + lbl_TabDeckEditor_aOpenCustomFolder->setText(QApplication::translate("shortcutsTab", "Open custom folder", 0)); + lbl_TabDeckEditor_aClose->setText(QApplication::translate("shortcutsTab", "Close", 0)); + lbl_TabDeckEditor_aPrintDeck->setText(QApplication::translate("shortcutsTab", "Print deck", 0)); + lbl_TabDeckEditor_aEditSets->setText(QApplication::translate("shortcutsTab", "Edit sets", 0)); + lbl_TabDeckEditor_aRemoveCard->setText(QApplication::translate("shortcutsTab", "Delete card", 0)); + lbl_TabDeckEditor_aEditTokens->setText(QApplication::translate("shortcutsTab", "Edit tokens", 0)); + lbl_TabDeckEditor_aResetLayout->setText(QApplication::translate("shortcutsTab", "Reset layout", 0)); + lbl_TabDeckEditor_aIncrement->setText(QApplication::translate("shortcutsTab", "Add card", 0)); + lbl_TabDeckEditor_aSaveDeck->setText(QApplication::translate("shortcutsTab", "Save deck", 0)); + lbl_TabDeckEditor_aDecrement->setText(QApplication::translate("shortcutsTab", "Remove card", 0)); + lbl_TabDeckEditor_aSaveDeckAs->setText(QApplication::translate("shortcutsTab", "Save deck as", 0)); + lbl_TabDeckEditor_aLoadDeck->setText(QApplication::translate("shortcutsTab", "Load deck", 0)); + lbl_TabDeckEditor_aSaveDeckToClipboard->setText(QApplication::translate("shortcutsTab", "Save deck (clipboard)", 0)); + groupBox_3->setTitle(QApplication::translate("shortcutsTab", "Counters", 0)); + groupBox_4->setTitle(QApplication::translate("shortcutsTab", "Life", 0)); + lbl_abstractCounter_sSet->setText(QApplication::translate("shortcutsTab", "Set", 0)); + lbl_abstractCounter_aInc->setText(QApplication::translate("shortcutsTab", "Add", 0)); + lbl_abstractCounter_aDec->setText(QApplication::translate("shortcutsTab", "Remove", 0)); + groupBox_5->setTitle(QApplication::translate("shortcutsTab", "Red", 0)); + lbl_Player_aSCRed->setText(QApplication::translate("shortcutsTab", "Set", 0)); + lbl_Player_aCCRed->setText(QApplication::translate("shortcutsTab", "Add", 0)); + lbl_Player_aRCRed->setText(QApplication::translate("shortcutsTab", "Remove", 0)); + groupBox_6->setTitle(QApplication::translate("shortcutsTab", "Green", 0)); + lbl_Player_aSCGreen->setText(QApplication::translate("shortcutsTab", "Set", 0)); + lbl_Player_aCCGreen->setText(QApplication::translate("shortcutsTab", "Add", 0)); + lbl_Player_aRCGreen->setText(QApplication::translate("shortcutsTab", "Remove", 0)); + groupBox_7->setTitle(QApplication::translate("shortcutsTab", "Yellow", 0)); + lbl_Player_aSCYellow->setText(QApplication::translate("shortcutsTab", "Set", 0)); + lbl_Player_aCCYellow->setText(QApplication::translate("shortcutsTab", "Add", 0)); + lbl_Player_aRCYellow->setText(QApplication::translate("shortcutsTab", "Remove", 0)); + tabWidget->setTabText(tabWidget->indexOf(tab), QApplication::translate("shortcutsTab", "Mainwindow / Deck editor", 0)); + groupBox_9->setTitle(QApplication::translate("shortcutsTab", "Power / toughness", 0)); + groupBox_12->setTitle(QApplication::translate("shortcutsTab", "Power and toughness", 0)); + lbl_Player_aIncPT->setText(QApplication::translate("shortcutsTab", "Add (+1/+1)", 0)); + lbl_Player_aDecPT->setText(QApplication::translate("shortcutsTab", "Remove (-1/-1)", 0)); + lbl_Player_aSetPT->setText(QApplication::translate("shortcutsTab", "Set", 0)); + groupBox_11->setTitle(QApplication::translate("shortcutsTab", "Toughness", 0)); + lbl_Player_aDecT->setText(QApplication::translate("shortcutsTab", "Remove (-0/-1)", 0)); + lbl_Player_aIncT->setText(QApplication::translate("shortcutsTab", "Add (+0/+1)", 0)); + groupBox_10->setTitle(QApplication::translate("shortcutsTab", "Power", 0)); + lbl_Player_aDecP->setText(QApplication::translate("shortcutsTab", "Remove (-1/-0)", 0)); + lbl_Player_IncP->setText(QApplication::translate("shortcutsTab", "Add (+1/+0)", 0)); + groupBox_8->setTitle(QApplication::translate("shortcutsTab", "Game Phases", 0)); + lbl_TabGame_phase0->setText(QApplication::translate("shortcutsTab", "Untap", 0)); + lbl_TabGame_phase1->setText(QApplication::translate("shortcutsTab", "Upkeep", 0)); + lbl_TabGame_phase2->setText(QApplication::translate("shortcutsTab", "Draw", 0)); + lbl_TabGame_phase3->setText(QApplication::translate("shortcutsTab", "Main 1", 0)); + lbl_TabGame_phase4->setText(QApplication::translate("shortcutsTab", "Start combat", 0)); + lbl_TabGame_phase5->setText(QApplication::translate("shortcutsTab", "Attack", 0)); + lbl_TabGame_phase6->setText(QApplication::translate("shortcutsTab", "Block", 0)); + lbl_TabGame_phase7->setText(QApplication::translate("shortcutsTab", "Damage", 0)); + lbl_TabGame_phase8->setText(QApplication::translate("shortcutsTab", "End combat", 0)); + lbl_TabGame_phase9->setText(QApplication::translate("shortcutsTab", "Main 2", 0)); + lbl_TabGame_phase10->setText(QApplication::translate("shortcutsTab", "End", 0)); + lbl_TabGame_aNextPhase->setText(QApplication::translate("shortcutsTab", "Next phase", 0)); + lbl_TabGame_aNextTurn->setText(QApplication::translate("shortcutsTab", "Next turn", 0)); + groupBox_13->setTitle(QApplication::translate("shortcutsTab", "Player", 0)); + lbl_Player_aTap->setText(QApplication::translate("shortcutsTab", "Tap Card", 0)); + lbl_Player_aUntap->setText(QApplication::translate("shortcutsTab", "Untap Card", 0)); + lbl_Player_aUntapAll->setText(QApplication::translate("shortcutsTab", "Untap all", 0)); + lbl_Player_aDoesntUntap->setText(QApplication::translate("shortcutsTab", "Toogle untap", 0)); + lbl_Player_aFlip->setText(QApplication::translate("shortcutsTab", "Flip card", 0)); + lbl_Player_aPeek->setText(QApplication::translate("shortcutsTab", "Peek card", 0)); + lbl_Player_aPlay->setText(QApplication::translate("shortcutsTab", "Play card", 0)); + lbl_Player_aAttach->setText(QApplication::translate("shortcutsTab", "Attach card", 0)); + lbl_Player_aUnattach->setText(QApplication::translate("shortcutsTab", "Unattach card", 0)); + lbl_Player_aClone->setText(QApplication::translate("shortcutsTab", "Clone card", 0)); + lbl_Player_aCreateToken->setText(QApplication::translate("shortcutsTab", "Create token", 0)); + lbl_Player_aCreateAnotherToken->setText(QApplication::translate("shortcutsTab", "Create another token", 0)); + lbl_Player_aSetAnnotation->setText(QApplication::translate("shortcutsTab", "Set annotation", 0)); + tabWidget->setTabText(tabWidget->indexOf(tab_2), QApplication::translate("shortcutsTab", "Phases / P/T / Player", 0)); + groupBox_15->setTitle(QApplication::translate("shortcutsTab", "Move card to", 0)); + lbl_Player_aMoveToBottomLibrary->setText(QApplication::translate("shortcutsTab", "Bottom library", 0)); + lbl_Player_aMoveToTopLibrary->setText(QApplication::translate("shortcutsTab", "Top library", 0)); + lbl_Player_aMoveToGraveyard->setText(QApplication::translate("shortcutsTab", "Graveyard", 0)); + lbl_Player_aMoveToExile->setText(QApplication::translate("shortcutsTab", "Exile", 0)); + lbl_Player_aMoveToHand->setText(QApplication::translate("shortcutsTab", "Hand", 0)); + groupBox_16->setTitle(QApplication::translate("shortcutsTab", "View", 0)); + lbl_Player_aViewGraveyard->setText(QApplication::translate("shortcutsTab", "Graveyard", 0)); + lbl_Player_aViewLibrary->setText(QApplication::translate("shortcutsTab", "Library", 0)); + lbl_Player_aViewTopCards->setText(QApplication::translate("shortcutsTab", "Tops card of library", 0)); + lbl_Player_aViewSideboard->setText(QApplication::translate("shortcutsTab", "Sideboard", 0)); + lbl_Player_aViewRfg->setText(QApplication::translate("shortcutsTab", "Exile", 0)); + lbl_GameView_aCloseMostRecentZoneView->setText(QApplication::translate("shortcutsTab", "Close recent view", 0)); + groupBox_17->setTitle(QApplication::translate("shortcutsTab", "Pre-play", 0)); + lbl_DeckViewContainer_loadRemoteButton->setText(QApplication::translate("shortcutsTab", "Load remote deck", 0)); + lbl_DeckViewContainer_loadLocalButton->setText(QApplication::translate("shortcutsTab", "Load local deck", 0)); + groupBox_18->setTitle(QApplication::translate("shortcutsTab", "Game play", 0)); + lbl_Player_aDrawArrow->setText(QApplication::translate("shortcutsTab", "Draw arrow", 0)); + lbl_TabGame_aLeaveGame->setText(QApplication::translate("shortcutsTab", "Leave game", 0)); + lbl_TabGame_aRemoveLocalArrows->setText(QApplication::translate("shortcutsTab", "Remove local arrows", 0)); + lbl_TabGame_aConcede->setText(QApplication::translate("shortcutsTab", "Concede", 0)); + lbl_Player_aRollDie->setText(QApplication::translate("shortcutsTab", "Roll dice", 0)); + lbl_TabGame_aRotateViewCW->setText(QApplication::translate("shortcutsTab", "Rotate view CW", 0)); + lbl_Player_aShuffle->setText(QApplication::translate("shortcutsTab", "Shuffle library", 0)); + lbl_TabGame_aRotateViewCCW->setText(QApplication::translate("shortcutsTab", "Rotate view CCW", 0)); + groupBox_14->setTitle(QApplication::translate("shortcutsTab", "Draw", 0)); + lbl_Player_aMulligan->setText(QApplication::translate("shortcutsTab", "Mulligan", 0)); + lbl_Player_aDrawCard->setText(QApplication::translate("shortcutsTab", "Draw card", 0)); + lbl_Player_aDrawCards->setText(QApplication::translate("shortcutsTab", "Draw cards", 0)); + lbl_Player_aUndoDraw->setText(QApplication::translate("shortcutsTab", "Undo draw", 0)); + lbl_Player_aAlwaysRevealTopCard->setText(QApplication::translate("shortcutsTab", "Always reveal top card", 0)); + tabWidget->setTabText(tabWidget->indexOf(tab_3), QApplication::translate("shortcutsTab", "Draw / Move / View / Game play", 0)); + tabWidget->setTabText(tabWidget->indexOf(tab_4), QApplication::translate("shortcutsTab","Counters", 0)); + } // retranslateUi + +}; + +namespace Ui { + class shortcutsTab: public Ui_shortcutsTab {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_SHORTCUTSTAB_H diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 11d4590c..9a3fb54d 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -1,11 +1,34 @@ #include "settingscache.h" #include +#if QT_VERSION >= 0x050000 + #include +#else + #include +#endif + +QString SettingsCache::getSettingsPath() +{ + QString file = ""; + +#ifndef PORTABLE_BUILD +#if QT_VERSION >= 0x050000 + file = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + #else + file = QDesktopServices::storageLocation(QDesktopServices::DataLocation); + #endif + file.append("/settings/"); +#endif + + return file; +} SettingsCache::SettingsCache() { settings = new QSettings(this); + shortcutsSettings = new ShortcutsSettings(getSettingsPath(),this); lang = settings->value("personal/lang").toString(); + keepalive = settings->value("personal/keepalive", 5).toInt(); deckPath = settings->value("paths/decks").toString(); replaysPath = settings->value("paths/replays").toString(); @@ -42,6 +65,7 @@ SettingsCache::SettingsCache() spectatorNotificationsEnabled = settings->value("interface/specnotificationsenabled", false).toBool(); doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool(); playToStack = settings->value("interface/playtostack", false).toBool(); + annotateTokens = settings->value("interface/annotatetokens", false).toBool(); cardInfoMinimized = settings->value("interface/cardinfominimized", 0).toInt(); tabGameSplitterSizes = settings->value("interface/tabgame_splittersizes").toByteArray(); displayCardNames = settings->value("cards/displaycardnames", true).toBool(); @@ -50,8 +74,11 @@ SettingsCache::SettingsCache() minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 5).toInt(); tapAnimation = settings->value("cards/tapanimation", true).toBool(); chatMention = settings->value("chat/mention", true).toBool(); + chatMentionCompleter = settings->value("chat/mentioncompleter", true).toBool(); chatMentionForeground = settings->value("chat/mentionforeground", true).toBool(); + chatHighlightForeground = settings->value("chat/highlightforeground", true).toBool(); chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString(); + chatHighlightColor = settings->value("chat/highlightcolor", "A6120D").toString(); zoneViewSortByName = settings->value("zoneview/sortbyname", true).toBool(); zoneViewSortByType = settings->value("zoneview/sortbytype", true).toBool(); @@ -77,6 +104,29 @@ SettingsCache::SettingsCache() masterVolume = settings->value("sound/mastervolume", 100).toInt(); cardInfoViewMode = settings->value("cards/cardinfoviewmode", 0).toInt(); + highlightWords = settings->value("personal/highlightWords", QString()).toString(); + gameDescription = settings->value("game/gamedescription","").toString(); + maxPlayers = settings->value("game/maxplayers", 2).toInt(); + gameTypes = settings->value("game/gametypes","").toString(); + onlyBuddies = settings->value("game/onlybuddies", false).toBool(); + onlyRegistered = settings->value("game/onlyregistered", true).toBool(); + spectatorsAllowed = settings->value("game/spectatorsallowed", true).toBool(); + spectatorsNeedPassword = settings->value("game/spectatorsneedpassword", false).toBool(); + spectatorsCanTalk = settings->value("game/spectatorscantalk", false).toBool(); + spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool(); + rememberGameSettings = settings->value("game/remembergamesettings", true).toBool(); + clientID = settings->value("personal/clientid", "notset").toString(); + + QString file = getSettingsPath(); + file.append("layouts/deckLayout.ini"); + + QSettings layout_settings(file , QSettings::IniFormat); + deckEditorLayoutState = layout_settings.value("layouts/deckEditor_state").toByteArray(); + deckEditorGeometry = layout_settings.value("layouts/deckEditor_geometry").toByteArray(); + + deckEditorCardSize = layout_settings.value("layouts/deckEditor_CardSize", QSize(250,500)).toSize(); + deckEditorFilterSize = layout_settings.value("layouts/deckEditor_FilterSize", QSize(250,250)).toSize(); + deckEditorDeckSize = layout_settings.value("layouts/deckEditor_DeckSize", QSize(250,360)).toSize(); } void SettingsCache::setCardInfoViewMode(const int _viewMode) { @@ -84,6 +134,11 @@ void SettingsCache::setCardInfoViewMode(const int _viewMode) { settings->setValue("cards/cardinfoviewmode", cardInfoViewMode); } +void SettingsCache::setHighlightWords(const QString &_highlightWords) { + highlightWords = _highlightWords; + settings->setValue("personal/highlightWords", highlightWords); +} + void SettingsCache::setMasterVolume(int _masterVolume) { masterVolume = _masterVolume; settings->setValue("sound/mastervolume", masterVolume); @@ -219,6 +274,12 @@ void SettingsCache::setPlayToStack(int _playToStack) settings->setValue("interface/playtostack", playToStack); } +void SettingsCache::setAnnotateTokens(int _annotateTokens) +{ + annotateTokens = _annotateTokens; + settings->setValue("interface/annotatetokens", annotateTokens); +} + void SettingsCache::setCardInfoMinimized(int _cardInfoMinimized) { cardInfoMinimized = _cardInfoMinimized; @@ -270,16 +331,33 @@ void SettingsCache::setChatMention(int _chatMention) { settings->setValue("chat/mention", chatMention); } +void SettingsCache::setChatMentionCompleter(const int _enableMentionCompleter) +{ + chatMentionCompleter = _enableMentionCompleter; + settings->setValue("chat/mentioncompleter", chatMentionCompleter); + emit chatMentionCompleterChanged(); +} + void SettingsCache::setChatMentionForeground(int _chatMentionForeground) { chatMentionForeground = _chatMentionForeground; settings->setValue("chat/mentionforeground", chatMentionForeground); } +void SettingsCache::setChatHighlightForeground(int _chatHighlightForeground) { + chatHighlightForeground = _chatHighlightForeground; + settings->setValue("chat/highlightforeground", chatHighlightForeground); +} + void SettingsCache::setChatMentionColor(const QString &_chatMentionColor) { chatMentionColor = _chatMentionColor; settings->setValue("chat/mentioncolor", chatMentionColor); } +void SettingsCache::setChatHighlightColor(const QString &_chatHighlightColor) { + chatHighlightColor = _chatHighlightColor; + settings->setValue("chat/highlightcolor", chatHighlightColor); +} + void SettingsCache::setZoneViewSortByName(int _zoneViewSortByName) { zoneViewSortByName = _zoneViewSortByName; @@ -354,3 +432,151 @@ void SettingsCache::setPixmapCacheSize(const int _pixmapCacheSize) settings->setValue("personal/pixmapCacheSize", pixmapCacheSize); emit pixmapCacheSizeChanged(pixmapCacheSize); } + +void SettingsCache::setClientID(QString _clientID) +{ + clientID = _clientID; + settings->setValue("personal/clientid", clientID); +} + +QStringList SettingsCache::getCountries() const +{ + static QStringList countries = QStringList() + << "ad" << "ae" << "af" << "ag" << "ai" << "al" << "am" << "ao" << "aq" << "ar" + << "as" << "at" << "au" << "aw" << "ax" << "az" << "ba" << "bb" << "bd" << "be" + << "bf" << "bg" << "bh" << "bi" << "bj" << "bl" << "bm" << "bn" << "bo" << "bq" + << "br" << "bs" << "bt" << "bv" << "bw" << "by" << "bz" << "ca" << "cc" << "cd" + << "cf" << "cg" << "ch" << "ci" << "ck" << "cl" << "cm" << "cn" << "co" << "cr" + << "cu" << "cv" << "cw" << "cx" << "cy" << "cz" << "de" << "dj" << "dk" << "dm" + << "do" << "dz" << "ec" << "ee" << "eg" << "eh" << "er" << "es" << "et" << "fi" + << "fj" << "fk" << "fm" << "fo" << "fr" << "ga" << "gb" << "gd" << "ge" << "gf" + << "gg" << "gh" << "gi" << "gl" << "gm" << "gn" << "gp" << "gq" << "gr" << "gs" + << "gt" << "gu" << "gw" << "gy" << "hk" << "hm" << "hn" << "hr" << "ht" << "hu" + << "id" << "ie" << "il" << "im" << "in" << "io" << "iq" << "ir" << "is" << "it" + << "je" << "jm" << "jo" << "jp" << "ke" << "kg" << "kh" << "ki" << "km" << "kn" + << "kp" << "kr" << "kw" << "ky" << "kz" << "la" << "lb" << "lc" << "li" << "lk" + << "lr" << "ls" << "lt" << "lu" << "lv" << "ly" << "ma" << "mc" << "md" << "me" + << "mf" << "mg" << "mh" << "mk" << "ml" << "mm" << "mn" << "mo" << "mp" << "mq" + << "mr" << "ms" << "mt" << "mu" << "mv" << "mw" << "mx" << "my" << "mz" << "na" + << "nc" << "ne" << "nf" << "ng" << "ni" << "nl" << "no" << "np" << "nr" << "nu" + << "nz" << "om" << "pa" << "pe" << "pf" << "pg" << "ph" << "pk" << "pl" << "pm" + << "pn" << "pr" << "ps" << "pt" << "pw" << "py" << "qa" << "re" << "ro" << "rs" + << "ru" << "rw" << "sa" << "sb" << "sc" << "sd" << "se" << "sg" << "sh" << "si" + << "sj" << "sk" << "sl" << "sm" << "sn" << "so" << "sr" << "ss" << "st" << "sv" + << "sx" << "sy" << "sz" << "tc" << "td" << "tf" << "tg" << "th" << "tj" << "tk" + << "tl" << "tm" << "tn" << "to" << "tr" << "tt" << "tv" << "tw" << "tz" << "ua" + << "ug" << "um" << "us" << "uy" << "uz" << "va" << "vc" << "ve" << "vg" << "vi" + << "vn" << "vu" << "wf" << "ws" << "ye" << "yt" << "za" << "zm" << "zw"; + + return countries; +} + +void SettingsCache::setDeckEditorLayoutState(const QByteArray &value) +{ + deckEditorLayoutState = value; + + QString file = getSettingsPath(); + file.append("layouts/deckLayout.ini"); + QSettings layout_settings(file , QSettings::IniFormat); + layout_settings.setValue("layouts/deckEditor_state",value); +} + +void SettingsCache::setDeckEditorGeometry(const QByteArray &value) +{ + deckEditorGeometry = value; + + QString file = getSettingsPath(); + file.append("layouts/deckLayout.ini"); + QSettings layout_settings(file , QSettings::IniFormat); + layout_settings.setValue("layouts/deckEditor_geometry",value); +} + +void SettingsCache::setDeckEditorCardSize(const QSize &value) +{ + deckEditorCardSize = value; + + QString file = getSettingsPath(); + file.append("layouts/deckLayout.ini"); + QSettings layout_settings(file , QSettings::IniFormat); + layout_settings.setValue("layouts/deckEditor_CardSize",value); +} + +void SettingsCache::setDeckEditorDeckSize(const QSize &value) +{ + deckEditorDeckSize = value; + + QString file = getSettingsPath(); + file.append("layouts/deckLayout.ini"); + QSettings layout_settings(file , QSettings::IniFormat); + layout_settings.setValue("layouts/deckEditor_DeckSize",value); +} + +void SettingsCache::setDeckEditorFilterSize(const QSize &value) +{ + deckEditorFilterSize = value; + + QString file = getSettingsPath(); + file.append("layouts/deckLayout.ini"); + QSettings layout_settings(file , QSettings::IniFormat); + layout_settings.setValue("layouts/deckEditor_FilterSize",value); +} + +void SettingsCache::setGameDescription(const QString _gameDescription) +{ + gameDescription = _gameDescription; + settings->setValue("game/gamedescription", gameDescription); +} + +void SettingsCache::setMaxPlayers(const int _maxPlayers) +{ + maxPlayers = _maxPlayers; + settings->setValue("game/maxplayers", maxPlayers); +} + +void SettingsCache::setGameTypes(const QString _gameTypes) +{ + gameTypes = _gameTypes; + settings->setValue("game/gametypes", gameTypes); +} + +void SettingsCache::setOnlyBuddies(const bool _onlyBuddies) +{ + onlyBuddies = _onlyBuddies; + settings->setValue("game/onlybuddies", onlyBuddies); +} + +void SettingsCache::setOnlyRegistered(const bool _onlyRegistered) +{ + onlyRegistered = _onlyRegistered; + settings->setValue("game/onlyregistered", onlyRegistered); +} + +void SettingsCache::setSpectatorsAllowed(const bool _spectatorsAllowed) +{ + spectatorsAllowed = _spectatorsAllowed; + settings->setValue("game/spectatorsallowed", spectatorsAllowed); +} + +void SettingsCache::setSpectatorsNeedPassword(const bool _spectatorsNeedPassword) +{ + spectatorsNeedPassword = _spectatorsNeedPassword; + settings->setValue("game/spectatorsneedpassword", spectatorsNeedPassword); +} + +void SettingsCache::setSpectatorsCanTalk(const bool _spectatorsCanTalk) +{ + spectatorsCanTalk = _spectatorsCanTalk; + settings->setValue("game/spectatorscantalk", spectatorsCanTalk); +} + +void SettingsCache::setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEverything) +{ + spectatorsCanSeeEverything = _spectatorsCanSeeEverything; + settings->setValue("game/spectatorscanseeeverything", spectatorsCanSeeEverything); +} + +void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings) +{ + rememberGameSettings = _rememberGameSettings; + settings->setValue("game/remembergamesettings", rememberGameSettings); +} \ No newline at end of file diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index cfc81113..6e439ca1 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -2,11 +2,15 @@ #define SETTINGSCACHE_H #include +#include +#include +#include "shortcutssettings.h" +// the falbacks are used for cards without a muid #define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" -#define PIC_URL_FALLBACK "http://mtgimage.com/set/!setcode!/!name!.jpg" -#define PIC_URL_HQ_DEFAULT "http://mtgimage.com/multiverseid/!cardid!.jpg" -#define PIC_URL_HQ_FALLBACK "http://mtgimage.com/set/!setcode!/!name!.jpg" +#define PIC_URL_FALLBACK "http://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card" +#define PIC_URL_HQ_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card" +#define PIC_URL_HQ_FALLBACK "http://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card" // size should be a multiple of 64 #define PIXMAPCACHE_SIZE_DEFAULT 2047 #define PIXMAPCACHE_SIZE_MIN 64 @@ -36,9 +40,10 @@ signals: void ignoreUnregisteredUserMessagesChanged(); void pixmapCacheSizeChanged(int newSizeInMBs); void masterVolumeChanged(int value); + void chatMentionCompleterChanged(); private: QSettings *settings; - + ShortcutsSettings *shortcutsSettings; QByteArray mainWindowGeometry; QString lang; QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath, themeName; @@ -48,6 +53,7 @@ private: bool spectatorNotificationsEnabled; bool doubleClickToPlay; bool playToStack; + bool annotateTokens; int cardInfoMinimized; QByteArray tabGameSplitterSizes; bool displayCardNames; @@ -56,8 +62,11 @@ private: int minPlayersForMultiColumnLayout; bool tapAnimation; bool chatMention; + bool chatMentionCompleter; QString chatMentionColor; + QString chatHighlightColor; bool chatMentionForeground; + bool chatHighlightForeground; bool zoneViewSortByName, zoneViewSortByType, zoneViewPileView; bool soundEnabled; QString soundPath; @@ -69,6 +78,7 @@ private: QString picUrlHq; QString picUrlFallback; QString picUrlHqFallback; + QString clientID; bool attemptAutoConnect; int pixmapCacheSize; bool scaleCards; @@ -77,6 +87,22 @@ private: bool leftJustified; int masterVolume; int cardInfoViewMode; + QString highlightWords; + QString gameDescription; + int maxPlayers; + QString gameTypes; + bool onlyBuddies; + bool onlyRegistered; + bool spectatorsAllowed; + bool spectatorsNeedPassword; + bool spectatorsCanTalk; + bool spectatorsCanSeeEverything; + bool rememberGameSettings; + int keepalive; + QByteArray deckEditorLayoutState, deckEditorGeometry; + QSize deckEditorFilterSize, deckEditorDeckSize, deckEditorCardSize; + QString getSettingsPath(); + public: SettingsCache(); const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; } @@ -88,6 +114,7 @@ public: QString getTokenDatabasePath() const { return tokenDatabasePath; } QString getThemeName() const { return themeName; } QString getChatMentionColor() const { return chatMentionColor; } + QString getChatHighlightColor() const { return chatHighlightColor; } bool getPicDownload() const { return picDownload; } bool getPicDownloadHq() const { return picDownloadHq; } bool getNotificationsEnabled() const { return notificationsEnabled; } @@ -95,6 +122,7 @@ public: bool getDoubleClickToPlay() const { return doubleClickToPlay; } bool getPlayToStack() const { return playToStack; } + bool getAnnotateTokens() const { return annotateTokens; } int getCardInfoMinimized() const { return cardInfoMinimized; } QByteArray getTabGameSplitterSizes() const { return tabGameSplitterSizes; } bool getDisplayCardNames() const { return displayCardNames; } @@ -103,7 +131,9 @@ public: int getMinPlayersForMultiColumnLayout() const { return minPlayersForMultiColumnLayout; } bool getTapAnimation() const { return tapAnimation; } bool getChatMention() const { return chatMention; } + bool getChatMentionCompleter() const { return chatMentionCompleter; } bool getChatMentionForeground() const { return chatMentionForeground; } + bool getChatHighlightForeground() const { return chatHighlightForeground; } bool getZoneViewSortByName() const { return zoneViewSortByName; } bool getZoneViewSortByType() const { return zoneViewSortByType; } /** @@ -129,6 +159,33 @@ public: bool getLeftJustified() const { return leftJustified; } int getMasterVolume() const { return masterVolume; } int getCardInfoViewMode() const { return cardInfoViewMode; } + QStringList getCountries() const; + QString getHighlightWords() const { return highlightWords; } + QString getGameDescription() const { return gameDescription; } + int getMaxPlayers() const { return maxPlayers; } + QString getGameTypes() const { return gameTypes; } + bool getOnlyBuddies() const { return onlyBuddies; } + bool getOnlyRegistered() const { return onlyRegistered; } + bool getSpectatorsAllowed() const { return spectatorsAllowed; } + bool getSpectatorsNeedPassword() const { return spectatorsNeedPassword; } + bool getSpectatorsCanTalk() const { return spectatorsCanTalk; } + bool getSpectatorsCanSeeEverything() const { return spectatorsCanSeeEverything; } + bool getRememberGameSettings() const { return rememberGameSettings; } + int getKeepAlive() const { return keepalive; } + void setClientID(QString clientID); + QString getClientID() { return clientID; } + QByteArray getDeckEditorLayoutState() const { return deckEditorLayoutState; } + void setDeckEditorLayoutState(const QByteArray &value); + QByteArray getDeckEditorGeometry() const { return deckEditorGeometry; } + void setDeckEditorGeometry(const QByteArray &value); + QSize getDeckEditorCardSize() const { return deckEditorCardSize; } + void setDeckEditorCardSize(const QSize &value); + QSize getDeckEditorDeckSize() const { return deckEditorDeckSize; } + void setDeckEditorDeckSize(const QSize &value); + QSize getDeckEditorFilterSize() const { return deckEditorFilterSize; } + void setDeckEditorFilterSize(const QSize &value); + ShortcutsSettings& shortcuts() const { return *shortcutsSettings; } + public slots: void setMainWindowGeometry(const QByteArray &_mainWindowGeometry); void setLang(const QString &_lang); @@ -139,12 +196,14 @@ public slots: void setTokenDatabasePath(const QString &_tokenDatabasePath); void setThemeName(const QString &_themeName); void setChatMentionColor(const QString &_chatMentionColor); + void setChatHighlightColor(const QString &_chatHighlightColor); void setPicDownload(int _picDownload); void setPicDownloadHq(int _picDownloadHq); void setNotificationsEnabled(int _notificationsEnabled); void setSpectatorNotificationsEnabled(int _spectatorNotificationsEnabled); void setDoubleClickToPlay(int _doubleClickToPlay); void setPlayToStack(int _playToStack); + void setAnnotateTokens(int _annotateTokens); void setCardInfoMinimized(int _cardInfoMinimized); void setTabGameSplitterSizes(const QByteArray &_tabGameSplitterSizes); void setDisplayCardNames(int _displayCardNames); @@ -153,7 +212,9 @@ public slots: void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout); void setTapAnimation(int _tapAnimation); void setChatMention(int _chatMention); + void setChatMentionCompleter(int _chatMentionCompleter); void setChatMentionForeground(int _chatMentionForeground); + void setChatHighlightForeground(int _chatHighlightForeground); void setZoneViewSortByName(int _zoneViewSortByName); void setZoneViewSortByType(int _zoneViewSortByType); void setZoneViewPileView(int _zoneViewPileView); @@ -175,6 +236,17 @@ public slots: void setLeftJustified( const int _leftJustified); void setMasterVolume(const int _masterVolume); void setCardInfoViewMode(const int _viewMode); + void setHighlightWords(const QString &_highlightWords); + void setGameDescription(const QString _gameDescription); + void setMaxPlayers(const int _maxPlayers); + void setGameTypes(const QString _gameTypes); + void setOnlyBuddies(const bool _onlyBuddies); + void setOnlyRegistered(const bool _onlyRegistered); + void setSpectatorsAllowed(const bool _spectatorsAllowed); + void setSpectatorsNeedPassword(const bool _spectatorsNeedPassword); + void setSpectatorsCanTalk(const bool _spectatorsCanTalk); + void setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEverything); + void setRememberGameSettings(const bool _rememberGameSettings); }; extern SettingsCache *settingsCache; diff --git a/cockatrice/src/shortcutssettings.cpp b/cockatrice/src/shortcutssettings.cpp new file mode 100644 index 00000000..242140a3 --- /dev/null +++ b/cockatrice/src/shortcutssettings.cpp @@ -0,0 +1,228 @@ +#include "shortcutssettings.h" +#include +#include + +ShortcutsSettings::ShortcutsSettings(QString settingsPath, QObject *parent) : QObject(parent) +{ + this->settingsFilePath = settingsPath; + this->settingsFilePath.append("shortcuts.ini"); + fillDefaultShorcuts(); + shortCuts = QMap >(defaultShortCuts); + + bool exists = QFile(settingsFilePath).exists(); + + QSettings shortCutsFile(settingsFilePath, QSettings::IniFormat); + + if(exists){ + shortCutsFile.beginGroup("Custom"); + const QStringList customKeys = shortCutsFile.allKeys(); + for(QStringList::const_iterator it = customKeys.constBegin(); it != customKeys.constEnd(); ++it) + { + QString stringSecuence = shortCutsFile.value(*it).toString(); + QList secuenceList = parseSecuenceString(stringSecuence); + shortCuts.insert(*it, secuenceList); + } + shortCutsFile.endGroup(); + } +} + +QList ShortcutsSettings::getShortcut(QString name) +{ + if(shortCuts.contains(name)) + return shortCuts.value(name); + + return defaultShortCuts.value(name, QList()); +} + +QKeySequence ShortcutsSettings::getSingleShortcut(QString name) +{ + return getShortcut(name).at(0); +} + +QString ShortcutsSettings::getDefaultShortcutString(QString name) +{ + return stringifySecuence(defaultShortCuts.value(name)); +} + +QString ShortcutsSettings::getShortcutString(QString name) +{ + return stringifySecuence(shortCuts.value(name)); +} + +QString ShortcutsSettings::stringifySecuence(QList secuence) const +{ + QString stringSecuence; + for(int i=0; i < secuence.size(); ++i) + { + stringSecuence.append(secuence.at(i).toString(QKeySequence::PortableText)); + if(i < secuence.size() - 1) + stringSecuence.append(";"); + } + + return stringSecuence; +} + +QList ShortcutsSettings::parseSecuenceString(QString stringSecuence) +{ + QStringList secuences = stringSecuence.split(";"); + QList secuenceList; + for(QStringList::const_iterator ss = secuences.constBegin(); ss != secuences.constEnd(); ++ss) + { + secuenceList.append(QKeySequence(*ss, QKeySequence::PortableText)); + } + + return secuenceList; +} + +void ShortcutsSettings::setShortcuts(QString name, QList secuence) +{ + shortCuts[name] = secuence; + + QSettings shortCutsFile(settingsFilePath, QSettings::IniFormat); + shortCutsFile.beginGroup("Custom"); + QString stringSecuence = stringifySecuence(secuence); + shortCutsFile.setValue(name, stringSecuence); + shortCutsFile.endGroup(); + emit shortCutchanged(); +} + +void ShortcutsSettings::setShortcuts(QString name, QKeySequence secuence) +{ + setShortcuts(name, QList() << secuence); +} + +void ShortcutsSettings::setShortcuts(QString name, QString secuences) +{ + setShortcuts(name,parseSecuenceString(secuences)); +} + +bool ShortcutsSettings::isValid(QString name, QString secuences) +{ + QString checkKey = name.left(name.indexOf("/")); + + QStringList stringSecuences = secuences.split(";"); + + QList allKeys = shortCuts.keys(); + for(int i=0; i < allKeys.size(); i++){ + QString key = allKeys.at(i); + if(key.startsWith(checkKey) || key.startsWith("MainWindow") || checkKey.startsWith("MainWindow")) + { + QString storedSecuence = stringifySecuence(shortCuts.value(key)); + for(int j = 0; j < stringSecuences.size(); j++) + { + if(storedSecuence.contains(stringSecuences.at(j))) + return false; + } + } + } + return true; +} + +void ShortcutsSettings::fillDefaultShorcuts() +{ + defaultShortCuts["MainWindow/aCheckCardUpdates"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aConnect"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aDeckEditor"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aDisconnect"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aExit"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aFullScreen"] = parseSecuenceString("Ctrl+F"); + defaultShortCuts["MainWindow/aRegister"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aSettings"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aSinglePlayer"] = parseSecuenceString(""); + defaultShortCuts["MainWindow/aWatchReplay"] = parseSecuenceString(""); + + defaultShortCuts["TabDeckEditor/aAnalyzeDeck"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aClearFilterAll"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aClearFilterOne"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aClose"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aDecrement"] = parseSecuenceString("-"); + defaultShortCuts["TabDeckEditor/aEditSets"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aEditTokens"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aIncrement"] = parseSecuenceString("+"); + defaultShortCuts["TabDeckEditor/aLoadDeck"] = parseSecuenceString("Ctrl+O"); + defaultShortCuts["TabDeckEditor/aLoadDeckFromClipboard"] = parseSecuenceString("Ctrl+V"); + defaultShortCuts["TabDeckEditor/aNewDeck"] = parseSecuenceString("Ctrl+N"); + defaultShortCuts["TabDeckEditor/aOpenCustomFolder"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aPrintDeck"] = parseSecuenceString("Ctrl+P"); + defaultShortCuts["TabDeckEditor/aRemoveCard"] = parseSecuenceString("Del"); + defaultShortCuts["TabDeckEditor/aResetLayout"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aSaveDeck"] = parseSecuenceString("Ctrl+S"); + defaultShortCuts["TabDeckEditor/aSaveDeckAs"] = parseSecuenceString(""); + defaultShortCuts["TabDeckEditor/aSaveDeckToClipboard"] = parseSecuenceString("Ctrl+C"); + + defaultShortCuts["DeckViewContainer/loadLocalButton"] = parseSecuenceString("Ctrl+O"); + defaultShortCuts["DeckViewContainer/loadRemoteButton"] = parseSecuenceString("Ctrl+Alt+O"); + + defaultShortCuts["Player/aDec"] = parseSecuenceString("F11"); + defaultShortCuts["Player/aInc"] = parseSecuenceString("F12"); + defaultShortCuts["Player/aSet"] = parseSecuenceString("Ctrl+L"); + defaultShortCuts["Player/aCloseMostRecentZoneView"] = parseSecuenceString("Esc"); + defaultShortCuts["Player/IncP"] = parseSecuenceString("Ctrl++"); + defaultShortCuts["Player/aAlwaysRevealTopCard"] = parseSecuenceString("Ctrl+N"); + defaultShortCuts["Player/aAttach"] = parseSecuenceString("Ctrl+A"); + defaultShortCuts["Player/aCCGreen"] = parseSecuenceString(""); + defaultShortCuts["Player/aCCRed"] = parseSecuenceString(""); + defaultShortCuts["Player/aCCYellow"] = parseSecuenceString(""); + defaultShortCuts["Player/aClone"] = parseSecuenceString("Ctrl+J"); + defaultShortCuts["Player/aCreateAnotherToken"] = parseSecuenceString("Ctrl+G"); + defaultShortCuts["Player/aCreateToken"] = parseSecuenceString("Ctrl+T"); + defaultShortCuts["Player/aDecP"] = parseSecuenceString("Ctrl+-"); + defaultShortCuts["Player/aDecPT"] = parseSecuenceString("Ctrl+Alt+-"); + defaultShortCuts["Player/aDecT"] = parseSecuenceString("Alt+-"); + defaultShortCuts["Player/aDoesntUntap"] = parseSecuenceString(""); + defaultShortCuts["Player/aDrawArrow"] = parseSecuenceString(""); + defaultShortCuts["Player/aDrawCard"] = parseSecuenceString("Ctrl+D"); + defaultShortCuts["Player/aDrawCards"] = parseSecuenceString("Ctrl+E"); + defaultShortCuts["Player/aFlip"] = parseSecuenceString(""); + defaultShortCuts["Player/aIncPT"] = parseSecuenceString("Ctrl+Alt++"); + defaultShortCuts["Player/aIncT"] = parseSecuenceString("Alt++"); + defaultShortCuts["Player/aMoveToBottomLibrary"] = parseSecuenceString(""); + defaultShortCuts["Player/aMoveToExile"] = parseSecuenceString(""); + defaultShortCuts["Player/aMoveToGraveyard"] = parseSecuenceString("Ctrl+Del"); + defaultShortCuts["Player/aMoveToHand"] = parseSecuenceString(""); + defaultShortCuts["Player/aMoveToTopLibrary"] = parseSecuenceString(""); + defaultShortCuts["Player/aMulligan"] = parseSecuenceString("Ctrl+M"); + defaultShortCuts["Player/aPeek"] = parseSecuenceString(""); + defaultShortCuts["Player/aPlay"] = parseSecuenceString(""); + defaultShortCuts["Player/aRCGreen"] = parseSecuenceString(""); + defaultShortCuts["Player/aRCRed"] = parseSecuenceString(""); + defaultShortCuts["Player/aRCYellow"] = parseSecuenceString(""); + defaultShortCuts["Player/aRollDie"] = parseSecuenceString("Ctrl+I"); + defaultShortCuts["Player/aSCGreen"] = parseSecuenceString(""); + defaultShortCuts["Player/aSCRed"] = parseSecuenceString(""); + defaultShortCuts["Player/aSCYellow"] = parseSecuenceString(""); + defaultShortCuts["Player/aSetAnnotation"] = parseSecuenceString(""); + defaultShortCuts["Player/aSetPT"] = parseSecuenceString("Ctrl+P"); + defaultShortCuts["Player/aShuffle"] = parseSecuenceString("Ctrl+S"); + defaultShortCuts["Player/aTap"] = parseSecuenceString(""); + defaultShortCuts["Player/aUnattach"] = parseSecuenceString(""); + defaultShortCuts["Player/aUndoDraw"] = parseSecuenceString("Ctrl+Shift+D"); + defaultShortCuts["Player/aUntap"] = parseSecuenceString(""); + defaultShortCuts["Player/aUntapAll"] = parseSecuenceString("Ctrl+U"); + defaultShortCuts["Player/aViewGraveyard"] = parseSecuenceString("F4"); + defaultShortCuts["Player/aViewLibrary"] = parseSecuenceString("F3"); + defaultShortCuts["Player/aViewRfg"] = parseSecuenceString(""); + defaultShortCuts["Player/aViewSideboard"] = parseSecuenceString("Ctrl+F3"); + defaultShortCuts["Player/aViewTopCards"] = parseSecuenceString("Ctrl+W"); + defaultShortCuts["Player/aConcede"] = parseSecuenceString("F2"); + defaultShortCuts["Player/aLeaveGame"] = parseSecuenceString("Ctrl+Q"); + defaultShortCuts["Player/aNextPhase"] = parseSecuenceString("Ctrl+Space;Tab"); + defaultShortCuts["Player/aNextTurn"] = parseSecuenceString("Ctrl+Return;Ctrl+Enter"); + defaultShortCuts["Player/aRemoveLocalArrows"] = parseSecuenceString("Ctrl+R"); + defaultShortCuts["Player/aRotateViewCCW"] = parseSecuenceString("Ctrl+["); + defaultShortCuts["Player/aRotateViewCW"] = parseSecuenceString("Ctrl+]"); + defaultShortCuts["Player/phase0"] = parseSecuenceString("F5"); + defaultShortCuts["Player/phase1"] = parseSecuenceString(""); + defaultShortCuts["Player/phase10"] = parseSecuenceString("F10"); + defaultShortCuts["Player/phase2"] = parseSecuenceString("F6"); + defaultShortCuts["Player/phase3"] = parseSecuenceString("F7"); + defaultShortCuts["Player/phase4"] = parseSecuenceString("F8"); + defaultShortCuts["Player/phase5"] = parseSecuenceString(""); + defaultShortCuts["Player/phase6"] = parseSecuenceString(""); + defaultShortCuts["Player/phase7"] = parseSecuenceString(""); + defaultShortCuts["Player/phase8"] = parseSecuenceString(""); + defaultShortCuts["Player/phase9"] = parseSecuenceString("F9"); + defaultShortCuts["tab_room/aClearChat"] = parseSecuenceString("F12"); + defaultShortCuts["DlgLoadDeckFromClipboard/refreshButton"] = parseSecuenceString("F5"); +} + diff --git a/cockatrice/src/shortcutssettings.h b/cockatrice/src/shortcutssettings.h new file mode 100644 index 00000000..45324106 --- /dev/null +++ b/cockatrice/src/shortcutssettings.h @@ -0,0 +1,40 @@ +#ifndef SHORTCUTSSETTINGS_H +#define SHORTCUTSSETTINGS_H + +#include +#include +#include +#include + +class ShortcutsSettings : public QObject +{ + Q_OBJECT +public: + ShortcutsSettings(QString settingsFilePath, QObject *parent = 0); + ~ShortcutsSettings() { } + + QList getShortcut(QString name); + QKeySequence getSingleShortcut(QString name); + + QString getDefaultShortcutString(QString name); + QString getShortcutString(QString name); + + void setShortcuts(QString name, QList secuence); + void setShortcuts(QString name, QKeySequence secuence); + void setShortcuts(QString name, QString secuences); + + bool isValid(QString name, QString secuences); +signals: + void shortCutchanged(); + +private: + QString settingsFilePath; + QMap > shortCuts; + QMap > defaultShortCuts; + void fillDefaultShorcuts(); + + QString stringifySecuence(QList secuence) const; + QList parseSecuenceString(QString stringSecuence); +}; + +#endif // SHORTCUTSSETTINGS_H diff --git a/cockatrice/src/tab_admin.cpp b/cockatrice/src/tab_admin.cpp index 615471e5..697644e7 100644 --- a/cockatrice/src/tab_admin.cpp +++ b/cockatrice/src/tab_admin.cpp @@ -58,10 +58,13 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool connect(updateServerMessageButton, SIGNAL(clicked()), this, SLOT(actUpdateServerMessage())); shutdownServerButton = new QPushButton; connect(shutdownServerButton, SIGNAL(clicked()), this, SLOT(actShutdownServer())); + reloadConfigButton = new QPushButton; + connect(reloadConfigButton, SIGNAL(clicked()), this, SLOT(actReloadConfig())); QVBoxLayout *vbox = new QVBoxLayout; vbox->addWidget(updateServerMessageButton); vbox->addWidget(shutdownServerButton); + vbox->addWidget(reloadConfigButton); vbox->addStretch(); adminGroupBox = new QGroupBox; @@ -87,6 +90,7 @@ void TabAdmin::retranslateUi() { updateServerMessageButton->setText(tr("Update server &message")); shutdownServerButton->setText(tr("&Shut down server")); + reloadConfigButton->setText(tr("&Reload configuration")); adminGroupBox->setTitle(tr("Server administration functions")); unlockButton->setText(tr("&Unlock functions")); @@ -110,6 +114,12 @@ void TabAdmin::actShutdownServer() } } +void TabAdmin::actReloadConfig() +{ + Command_ReloadConfig cmd; + client->sendCommand(client->prepareAdminCommand(cmd)); +} + void TabAdmin::actUnlock() { if (QMessageBox::question(this, tr("Unlock administration functions"), tr("Do you really want to unlock the administration functions?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { diff --git a/cockatrice/src/tab_admin.h b/cockatrice/src/tab_admin.h index cc5ae6a9..d0bd3148 100644 --- a/cockatrice/src/tab_admin.h +++ b/cockatrice/src/tab_admin.h @@ -28,7 +28,7 @@ private: bool locked; AbstractClient *client; bool fullAdmin; - QPushButton *updateServerMessageButton, *shutdownServerButton; + QPushButton *updateServerMessageButton, *shutdownServerButton, *reloadConfigButton; QGroupBox *adminGroupBox; QPushButton *unlockButton, *lockButton; signals: @@ -36,6 +36,7 @@ signals: private slots: void actUpdateServerMessage(); void actShutdownServer(); + void actReloadConfig(); void actUnlock(); void actLock(); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index a875e1f4..f3117c9c 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -19,6 +19,10 @@ #include #include #include +#include +#include +#include +#include #include "tab_deck_editor.h" #include "window_sets.h" #include "carddatabase.h" @@ -47,108 +51,37 @@ void SearchLineEdit::keyPressEvent(QKeyEvent *event) QLineEdit::keyPressEvent(event); } -TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) - : Tab(_tabSupervisor, parent), modified(false) +void TabDeckEditor::createShowHideDocksButtons() { - aClearFilterAll = new QAction(QString(), this); - aClearFilterAll->setIcon(QIcon("theme:icons/clearsearch.svg")); - connect(aClearFilterAll, SIGNAL(triggered()), this, SLOT(actClearFilterAll())); + btnFilter = new QPushButton(QIcon("theme:icons/view.svg"),QString()); + btnFilter->setObjectName("btnFilter"); + btnFilter->setCheckable(true); + btnFilter->setChecked(true); + btnFilter->setMaximumWidth(30); + searchLayout->addWidget(btnFilter); - aClearFilterOne = new QAction(QString(), this); - aClearFilterOne->setIcon(QIcon("theme:icons/decrement.svg")); - connect(aClearFilterOne, SIGNAL(triggered()), this, SLOT(actClearFilterOne())); + btnDeck = new QPushButton(QIcon("theme:hand.svg"),QString()); + btnDeck->setObjectName("btnDeck"); + btnDeck->setCheckable(true); + btnDeck->setChecked(true); + btnDeck->setMaximumWidth(30); + searchLayout->addWidget(btnDeck); - searchEdit = new SearchLineEdit; -#if QT_VERSION >= 0x050300 - searchEdit->addAction(QIcon("theme:icons/search.svg"), QLineEdit::LeadingPosition); -#endif - searchEdit->setObjectName("searchEdit"); - - setFocusProxy(searchEdit); - setFocusPolicy(Qt::ClickFocus); - - searchEdit->installEventFilter(&searchKeySignals); - connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); - connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard())); - connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard())); - - QToolBar *deckEditToolBar = new QToolBar; - deckEditToolBar->setOrientation(Qt::Horizontal); - deckEditToolBar->setIconSize(QSize(24, 24)); - - QHBoxLayout *searchLayout = new QHBoxLayout; - searchLayout->addWidget(deckEditToolBar); - searchLayout->addWidget(searchEdit); - - databaseModel = new CardDatabaseModel(db, this); - databaseDisplayModel = new CardDatabaseDisplayModel(this); - databaseDisplayModel->setSourceModel(databaseModel); - databaseDisplayModel->setFilterKeyColumn(0); - databaseDisplayModel->sort(0, Qt::AscendingOrder); - - databaseView = new QTreeView(); - databaseView->setFocusProxy(searchEdit); - databaseView->setModel(databaseDisplayModel); - databaseView->setUniformRowHeights(true); - databaseView->setRootIsDecorated(false); - databaseView->setAlternatingRowColors(true); - databaseView->setSortingEnabled(true); - databaseView->sortByColumn(0, Qt::AscendingOrder); - databaseView->resizeColumnToContents(0); - connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoLeft(const QModelIndex &, const QModelIndex &))); - connect(databaseView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actAddCard())); - searchEdit->setTreeView(databaseView); - - QVBoxLayout *leftFrame = new QVBoxLayout; - leftFrame->addLayout(searchLayout); - leftFrame->addWidget(databaseView); - - cardInfo = new CardFrame(250, 356); - - filterModel = new FilterTreeModel(); - databaseDisplayModel->setFilterTree(filterModel->filterTree()); - filterView = new QTreeView; - filterView->setModel(filterModel); - filterView->setMaximumWidth(250); - filterView->setUniformRowHeights(true); - filterView->setHeaderHidden(true); - filterView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(filterModel, SIGNAL(layoutChanged()), filterView, SLOT(expandAll())); - connect(filterView, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(filterViewCustomContextMenu(const QPoint &))); - FilterBuilder *filterBuilder = new FilterBuilder; - connect(filterBuilder, SIGNAL(add(const CardFilter *)), filterModel, SLOT(addFilter(const CardFilter *))); - - QToolButton *filterDelOne = new QToolButton(); - filterDelOne->setDefaultAction(aClearFilterOne); - filterDelOne->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - QToolButton *filterDelAll = new QToolButton(); - filterDelAll->setDefaultAction(aClearFilterAll); - filterDelAll->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - QGridLayout *filterLayout = new QGridLayout; - filterLayout->addWidget(filterBuilder, 0, 0, 1, 2); - filterLayout->addWidget(filterView, 1, 0, 1, 2); - filterLayout->addWidget(filterDelOne, 2, 0); - filterLayout->addWidget(filterDelAll, 2, 1); - - filterBox = new QGroupBox(); - filterBox->setMaximumWidth(250); - filterBox->setLayout(filterLayout); - - QVBoxLayout *middleFrame = new QVBoxLayout; - middleFrame->addWidget(cardInfo, 0, Qt::AlignTop); - middleFrame->addWidget(filterBox, 0); + btnCard = new QPushButton(QIcon("theme:back.svg"),QString()); + btnCard->setObjectName("btnCard"); + btnCard->setCheckable(true); + btnCard->setChecked(true); + btnCard->setMaximumWidth(30); + searchLayout->addWidget(btnCard); +} +void TabDeckEditor::createDeckDock() +{ deckModel = new DeckListModel(this); + deckModel->setObjectName("deckModel"); connect(deckModel, SIGNAL(deckHashChanged()), this, SLOT(updateHash())); deckView = new QTreeView(); + deckView->setObjectName("deckView"); deckView->setModel(deckModel); deckView->setUniformRowHeights(true); deckView->setSortingEnabled(true); @@ -170,18 +103,26 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) connect(&deckViewKeySignals, SIGNAL(onDelete()), this, SLOT(actRemoveCard())); nameLabel = new QLabel(); + nameLabel->setObjectName("nameLabel"); nameEdit = new QLineEdit; + nameEdit->setObjectName("nameEdit"); nameLabel->setBuddy(nameEdit); connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateName(const QString &))); commentsLabel = new QLabel(); + commentsLabel->setObjectName("commentsLabel"); commentsEdit = new QTextEdit; + commentsEdit->setObjectName("commentsEdit"); commentsEdit->setMaximumHeight(70); commentsLabel->setBuddy(commentsEdit); connect(commentsEdit, SIGNAL(textChanged()), this, SLOT(updateComments())); + hashLabel1 = new QLabel(); + hashLabel1->setObjectName("hashLabel1"); hashLabel = new QLabel; + hashLabel->setObjectName("hashLabel"); QGridLayout *grid = new QGridLayout; + grid->setObjectName("grid"); grid->addWidget(nameLabel, 0, 0); grid->addWidget(nameEdit, 0, 1); @@ -201,59 +142,161 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) */ QToolBar *deckToolBar = new QToolBar; + deckToolBar->setObjectName("deckToolBar"); deckToolBar->setOrientation(Qt::Vertical); deckToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); deckToolBar->setIconSize(QSize(24, 24)); //deckToolBar->addAction(aUpdatePrices); QHBoxLayout *deckToolbarLayout = new QHBoxLayout; + deckToolbarLayout->setObjectName("deckToolbarLayout"); deckToolbarLayout->addStretch(); deckToolbarLayout->addWidget(deckToolBar); deckToolbarLayout->addStretch(); QVBoxLayout *rightFrame = new QVBoxLayout; + rightFrame->setObjectName("rightFrame"); rightFrame->addLayout(grid); rightFrame->addWidget(deckView, 10); rightFrame->addLayout(deckToolbarLayout); - QHBoxLayout *mainLayout = new QHBoxLayout; - mainLayout->addLayout(leftFrame, 10); - mainLayout->addLayout(middleFrame); - mainLayout->addLayout(rightFrame); - setLayout(mainLayout); + deckDock = new QDockWidget(MainWindow); + deckDock->setObjectName("deckDock"); + deckDock->setMinimumSize(QSize(200, 41)); + deckDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + deckDock->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable); + QWidget *deckDockContents = new QWidget(); + deckDockContents->setObjectName("deckDockContents"); + deckDockContents->setLayout(rightFrame); + deckDock->setWidget(deckDockContents); + + connect(btnDeck,SIGNAL(toggled(bool)),deckDock,SLOT(setVisible(bool))); + deckDock->installEventFilter(this); +} + +void TabDeckEditor::createCardInfoDock() +{ + cardInfo = new CardFrame(); + cardInfo->setObjectName("cardInfo"); + QVBoxLayout *cardInfoFrame = new QVBoxLayout; + cardInfoFrame->setObjectName("cardInfoFrame"); + cardInfoFrame->addWidget(cardInfo); + + cardInfoDock = new QDockWidget(MainWindow); + cardInfoDock->setObjectName("cardInfoDock"); + + cardInfoDock->setMinimumSize(QSize(200, 41)); + cardInfoDock->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea); + cardInfoDock->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable); + QWidget *cardInfoDockContents = new QWidget(); + cardInfoDockContents->setObjectName("cardInfoDockContents"); + cardInfoDockContents->setLayout(cardInfoFrame); + cardInfoDock->setWidget(cardInfoDockContents); + + connect(btnCard,SIGNAL(toggled(bool)),cardInfoDock,SLOT(setVisible(bool))); + cardInfoDock->installEventFilter(this); +} + +void TabDeckEditor::createFiltersDock() +{ + filterModel = new FilterTreeModel(); + filterModel->setObjectName("filterModel"); + databaseDisplayModel->setFilterTree(filterModel->filterTree()); + databaseDisplayModel->setObjectName("databaseDisplayModel"); + filterView = new QTreeView; + filterView->setObjectName("filterView"); + filterView->setModel(filterModel); + filterView->setUniformRowHeights(true); + filterView->setHeaderHidden(true); + filterView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(filterModel, SIGNAL(layoutChanged()), filterView, SLOT(expandAll())); + connect(filterView, SIGNAL(customContextMenuRequested(const QPoint &)),this, SLOT(filterViewCustomContextMenu(const QPoint &))); + + FilterBuilder *filterBuilder = new FilterBuilder; + filterBuilder->setObjectName("filterBuilder"); + connect(filterBuilder, SIGNAL(add(const CardFilter *)), filterModel, SLOT(addFilter(const CardFilter *))); + + QToolButton *filterDelOne = new QToolButton(); + filterDelOne->setObjectName("filterDelOne"); + filterDelOne->setDefaultAction(aClearFilterOne); + filterDelOne->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + QToolButton *filterDelAll = new QToolButton(); + filterDelAll->setObjectName("filterDelAll"); + filterDelAll->setDefaultAction(aClearFilterAll); + filterDelAll->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + QGridLayout *filterLayout = new QGridLayout; + filterLayout->setObjectName("filterLayout"); + filterLayout->setContentsMargins(0,0,0,0); + filterLayout->addWidget(filterBuilder, 0, 0, 1, 2); + filterLayout->addWidget(filterView, 1, 0, 1, 2); + + filterBox = new QWidget(); + filterBox->setObjectName("filterBox"); + filterBox->setLayout(filterLayout); + + QVBoxLayout *filterFrame = new QVBoxLayout; + filterFrame->setObjectName("filterFrame"); + filterFrame->addWidget(filterBox); + + filterDock = new QDockWidget(MainWindow); + filterDock->setObjectName("filterDock"); + + filterDock->setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable); + QWidget *filterDockContents = new QWidget(MainWindow); + filterDockContents->setObjectName("filterDockContents"); + filterDockContents->setLayout(filterFrame); + filterDock->setWidget(filterDockContents); + + connect(btnFilter,SIGNAL(toggled(bool)),filterDock,SLOT(setVisible(bool))); + filterDock->installEventFilter(this); +} + +void TabDeckEditor::createMenus() +{ aNewDeck = new QAction(QString(), this); - aNewDeck->setShortcuts(QKeySequence::New); connect(aNewDeck, SIGNAL(triggered()), this, SLOT(actNewDeck())); + aLoadDeck = new QAction(QString(), this); - aLoadDeck->setShortcuts(QKeySequence::Open); connect(aLoadDeck, SIGNAL(triggered()), this, SLOT(actLoadDeck())); + aSaveDeck = new QAction(QString(), this); - aSaveDeck->setShortcuts(QKeySequence::Save); connect(aSaveDeck, SIGNAL(triggered()), this, SLOT(actSaveDeck())); + aSaveDeckAs = new QAction(QString(), this); -// aSaveDeckAs->setShortcuts(QKeySequence::SaveAs); connect(aSaveDeckAs, SIGNAL(triggered()), this, SLOT(actSaveDeckAs())); + + aOpenCustomsetsFolder = new QAction(QString(), this); + connect(aOpenCustomsetsFolder, SIGNAL(triggered()), this, SLOT(actOpenCustomsetsFolder())); + aLoadDeckFromClipboard = new QAction(QString(), this); connect(aLoadDeckFromClipboard, SIGNAL(triggered()), this, SLOT(actLoadDeckFromClipboard())); - aLoadDeckFromClipboard->setShortcuts(QKeySequence::Paste); + aSaveDeckToClipboard = new QAction(QString(), this); connect(aSaveDeckToClipboard, SIGNAL(triggered()), this, SLOT(actSaveDeckToClipboard())); - aSaveDeckToClipboard->setShortcuts(QKeySequence::Copy); + aPrintDeck = new QAction(QString(), this); - aPrintDeck->setShortcuts(QKeySequence::Print); connect(aPrintDeck, SIGNAL(triggered()), this, SLOT(actPrintDeck())); + aAnalyzeDeck = new QAction(QString(), this); connect(aAnalyzeDeck, SIGNAL(triggered()), this, SLOT(actAnalyzeDeck())); + aClose = new QAction(QString(), this); connect(aClose, SIGNAL(triggered()), this, SLOT(closeRequest())); + aOpenCustomFolder = new QAction(QString(), this); connect(aOpenCustomFolder, SIGNAL(triggered()), this, SLOT(actOpenCustomFolder())); aEditSets = new QAction(QString(), this); connect(aEditSets, SIGNAL(triggered()), this, SLOT(actEditSets())); + aEditTokens = new QAction(QString(), this); connect(aEditTokens, SIGNAL(triggered()), this, SLOT(actEditTokens())); + aResetLayout = new QAction(QString(), this); + connect(aResetLayout,SIGNAL(triggered()),this,SLOT(restartLayout())); + deckMenu = new QMenu(this); deckMenu->addAction(aNewDeck); deckMenu->addAction(aLoadDeck); @@ -267,19 +310,75 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) deckMenu->addSeparator(); deckMenu->addAction(aAnalyzeDeck); deckMenu->addSeparator(); + deckMenu->addAction(aResetLayout); + deckMenu->addSeparator(); deckMenu->addAction(aClose); addTabMenu(deckMenu); + aClearFilterAll = new QAction(QString(), this); + aClearFilterAll->setIcon(QIcon(":/resources/icon_clearsearch.svg")); + connect(aClearFilterAll, SIGNAL(triggered()), this, SLOT(actClearFilterAll())); + + aClearFilterOne = new QAction(QString(), this); + aClearFilterOne->setIcon(QIcon(":/resources/decrement.svg")); + connect(aClearFilterOne, SIGNAL(triggered()), this, SLOT(actClearFilterOne())); + dbMenu = new QMenu(this); dbMenu->addAction(aEditSets); dbMenu->addAction(aEditTokens); dbMenu->addSeparator(); + dbMenu->addAction(aClearFilterOne); dbMenu->addAction(aClearFilterAll); #if defined(Q_OS_WIN) || defined(Q_OS_MAC) dbMenu->addSeparator(); dbMenu->addAction(aOpenCustomFolder); + dbMenu->addAction(aOpenCustomsetsFolder); #endif addTabMenu(dbMenu); +} + +void TabDeckEditor::createCentralFrame() +{ + searchEdit = new SearchLineEdit; + searchEdit->setObjectName("searchEdit"); +#if QT_VERSION >= 0x050300 + searchEdit->addAction(QIcon(":/resources/icon_search_black.svg"), QLineEdit::LeadingPosition); +#endif + + setFocusProxy(searchEdit); + setFocusPolicy(Qt::ClickFocus); + + searchEdit->installEventFilter(&searchKeySignals); + searchKeySignals.setObjectName("searchKeySignals"); + connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &))); + connect(&searchKeySignals, SIGNAL(onEnter()), this, SLOT(actAddCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actAddCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltRBracket()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrementCard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltLBracket()), this, SLOT(actDecrementCardFromSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlAltEnter()), this, SLOT(actAddCardToSideboard())); + connect(&searchKeySignals, SIGNAL(onCtrlEnter()), this, SLOT(actAddCardToSideboard())); + + databaseModel = new CardDatabaseModel(db, this); + databaseModel->setObjectName("databaseModel"); + databaseDisplayModel = new CardDatabaseDisplayModel(this); + databaseDisplayModel->setSourceModel(databaseModel); + databaseDisplayModel->setFilterKeyColumn(0); + databaseDisplayModel->sort(0, Qt::AscendingOrder); + + databaseView = new QTreeView(); + databaseView->setObjectName("databaseView"); + databaseView->setFocusProxy(searchEdit); + databaseView->setModel(databaseDisplayModel); + databaseView->setUniformRowHeights(true); + databaseView->setRootIsDecorated(false); + databaseView->setAlternatingRowColors(true); + databaseView->setSortingEnabled(true); + databaseView->sortByColumn(0, Qt::AscendingOrder); + databaseView->resizeColumnToContents(0); + connect(databaseView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoLeft(const QModelIndex &, const QModelIndex &))); + connect(databaseView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actAddCard())); + searchEdit->setTreeView(databaseView); aAddCard = new QAction(QString(), this); aAddCard->setIcon(QIcon("theme:icons/arrow_right_green.svg")); @@ -297,6 +396,11 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) aDecrement->setIcon(QIcon("theme:icons/decrement.svg")); connect(aDecrement, SIGNAL(triggered()), this, SLOT(actDecrement())); + QToolBar *deckEditToolBar = new QToolBar; + deckEditToolBar->setObjectName("deckEditToolBar"); + deckEditToolBar->setOrientation(Qt::Horizontal); + deckEditToolBar->setIconSize(QSize(24, 24)); + deckEditToolBar->addAction(aAddCard); deckEditToolBar->addAction(aAddCardToSideboard); deckEditToolBar->addAction(aRemoveCard); @@ -304,11 +408,135 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) deckEditToolBar->addAction(aIncrement); deckEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + searchLayout = new QHBoxLayout; + searchLayout->setObjectName("searchLayout"); + searchLayout->addWidget(deckEditToolBar); + searchLayout->addWidget(searchEdit); + createShowHideDocksButtons(); + + centralFrame = new QVBoxLayout; + centralFrame->setObjectName("centralFrame"); + centralFrame->addLayout(searchLayout); + centralFrame->addWidget(databaseView); + + centralWidget = new QWidget(MainWindow); + centralWidget->setObjectName("centralWidget"); + centralWidget->setLayout(centralFrame); + MainWindow->setCentralWidget(centralWidget); + MainWindow->setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks); + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->setObjectName("mainLayout"); + mainLayout->addWidget(MainWindow); + setLayout(mainLayout); +} + +void TabDeckEditor::restartLayout() +{ + btnDeck->setChecked(true); + btnFilter->setChecked(true); + btnCard->setChecked(true); + + deckDock->setFloating(false); + cardInfoDock->setFloating(false); + filterDock->setFloating(false); + + MainWindow->addDockWidget(static_cast(2), deckDock); + MainWindow->addDockWidget(static_cast(2), cardInfoDock); + MainWindow->addDockWidget(static_cast(2), filterDock); + + MainWindow->splitDockWidget(cardInfoDock, deckDock, Qt::Horizontal); + MainWindow->splitDockWidget(cardInfoDock, filterDock, Qt::Vertical); + + deckDock->setMinimumWidth(360); + deckDock->setMaximumWidth(360); + + cardInfoDock->setMinimumSize(250, 360); + cardInfoDock->setMaximumSize(250, 360); + QTimer::singleShot(100, this, SLOT(freeDocksSize())); +} + +void TabDeckEditor::freeDocksSize() +{ + deckDock->setMinimumSize(100, 100); + deckDock->setMaximumSize(5000, 5000); + + cardInfoDock->setMinimumSize(100, 100); + cardInfoDock->setMaximumSize(5000, 5000); + + filterDock->setMinimumSize(100,100); + filterDock->setMaximumSize(5000,5000); +} + +void TabDeckEditor::refreshShortcuts() +{ + aNewDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aNewDeck")); + aLoadDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aLoadDeck")); + aSaveDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aSaveDeck")); + aSaveDeckAs->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aSaveDeckAs")); + aLoadDeckFromClipboard->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aLoadDeckFromClipboard")); + aPrintDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aPrintDeck")); + aAnalyzeDeck->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aAnalyzeDeck")); + aClose->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aClose")); + aOpenCustomFolder->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aOpenCustomFolder")); + aEditSets->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aEditSets")); + aEditTokens->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aEditTokens")); + aResetLayout->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aResetLayout")); + aClearFilterAll->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aClearFilterAll")); + aClearFilterOne->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aClearFilterOne")); + aSaveDeckToClipboard->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aSaveDeckToClipboard")); + aClearFilterOne->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aClearFilterOne")); + aClose->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aClose")); + aRemoveCard->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aRemoveCard")); + aIncrement->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aIncrement")); + aDecrement->setShortcuts(settingsCache->shortcuts().getShortcut("TabDeckEditor/aDecrement")); +} + +void TabDeckEditor::loadLayout() +{ + MainWindow->restoreState(settingsCache->getDeckEditorLayoutState()); + MainWindow->restoreGeometry(settingsCache->getDeckEditorGeometry()); + + btnCard->setChecked(!cardInfoDock->isHidden()); + btnFilter->setChecked(!filterDock->isHidden()); + btnDeck->setChecked(!deckDock->isHidden()); + + cardInfoDock->setMinimumSize(settingsCache->getDeckEditorCardSize()); + cardInfoDock->setMaximumSize(settingsCache->getDeckEditorCardSize()); + + filterDock->setMinimumSize(settingsCache->getDeckEditorFilterSize()); + filterDock->setMaximumSize(settingsCache->getDeckEditorFilterSize()); + + deckDock->setMinimumSize(settingsCache->getDeckEditorDeckSize()); + deckDock->setMaximumSize(settingsCache->getDeckEditorDeckSize()); + + QTimer::singleShot(100, this, SLOT(freeDocksSize())); +} + +TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent) + : Tab(_tabSupervisor, parent), modified(false) +{ + MainWindow = new QMainWindow; + MainWindow->setObjectName("MainWindow"); + + createMenus(); + + createCentralFrame(); + + createDeckDock(); + createCardInfoDock(); + createFiltersDock(); + + restartLayout(); + + this->installEventFilter(this); + retranslateUi(); - - resize(950, 700); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); QTimer::singleShot(0, this, SLOT(checkFirstRunDetected())); + QTimer::singleShot(0, this, SLOT(loadLayout())); } TabDeckEditor::~TabDeckEditor() @@ -320,7 +548,6 @@ void TabDeckEditor::retranslateUi() { cardInfo->retranslateUi(); - filterBox->setTitle(tr("Filters")); aClearFilterAll->setText(tr("&Clear all filters")); aClearFilterOne->setText(tr("Delete selected")); @@ -340,24 +567,31 @@ void TabDeckEditor::retranslateUi() aPrintDeck->setText(tr("&Print deck...")); aAnalyzeDeck->setText(tr("&Analyze deck on deckstats.net")); aOpenCustomFolder->setText(tr("Open custom image folder")); + aOpenCustomsetsFolder->setText(tr("Open custom sets folder")); aClose->setText(tr("&Close")); - aClose->setShortcut(QKeySequence("Ctrl+Q")); aAddCard->setText(tr("Add card to &maindeck")); aAddCardToSideboard->setText(tr("Add card to &sideboard")); aRemoveCard->setText(tr("&Remove row")); - aRemoveCard->setShortcut(QKeySequence("Del")); + aIncrement->setText(tr("&Increment number")); - aIncrement->setShortcut(QKeySequence("+")); + aDecrement->setText(tr("&Decrement number")); - aDecrement->setShortcut(QKeySequence("-")); deckMenu->setTitle(tr("&Deck Editor")); dbMenu->setTitle(tr("C&ard Database")); aEditSets->setText(tr("&Edit sets...")); aEditTokens->setText(tr("Edit &tokens...")); + + btnCard->setToolTip(tr("Show/Hide card information")); + btnDeck->setToolTip(tr("Show/Hide deck")); + btnFilter->setToolTip(tr("Show/Hide filters")); + aResetLayout->setText(tr("Reset layout")); + cardInfoDock->setWindowTitle(tr("Card Info")); + deckDock->setWindowTitle(tr("Deck")); + filterDock->setWindowTitle(tr("Filters")); } QString TabDeckEditor::getTabText() const @@ -574,6 +808,33 @@ void TabDeckEditor::actOpenCustomFolder() { } +void TabDeckEditor::actOpenCustomsetsFolder() { +#if QT_VERSION < 0x050000 + QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); +#else + QString dataDir = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first(); +#endif + +#if defined(Q_OS_MAC) + + QStringList scriptArgs; + scriptArgs << QLatin1String("-e"); + scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dataDir + "/customsets/"); + scriptArgs << QLatin1String("-e"); + scriptArgs << QLatin1String("tell application \"Finder\" to activate"); + + QProcess::execute("/usr/bin/osascript", scriptArgs); +#endif +#if defined(Q_OS_WIN) + QStringList args; + dataDir.append("/customsets"); + args << QDir::toNativeSeparators(dataDir); + aOpenCustomsetsFolder->setText(dataDir); + QProcess::startDetached("explorer", args); +#endif + +} + void TabDeckEditor::actEditSets() { WndSets *w = new WndSets; @@ -657,7 +918,10 @@ void TabDeckEditor::actSwapCard() void TabDeckEditor::actAddCard() { - addCardHelper("main"); + if(QApplication::keyboardModifiers() & Qt::ControlModifier) + actAddCardToSideboard(); + else + addCardHelper("main"); } void TabDeckEditor::actAddCardToSideboard() @@ -733,6 +997,27 @@ void TabDeckEditor::setPriceTagFeatureEnabled(int /* enabled */) deckModel->pricesUpdated(); } +bool TabDeckEditor::eventFilter(QObject * o, QEvent * e) +{ + if(e->type() == QEvent::Close) + { + if(o == cardInfoDock) + btnCard->setChecked(false); + else if(o == deckDock) + btnDeck->setChecked(false); + else if(o == filterDock) + btnFilter->setChecked(false); + } + if( o == this && e->type() == QEvent::Hide){ + settingsCache->setDeckEditorLayoutState(MainWindow->saveState()); + settingsCache->setDeckEditorGeometry(MainWindow->saveGeometry()); + settingsCache->setDeckEditorCardSize(cardInfoDock->size()); + settingsCache->setDeckEditorFilterSize(filterDock->size()); + settingsCache->setDeckEditorDeckSize(deckDock->size()); + } + return false; +} + /* void TabDeckEditor::actUpdatePrices() @@ -818,4 +1103,4 @@ void TabDeckEditor::checkFirstRunDetected() QMessageBox::information(this, tr("Welcome"), tr("Hi! It seems like you're running this version of Cockatrice for the first time.\nAll the sets in the card database have been enabled.\nRead more about changing the set order or disabling specific sets and consequent effects in the \"Edit Sets\" window.")); actEditSets(); } -} \ No newline at end of file +} diff --git a/cockatrice/src/tab_deck_editor.h b/cockatrice/src/tab_deck_editor.h index 5f91d4c3..996e3ec8 100644 --- a/cockatrice/src/tab_deck_editor.h +++ b/cockatrice/src/tab_deck_editor.h @@ -20,6 +20,10 @@ class FilterTreeModel; class FilterBuilder; class CardInfo; class QGroupBox; +class QHBoxLayout; +class QPushButton; +class QMainWindow; +class QDockWidget; class SearchLineEdit : public QLineEdit { private: @@ -50,6 +54,7 @@ class TabDeckEditor : public Tab { void actPrintDeck(); void actAnalyzeDeck(); void actOpenCustomFolder(); + void actOpenCustomsetsFolder(); void actEditSets(); void actEditTokens(); @@ -73,6 +78,13 @@ class TabDeckEditor : public Tab { void filterViewCustomContextMenu(const QPoint &point); void filterRemove(QAction *action); void setPriceTagFeatureEnabled(int enabled); + + bool eventFilter(QObject *o, QEvent *e); + void loadLayout(); + void restartLayout(); + void freeDocksSize(); + void refreshShortcuts(); + private: CardInfo *currentCardInfo() const; void addCardHelper(QString zoneName); @@ -99,14 +111,25 @@ private: QLabel *hashLabel; FilterTreeModel *filterModel; QTreeView *filterView; - QGroupBox *filterBox; + QWidget *filterBox; QMenu *deckMenu, *dbMenu; - QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder; + QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder, *aOpenCustomsetsFolder; QAction *aEditSets, *aEditTokens, *aClearFilterAll, *aClearFilterOne; QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;// *aUpdatePrices; + QAction *aResetLayout; bool modified; + QMainWindow *MainWindow; + QVBoxLayout *centralFrame; + QHBoxLayout *searchLayout; + QPushButton *btnFilter; + QPushButton *btnDeck; + QPushButton *btnCard; + QDockWidget *cardInfoDock; + QDockWidget *deckDock; + QDockWidget *filterDock; + QWidget *centralWidget; public: TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent = 0); ~TabDeckEditor(); @@ -115,6 +138,13 @@ public: void setDeck(DeckLoader *_deckLoader); void setModified(bool _windowModified); bool confirmClose(); + void createShowHideDocksButtons(); + void createDeckDock(); + void createCardInfoDock(); + void createFiltersDock(); + void createMenus(); + void createCentralFrame(); + public slots: void closeRequest(); void checkFirstRunDetected(); diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 517e6d85..36a20ff9 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -8,11 +8,13 @@ #include #include #include +#include +#include #include "dlg_creategame.h" #include "tab_game.h" #include "tab_supervisor.h" -#include "cardinfowidget.h" +#include "cardframe.h" #include "playerlistwidget.h" #include "messagelogwidget.h" #include "phasestoolbar.h" @@ -31,6 +33,7 @@ #include "settingscache.h" #include "carddatabase.h" #include "replay_timeline_widget.h" +#include "lineeditcompleter.h" #include #include "pending_command.h" @@ -120,12 +123,14 @@ DeckViewContainer::DeckViewContainer(int _playerId, TabGame *parent) setLayout(deckViewLayout); retranslateUi(); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); } void DeckViewContainer::retranslateUi() { - loadLocalButton->setText(tr("Load &local deck")); - loadRemoteButton->setText(tr("Load d&eck from server")); + loadLocalButton->setText(tr("Load local deck")); + loadRemoteButton->setText(tr("Load deck from server")); readyStartButton->setText(tr("Ready to s&tart")); updateSideboardLockButtonText(); } @@ -146,6 +151,58 @@ void DeckViewContainer::updateSideboardLockButtonText() sideboardLockButton->setText(tr("S&ideboard locked")); } +void DeckViewContainer::refreshShortcuts() +{ + loadLocalButton->setShortcut(settingsCache->shortcuts().getSingleShortcut("DeckViewContainer/loadLocalButton")); + loadRemoteButton->setShortcut(settingsCache->shortcuts().getSingleShortcut("DeckViewContainer/loadRemoteButton")); +} + +void TabGame::refreshShortcuts() +{ + for (int i = 0; i < phaseActions.size(); ++i) { + QAction *temp = phaseActions.at(i); + switch (i) { + case 0: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase0")); break; + case 1: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase1")); break; + case 2: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase2")); break; + case 3: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase3")); break; + case 4: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase4")); break; + case 5: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase5")); break; + case 6: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase6")); break; + case 7: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase7")); break; + case 8: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase8")); break; + case 9: temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase9")); break; + case 10:temp->setShortcuts(settingsCache->shortcuts().getShortcut("Player/phase10")); break; + default: ; + } + } + + if (aNextPhase) { + aNextPhase->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aNextPhase")); + } + if (aNextTurn) { + aNextTurn->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aNextTurn")); + } + if (aRemoveLocalArrows) { + aRemoveLocalArrows->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aRemoveLocalArrows")); + } + if (aRotateViewCW) { + aRotateViewCW->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aRotateViewCW")); + } + if (aRotateViewCCW) { + aRotateViewCCW->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aRotateViewCCW")); + } + if (aConcede) { + aConcede->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aConcede")); + } + if (aLeaveGame) { + aLeaveGame->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aLeaveGame")); + } + if (aCloseReplay) { + aCloseReplay->setShortcuts(settingsCache->shortcuts().getShortcut("Player/aCloseReplay")); + } +} + void DeckViewContainer::loadLocalDeck() { QFileDialog dialog(this, tr("Load deck")); @@ -280,7 +337,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) gameView = new GameView(scene); gameView->hide(); - cardInfo = new CardInfoWidget(CardInfoWidget::ModeGameTab); + cardInfo = new CardFrame(); playerListWidget = new PlayerListWidget(0, 0, this); playerListWidget->setFocusPolicy(Qt::NoFocus); @@ -293,6 +350,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) deckViewContainerLayout = new QVBoxLayout; QVBoxLayout *messageLogLayout = new QVBoxLayout; + messageLogLayout->setContentsMargins(0, 0, 0, 0); messageLogLayout->addWidget(messageLog); QWidget *messageLogLayoutWidget = new QWidget; @@ -342,6 +400,8 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) aNextPhase = 0; aNextTurn = 0; aRemoveLocalArrows = 0; + aRotateViewCW = 0; + aRotateViewCCW = 0; aGameInfo = 0; aConcede = 0; aLeaveGame = 0; @@ -354,10 +414,13 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, GameReplay *_replay) addTabMenu(gameMenu); retranslateUi(); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); setLayout(superMainLayout); splitter->restoreState(settingsCache->getTabGameSplitterSizes()); - + splitter->setChildrenCollapsible(false); + messageLog->logReplayStarted(gameInfo.game_id()); } @@ -390,7 +453,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client gameView = new GameView(scene); gameView->hide(); - cardInfo = new CardInfoWidget(CardInfoWidget::ModeGameTab); + cardInfo = new CardFrame(); playerListWidget = new PlayerListWidget(tabSupervisor, clients.first(), this); playerListWidget->setFocusPolicy(Qt::NoFocus); connect(playerListWidget, SIGNAL(openMessageDialog(QString, bool)), this, SIGNAL(openMessageDialog(QString, bool))); @@ -403,8 +466,9 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client connect(messageLog, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(messageLog, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString))); connect(messageLog, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString))); + connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged())); sayLabel = new QLabel; - sayEdit = new QLineEdit; + sayEdit = new LineEditCompleter; sayLabel->setBuddy(sayEdit); QHBoxLayout *hLayout = new QHBoxLayout; @@ -414,6 +478,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client deckViewContainerLayout = new QVBoxLayout; QVBoxLayout *messageLogLayout = new QVBoxLayout; + messageLogLayout->setContentsMargins(0, 0, 0, 0); messageLogLayout->addWidget(timeElapsedLabel); messageLogLayout->addWidget(messageLog); messageLogLayout->addLayout(hLayout); @@ -445,6 +510,10 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client connect(aNextTurn, SIGNAL(triggered()), this, SLOT(actNextTurn())); aRemoveLocalArrows = new QAction(this); connect(aRemoveLocalArrows, SIGNAL(triggered()), this, SLOT(actRemoveLocalArrows())); + aRotateViewCW = new QAction(this); + connect(aRotateViewCW, SIGNAL(triggered()), this, SLOT(actRotateViewCW())); + aRotateViewCCW = new QAction(this); + connect(aRotateViewCCW, SIGNAL(triggered()), this, SLOT(actRotateViewCCW())); aGameInfo = new QAction(this); connect(aGameInfo, SIGNAL(triggered()), this, SLOT(actGameInfo())); aConcede = new QAction(this); @@ -457,18 +526,10 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client for (int i = 0; i < phasesToolbar->phaseCount(); ++i) { QAction *temp = new QAction(QString(), this); connect(temp, SIGNAL(triggered()), this, SLOT(actPhaseAction())); - switch (i) { - case 0: temp->setShortcut(QKeySequence("F5")); break; - case 2: temp->setShortcut(QKeySequence("F6")); break; - case 3: temp->setShortcut(QKeySequence("F7")); break; - case 4: temp->setShortcut(QKeySequence("F8")); break; - case 9: temp->setShortcut(QKeySequence("F9")); break; - case 10: temp->setShortcut(QKeySequence("F10")); break; - default: ; - } phasesMenu->addAction(temp); phaseActions.append(temp); } + phasesMenu->addSeparator(); phasesMenu->addAction(aNextPhase); @@ -478,6 +539,8 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client gameMenu->addAction(aNextTurn); gameMenu->addSeparator(); gameMenu->addAction(aRemoveLocalArrows); + gameMenu->addAction(aRotateViewCW); + gameMenu->addAction(aRotateViewCCW); gameMenu->addSeparator(); gameMenu->addAction(aGameInfo); gameMenu->addAction(aConcede); @@ -485,14 +548,28 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client addTabMenu(gameMenu); retranslateUi(); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); setLayout(mainLayout); splitter->restoreState(settingsCache->getTabGameSplitterSizes()); + splitter->setChildrenCollapsible(false); messageLog->logGameJoined(gameInfo.game_id()); for (int i = gameInfo.game_types_size() - 1; i >= 0; i--) gameTypes.append(roomGameTypes.find(gameInfo.game_types(i)).value()); + + completer = new QCompleter(autocompleteUserList, sayEdit); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setMaxVisibleItems(5); + + #if QT_VERSION >= 0x050000 + completer->setFilterMode(Qt::MatchStartsWith); + #endif + + sayEdit->setCompleter(completer); + actCompleterChanged(); } void TabGame::addMentionTag(QString value) { @@ -531,29 +608,29 @@ void TabGame::retranslateUi() gameMenu->setTitle(tr("&Game")); if (aNextPhase) { aNextPhase->setText(tr("Next &phase")); - aNextPhase->setShortcuts(QList() << QKeySequence("Ctrl+Space") << QKeySequence("Tab")); } if (aNextTurn) { aNextTurn->setText(tr("Next &turn")); - aNextTurn->setShortcuts(QList() << QKeySequence("Ctrl+Return") << QKeySequence("Ctrl+Enter")); } if (aRemoveLocalArrows) { aRemoveLocalArrows->setText(tr("&Remove all local arrows")); - aRemoveLocalArrows->setShortcut(QKeySequence("Ctrl+R")); + } + if (aRotateViewCW) { + aRotateViewCW->setText(tr("Rotate View Cl&ockwise")); + } + if (aRotateViewCCW) { + aRotateViewCCW->setText(tr("Rotate View Co&unterclockwise")); } if (aGameInfo) aGameInfo->setText(tr("Game &information")); if (aConcede) { aConcede->setText(tr("&Concede")); - aConcede->setShortcut(QKeySequence("F2")); } if (aLeaveGame) { aLeaveGame->setText(tr("&Leave game")); - aLeaveGame->setShortcut(QKeySequence("Ctrl+Q")); } if (aCloseReplay) { aCloseReplay->setText(tr("C&lose replay")); - aCloseReplay->setShortcut(QKeySequence("Ctrl+Q")); } if (sayLabel) @@ -659,6 +736,9 @@ void TabGame::actLeaveGame() void TabGame::actSay() { + if (completer->popup()->isVisible()) + return; + if (!sayEdit->text().isEmpty()) { Command_GameSay cmd; cmd.set_message(sayEdit->text().toStdString()); @@ -707,11 +787,31 @@ void TabGame::actRemoveLocalArrows() } } +void TabGame::actRotateViewCW() +{ + scene->adjustPlayerRotation(-1); +} + +void TabGame::actRotateViewCCW() +{ + scene->adjustPlayerRotation(1); +} + +void TabGame::actCompleterChanged() +{ + settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1); +} + Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info) { bool local = ((clients.size() > 1) || (playerId == localPlayerId)); Player *newPlayer = new Player(info, playerId, local, this); connect(newPlayer, SIGNAL(openDeckEditor(const DeckLoader *)), this, SIGNAL(openDeckEditor(const DeckLoader *))); + QString newPlayerName = "@" + newPlayer->getName(); + if (!autocompleteUserList.contains(newPlayerName)){ + autocompleteUserList << newPlayerName; + sayEdit->setCompletionList(autocompleteUserList); + } scene->addPlayer(newPlayer); connect(newPlayer, SIGNAL(newCardAdded(AbstractCardItem *)), this, SLOT(newCardAdded(AbstractCardItem *))); @@ -731,7 +831,6 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info) players.insert(playerId, newPlayer); emit playerAdded(newPlayer); - return newPlayer; } @@ -906,6 +1005,9 @@ void TabGame::eventSpectatorSay(const Event_GameSay &event, int eventPlayerId, c void TabGame::eventSpectatorLeave(const Event_Leave & /*event*/, int eventPlayerId, const GameEventContext & /*context*/) { + QString playerName = "@" + QString::fromStdString(spectators.value(eventPlayerId).name()); + if (autocompleteUserList.removeOne(playerName)) + sayEdit->setCompletionList(autocompleteUserList); messageLog->logLeaveSpectator(QString::fromStdString(spectators.value(eventPlayerId).name())); playerListWidget->removePlayer(eventPlayerId); spectators.remove(eventPlayerId); @@ -920,6 +1022,11 @@ void TabGame::eventGameStateChanged(const Event_GameStateChanged &event, int /*e const ServerInfo_Player &playerInfo = event.player_list(i); const ServerInfo_PlayerProperties &prop = playerInfo.properties(); const int playerId = prop.player_id(); + QString playerName = "@" + QString::fromStdString(prop.user_info().name()); + if (!autocompleteUserList.contains(playerName)){ + autocompleteUserList << playerName; + sayEdit->setCompletionList(autocompleteUserList); + } if (prop.spectator()) { if (!spectators.contains(playerId)) { spectators.insert(playerId, prop.user_info()); @@ -1027,11 +1134,18 @@ void TabGame::eventJoin(const Event_Join &event, int /*eventPlayerId*/, const Ga { const ServerInfo_PlayerProperties &playerInfo = event.player_properties(); const int playerId = playerInfo.player_id(); + QString playerName = QString::fromStdString(playerInfo.user_info().name()); + if (!autocompleteUserList.contains("@" + playerName)){ + autocompleteUserList << "@" + playerName; + sayEdit->setCompletionList(autocompleteUserList); + } + if (players.contains(playerId)) return; + if (playerInfo.spectator()) { spectators.insert(playerId, playerInfo.user_info()); - messageLog->logJoinSpectator(QString::fromStdString(playerInfo.user_info().name())); + messageLog->logJoinSpectator(playerName); } else { Player *newPlayer = addPlayer(playerId, playerInfo.user_info()); messageLog->logJoin(newPlayer); @@ -1046,6 +1160,10 @@ void TabGame::eventLeave(const Event_Leave & /*event*/, int eventPlayerId, const if (!player) return; + QString playerName = "@" + player->getName(); + if(autocompleteUserList.removeOne(playerName)) + sayEdit->setCompletionList(autocompleteUserList); + messageLog->logLeave(player); playerListWidget->removePlayer(eventPlayerId); players.remove(eventPlayerId); diff --git a/cockatrice/src/tab_game.h b/cockatrice/src/tab_game.h index ae2ac28f..5b13ffdd 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -3,6 +3,7 @@ #include #include +#include #include "tab.h" #include "pb/serverinfo_game.pb.h" @@ -11,7 +12,7 @@ class CardDatabase; class GameView; class DeckView; class GameScene; -class CardInfoWidget; +class CardFrame; class MessageLogWidget; class QTimer; class QSplitter; @@ -54,6 +55,7 @@ class QHBoxLayout; class GameReplay; class ServerInfo_User; class PendingCommand; +class LineEditCompleter; class ToggleButton : public QPushButton { Q_OBJECT @@ -84,6 +86,7 @@ private slots: void sideboardPlanChanged(); void sideboardLockButtonClicked(); void updateSideboardLockButtonText(); + void refreshShortcuts(); signals: void newCardAdded(AbstractCardItem *card); public: @@ -116,6 +119,8 @@ private: CardItem *activeCard; bool gameClosed; QStringList gameTypes; + QCompleter *completer; + QStringList autocompleteUserList; // Replay related members GameReplay *replay; @@ -125,12 +130,12 @@ private: QToolButton *replayStartButton, *replayPauseButton, *replayFastForwardButton; QSplitter *splitter; - CardInfoWidget *cardInfo; + CardFrame *cardInfo; PlayerListWidget *playerListWidget; QLabel *timeElapsedLabel; MessageLogWidget *messageLog; QLabel *sayLabel; - QLineEdit *sayEdit; + LineEditCompleter *sayEdit; PhasesToolbar *phasesToolbar; GameScene *scene; GameView *gameView; @@ -141,7 +146,7 @@ private: QAction *playersSeparator; QMenu *gameMenu; QMenu *phasesMenu; - QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextTurn, *aRemoveLocalArrows; + QAction *aGameInfo, *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextTurn, *aRemoveLocalArrows, *aRotateViewCW, *aRotateViewCCW; QList phaseActions; Player *addPlayer(int playerId, const ServerInfo_User &info); @@ -190,6 +195,8 @@ private slots: void actConcede(); void actLeaveGame(); void actRemoveLocalArrows(); + void actRotateViewCW(); + void actRotateViewCCW(); void actSay(); void actPhaseAction(); void actNextPhase(); @@ -197,6 +204,10 @@ private slots: void addMentionTag(QString value); void commandFinished(const Response &response); + + void refreshShortcuts(); + + void actCompleterChanged(); public: TabGame(TabSupervisor *_tabSupervisor, QList &_clients, const Event_GameJoined &event, const QMap &_roomGameTypes); TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay); diff --git a/cockatrice/src/tab_replays.cpp b/cockatrice/src/tab_replays.cpp index d6d36077..3947ca08 100644 --- a/cockatrice/src/tab_replays.cpp +++ b/cockatrice/src/tab_replays.cpp @@ -80,12 +80,14 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) aOpenLocalReplay = new QAction(this); aOpenLocalReplay->setIcon(QIcon("theme:icons/view.svg")); connect(aOpenLocalReplay, SIGNAL(triggered()), this, SLOT(actOpenLocalReplay())); + connect(localDirView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actOpenLocalReplay())); aDeleteLocalReplay = new QAction(this); aDeleteLocalReplay->setIcon(QIcon("theme:icons/remove_row.svg")); connect(aDeleteLocalReplay, SIGNAL(triggered()), this, SLOT(actDeleteLocalReplay())); aOpenRemoteReplay = new QAction(this); aOpenRemoteReplay->setIcon(QIcon("theme:icons/view.svg")); connect(aOpenRemoteReplay, SIGNAL(triggered()), this, SLOT(actOpenRemoteReplay())); + connect(serverDirView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(actOpenRemoteReplay())); aDownload = new QAction(this); aDownload->setIcon(QIcon("theme:icons/arrow_left_green.svg")); connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload())); diff --git a/cockatrice/src/tab_room.cpp b/cockatrice/src/tab_room.cpp index f41ba307..e7e37171 100644 --- a/cockatrice/src/tab_room.cpp +++ b/cockatrice/src/tab_room.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "tab_supervisor.h" #include "tab_room.h" #include "tab_userlists.h" @@ -20,6 +22,7 @@ #include "gameselector.h" #include "settingscache.h" #include "main.h" +#include "lineeditcompleter.h" #include "get_pb_extension.h" #include "pb/room_commands.pb.h" @@ -51,15 +54,15 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString))); connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString))); connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString))); + connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged())); sayLabel = new QLabel; - sayEdit = new QLineEdit; + sayEdit = new LineEditCompleter; sayLabel->setBuddy(sayEdit); connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage())); QMenu *chatSettingsMenu = new QMenu(this); aClearChat = chatSettingsMenu->addAction(QString()); - aClearChat->setShortcut(QKeySequence("F12")); connect(aClearChat, SIGNAL(triggered()), this, SLOT(actClearChat())); chatSettingsMenu->addSeparator(); @@ -103,13 +106,28 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI setLayout(hbox); const int userListSize = info.user_list_size(); - for (int i = 0; i < userListSize; ++i) + for (int i = 0; i < userListSize; ++i){ userList->processUserInfo(info.user_list(i), true); + autocompleteUserList.append("@" + QString::fromStdString(info.user_list(i).name())); + } userList->sortItems(); const int gameListSize = info.game_list_size(); for (int i = 0; i < gameListSize; ++i) gameSelector->processGameInfo(info.game_list(i)); + + completer = new QCompleter(autocompleteUserList, sayEdit); + completer->setCaseSensitivity(Qt::CaseInsensitive); + completer->setMaxVisibleItems(5); + + #if QT_VERSION >= 0x050000 + completer->setFilterMode(Qt::MatchStartsWith); + #endif + + sayEdit->setCompleter(completer); + actCompleterChanged(); + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); } TabRoom::~TabRoom() @@ -119,7 +137,7 @@ TabRoom::~TabRoom() void TabRoom::retranslateUi() { - gameSelector->retranslateUi(); + gameSelector->retranslateUi(); chatView->retranslateUi(); userList->retranslateUi(); sayLabel->setText(tr("&Say:")); @@ -166,16 +184,20 @@ QString TabRoom::sanitizeHtml(QString dirty) const void TabRoom::sendMessage() { - if (sayEdit->text().isEmpty()) - return; + if (sayEdit->text().isEmpty()){ + return; + }else if (completer->popup()->isVisible()){ + completer->popup()->hide(); + return; + }else{ + Command_RoomSay cmd; + cmd.set_message(sayEdit->text().toStdString()); - Command_RoomSay cmd; - cmd.set_message(sayEdit->text().toStdString()); - - PendingCommand *pend = prepareRoomCommand(cmd); - connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &))); - sendRoomCommand(pend); - sayEdit->clear(); + PendingCommand *pend = prepareRoomCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &))); + sendRoomCommand(pend); + sayEdit->clear(); + } } void TabRoom::sayFinished(const Response &response) @@ -200,6 +222,11 @@ void TabRoom::actOpenChatSettings() { settings.exec(); } +void TabRoom::actCompleterChanged() +{ + settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1); +} + void TabRoom::processRoomEvent(const RoomEvent &event) { switch (static_cast(getPbExtension(event))) { @@ -222,11 +249,17 @@ void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event) { userList->processUserInfo(event.user_info(), true); userList->sortItems(); + if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))){ + autocompleteUserList << "@" + QString::fromStdString(event.user_info().name()); + sayEdit->setCompletionList(autocompleteUserList); + } } void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event) { userList->deleteUser(QString::fromStdString(event.name())); + autocompleteUserList.removeOne("@" + QString::fromStdString(event.name())); + sayEdit->setCompletionList(autocompleteUserList); } void TabRoom::processRoomSayEvent(const Event_RoomSay &event) @@ -245,6 +278,11 @@ void TabRoom::processRoomSayEvent(const Event_RoomSay &event) emit userEvent(false); } +void TabRoom::refreshShortcuts() +{ + aClearChat->setShortcuts(settingsCache->shortcuts().getShortcut("tab_room/aClearChat")); +} + void TabRoom::addMentionTag(QString mentionTag) { sayEdit->insert(mentionTag + " "); sayEdit->setFocus(); @@ -258,4 +296,4 @@ PendingCommand *TabRoom::prepareRoomCommand(const ::google::protobuf::Message &c void TabRoom::sendRoomCommand(PendingCommand *pend) { client->sendCommand(pend); -} +} \ No newline at end of file diff --git a/cockatrice/src/tab_room.h b/cockatrice/src/tab_room.h index 377f357f..41b26b75 100644 --- a/cockatrice/src/tab_room.h +++ b/cockatrice/src/tab_room.h @@ -2,8 +2,12 @@ #define TAB_ROOM_H #include "tab.h" +#include "lineeditcompleter.h" #include #include +#include +#include +#include namespace google { namespace protobuf { class Message; } } class AbstractClient; @@ -13,6 +17,7 @@ class ChatView; class QLineEdit; class QPushButton; class QTextTable; +class QCompleter; class RoomEvent; class ServerInfo_Room; class ServerInfo_Game; @@ -24,6 +29,7 @@ class GameSelector; class Response; class PendingCommand; class ServerInfo_User; +class LineEditCompleter; class TabRoom : public Tab { Q_OBJECT @@ -38,14 +44,17 @@ private: UserList *userList; ChatView *chatView; QLabel *sayLabel; - QLineEdit *sayEdit; + LineEditCompleter *sayEdit; QGroupBox *chatGroupBox; QMenu *roomMenu; QAction *aLeaveRoom; QAction *aOpenChatSettings; - QAction * aClearChat; + QAction *aClearChat; QString sanitizeHtml(QString dirty) const; + + QStringList autocompleteUserList; + QCompleter *completer; signals: void roomClosing(TabRoom *tab); void openMessageDialog(const QString &userName, bool focus); @@ -59,11 +68,13 @@ private slots: void addMentionTag(QString mentionTag); void focusTab(); void actShowMentionPopup(QString &sender); - + void actCompleterChanged(); + void processListGamesEvent(const Event_ListGames &event); void processJoinRoomEvent(const Event_JoinRoom &event); void processLeaveRoomEvent(const Event_LeaveRoom &event); void processRoomSayEvent(const Event_RoomSay &event); + void refreshShortcuts(); public: TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerInfo_User *_ownUser, const ServerInfo_Room &info); ~TabRoom(); diff --git a/cockatrice/src/tab_server.cpp b/cockatrice/src/tab_server.cpp index 12820686..d15f6d8c 100644 --- a/cockatrice/src/tab_server.cpp +++ b/cockatrice/src/tab_server.cpp @@ -12,7 +12,6 @@ #include "tab_server.h" #include "abstractclient.h" #include "userlist.h" -#include "userinfobox.h" #include #include "pending_command.h" @@ -26,7 +25,7 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) { roomList = new QTreeWidget; roomList->setRootIsDecorated(false); - roomList->setColumnCount(4); + roomList->setColumnCount(5); roomList->header()->setStretchLastSection(false); #if QT_VERSION < 0x050000 roomList->header()->setResizeMode(0, QHeaderView::ResizeToContents); @@ -36,6 +35,7 @@ RoomSelector::RoomSelector(AbstractClient *_client, QWidget *parent) #else roomList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); roomList->header()->setSectionResizeMode(1, QHeaderView::Stretch); + roomList->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); roomList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); roomList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); #endif @@ -64,10 +64,12 @@ void RoomSelector::retranslateUi() QTreeWidgetItem *header = roomList->headerItem(); header->setText(0, tr("Room")); header->setText(1, tr("Description")); - header->setText(2, tr("Players")); - header->setText(3, tr("Games")); + header->setText(2, tr("Permissions")); + header->setText(3, tr("Players")); + header->setText(4, tr("Games")); header->setTextAlignment(2, Qt::AlignRight); header->setTextAlignment(3, Qt::AlignRight); + header->setTextAlignment(4, Qt::AlignRight); } void RoomSelector::processListRoomsEvent(const Event_ListRooms &event) @@ -83,10 +85,12 @@ void RoomSelector::processListRoomsEvent(const Event_ListRooms &event) twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name())); if (room.has_description()) twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description())); + if (room.has_permissionlevel()) + twi->setData(2, Qt::DisplayRole, QString::fromStdString(room.permissionlevel()).toLower()); if (room.has_player_count()) - twi->setData(2, Qt::DisplayRole, room.player_count()); + twi->setData(3, Qt::DisplayRole, room.player_count()); if (room.has_game_count()) - twi->setData(3, Qt::DisplayRole, room.game_count()); + twi->setData(4, Qt::DisplayRole, room.game_count()); return; } } @@ -96,10 +100,13 @@ void RoomSelector::processListRoomsEvent(const Event_ListRooms &event) twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name())); if (room.has_description()) twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description())); - twi->setData(2, Qt::DisplayRole, room.player_count()); - twi->setData(3, Qt::DisplayRole, room.game_count()); + if (room.has_permissionlevel()) + twi->setData(2, Qt::DisplayRole, QString::fromStdString(room.permissionlevel()).toLower()); + twi->setData(3, Qt::DisplayRole, room.player_count()); + twi->setData(4, Qt::DisplayRole, room.game_count()); twi->setTextAlignment(2, Qt::AlignRight); twi->setTextAlignment(3, Qt::AlignRight); + twi->setTextAlignment(4, Qt::AlignRight); roomList->addTopLevelItem(twi); if (room.has_auto_join()) @@ -131,10 +138,15 @@ void RoomSelector::joinClicked() void RoomSelector::joinFinished(const Response &r, const CommandContainer & /*commandContainer*/, const QVariant &extraData) { - if (r.response_code() != Response::RespOk) - return; + switch (r.response_code()) { + case Response::RespOk: break; + case Response::RespUserLevelTooLow: QMessageBox::critical(this, tr("Error"), tr("You do not have the proper permission to join this room.")); return; + default: + QMessageBox::critical(this, tr("Error"), tr("Failed to join the room due to an unknown error.")); + return; + } + const Response_JoinRoom &resp = r.GetExtension(Response_JoinRoom::ext); - emit roomJoined(resp.room_info(), extraData.toBool()); } diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 3edb4775..015d9267 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -1,3 +1,6 @@ +#include +#include +#include #include #include "tab_supervisor.h" #include "abstractclient.h" @@ -13,14 +16,12 @@ #include "pixmapgenerator.h" #include "userlist.h" #include "settingscache.h" -#include -#include -#include #include "pb/room_commands.pb.h" #include "pb/room_event.pb.h" #include "pb/game_event_container.pb.h" #include "pb/event_user_message.pb.h" +#include "pb/event_notify_user.pb.h" #include "pb/event_game_joined.pb.h" #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_room.pb.h" @@ -90,6 +91,7 @@ TabSupervisor::TabSupervisor(AbstractClient *_client, QWidget *parent) connect(client, SIGNAL(gameJoinedEventReceived(const Event_GameJoined &)), this, SLOT(gameJoined(const Event_GameJoined &))); connect(client, SIGNAL(userMessageEventReceived(const Event_UserMessage &)), this, SLOT(processUserMessageEvent(const Event_UserMessage &))); connect(client, SIGNAL(maxPingTime(int, int)), this, SLOT(updatePingTime(int, int))); + connect(client, SIGNAL(notifyUserEventReceived(const Event_NotifyUser &)), this, SLOT(processNotifyUserEvent(const Event_NotifyUser &))); retranslateUi(); } @@ -184,6 +186,7 @@ int TabSupervisor::myAddTab(Tab *tab) void TabSupervisor::start(const ServerInfo_User &_userInfo) { + isLocalGame = false; userInfo = new ServerInfo_User(_userInfo); tabServer = new TabServer(this, client); @@ -227,6 +230,7 @@ void TabSupervisor::startLocal(const QList &_clients) tabDeckStorage = 0; tabReplays = 0; tabAdmin = 0; + isLocalGame = true; userInfo = new ServerInfo_User; localClients = _clients; for (int i = 0; i < localClients.size(); ++i) @@ -557,3 +561,13 @@ bool TabSupervisor::getAdminLocked() const return true; return tabAdmin->getLocked(); } + +void TabSupervisor::processNotifyUserEvent(const Event_NotifyUser &event) +{ + switch ((Event_NotifyUser::NotificationType) event.type()) { + case Event_NotifyUser::PROMOTED: QMessageBox::information(this, tr("Promotion"), tr("You have been promoted to moderator. Please log out and back in for changes to take effect.")); break; + default: ; + } + +} + diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index 0144de12..13722032 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -22,6 +22,7 @@ class RoomEvent; class GameEventContainer; class Event_GameJoined; class Event_UserMessage; +class Event_NotifyUser; class ServerInfo_Room; class ServerInfo_User; class GameReplay; @@ -60,6 +61,7 @@ private: void addCloseButtonToTab(Tab *tab, int tabIndex); QString sanitizeTabName(QString dirty) const; QString sanitizeHtml(QString dirty) const; + bool isLocalGame; public: TabSupervisor(AbstractClient *_client, QWidget *parent = 0); ~TabSupervisor(); @@ -67,6 +69,7 @@ public: void start(const ServerInfo_User &userInfo); void startLocal(const QList &_clients); void stop(); + bool getIsLocalGame() const { return isLocalGame; } int getGameCount() const { return gameTabs.size(); } TabUserLists *getUserListsTab() const { return tabUserLists; } ServerInfo_User *getUserInfo() const { return userInfo; } @@ -103,6 +106,7 @@ private slots: void processRoomEvent(const RoomEvent &event); void processGameEventContainer(const GameEventContainer &cont); void processUserMessageEvent(const Event_UserMessage &event); + void processNotifyUserEvent(const Event_NotifyUser &event); }; -#endif +#endif \ No newline at end of file diff --git a/cockatrice/src/tab_userlists.cpp b/cockatrice/src/tab_userlists.cpp index 61fe693c..7cd3fec7 100644 --- a/cockatrice/src/tab_userlists.cpp +++ b/cockatrice/src/tab_userlists.cpp @@ -21,7 +21,7 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor, AbstractClient *_clien allUsersList = new UserList(_tabSupervisor, client, UserList::AllUsersList); buddyList = new UserList(_tabSupervisor, client, UserList::BuddyList); ignoreList = new UserList(_tabSupervisor, client, UserList::IgnoreList); - userInfoBox = new UserInfoBox(client, false); + userInfoBox = new UserInfoBox(client, true); userInfoBox->updateInfo(userInfo); connect(allUsersList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool))); diff --git a/cockatrice/src/tablezone.cpp b/cockatrice/src/tablezone.cpp index 0d486870..eebae0f3 100644 --- a/cockatrice/src/tablezone.cpp +++ b/cockatrice/src/tablezone.cpp @@ -30,9 +30,9 @@ TableZone::TableZone(Player *_p, QGraphicsItem *parent) updateBg(); - height = 2 * BOX_LINE_WIDTH + 3 * (CARD_HEIGHT + 20) + 2 * PADDING_Y; - width = MIN_WIDTH + 2 * MARGIN_X + 2 * BOX_LINE_WIDTH; - currentMinimumWidth = MIN_WIDTH; + height = MARGIN_TOP + MARGIN_BOTTOM + TABLEROWS * CARD_HEIGHT + (TABLEROWS-1) * PADDING_Y; + width = MIN_WIDTH; + currentMinimumWidth = width; setCacheMode(DeviceCoordinateCache); #if QT_VERSION < 0x050000 @@ -107,10 +107,12 @@ void TableZone::paintZoneOutline(QPainter *painter) { @painter QPainter object */ void TableZone::paintLandDivider(QPainter *painter){ - painter->setPen(QColor(255, 255, 255, 40)); - qreal separatorY = 2 * (CARD_HEIGHT + 20 + PADDING_Y) + BOX_LINE_WIDTH - PADDING_Y / 2; + // Place the line 2 grid heights down then back it off just enough to allow + // some space between a 3-card stack and the land area. + qreal separatorY = MARGIN_TOP + 2 * (CARD_HEIGHT + PADDING_Y) - STACKED_CARD_OFFSET_Y / 2; if (isInverted()) separatorY = height - separatorY; + painter->setPen(QColor(255, 255, 255, 40)); painter->drawLine(QPointF(0, separatorY), QPointF(width, separatorY)); } @@ -157,30 +159,9 @@ void TableZone::reorganizeCards() { QList arrowsToUpdate; - // Calculate table grid distortion so that the mapping functions work properly - QMap gridPointStackCount; - for (int i = 0; i < cards.size(); ++i) { - const QPoint &gridPoint = cards[i]->getGridPos(); - if (gridPoint.x() == -1) - continue; - - const int key = gridPoint.x() / 3 + gridPoint.y() * 1000; - gridPointStackCount.insert(key, gridPointStackCount.value(key, 0) + 1); - } - gridPointWidth.clear(); - for (int i = 0; i < cards.size(); ++i) { - const QPoint &gridPoint = cards[i]->getGridPos(); - if (gridPoint.x() == -1) - continue; - - const int key = gridPoint.x() / 3 + gridPoint.y() * 1000; - const int stackCount = gridPointStackCount.value(key, 0); - if (stackCount == 1) - gridPointWidth.insert(key, CARD_WIDTH * (1 + cards[i]->getAttachedCards().size() / 3.0)); - else - gridPointWidth.insert(key, CARD_WIDTH * (1 + (stackCount - 1) / 3.0)); - } - + // Calculate card stack widths so mapping functions work properly + computeCardStackWidths(); + for (int i = 0; i < cards.size(); ++i) { QPoint gridPoint = cards[i]->getGridPos(); if (gridPoint.x() == -1) @@ -191,7 +172,7 @@ void TableZone::reorganizeCards() qreal y = mapPoint.y(); int numberAttachedCards = cards[i]->getAttachedCards().size(); - qreal actualX = x + numberAttachedCards * CARD_WIDTH / 3.0; + qreal actualX = x + numberAttachedCards * STACKED_CARD_OFFSET_X; qreal actualY = y; if (numberAttachedCards) actualY += 15; @@ -204,7 +185,7 @@ void TableZone::reorganizeCards() while (attachedCardIterator.hasNext()) { ++j; CardItem *attachedCard = attachedCardIterator.next(); - qreal childX = actualX - j * CARD_WIDTH / 3.0; + qreal childX = actualX - j * STACKED_CARD_OFFSET_X; qreal childY = y + 5; attachedCard->setPos(childX, childY); attachedCard->setRealZValue((childY + CARD_HEIGHT) * 100000 + (childX + 1) * 100); @@ -263,13 +244,19 @@ CardItem *TableZone::takeCard(int position, int cardId, bool canResize) void TableZone::resizeToContents() { int xMax = 0; + + // Find rightmost card position, which includes the left margin amount. for (int i = 0; i < cards.size(); ++i) if (cards[i]->pos().x() > xMax) xMax = (int) cards[i]->pos().x(); - xMax += 2 * CARD_WIDTH; - if (xMax < MIN_WIDTH) - xMax = MIN_WIDTH; - currentMinimumWidth = xMax + 2 * MARGIN_X + 2 * BOX_LINE_WIDTH; + + // Minimum width is the rightmost card position plus enough room for + // another card with padding, then margin. + currentMinimumWidth = xMax + (2 * CARD_WIDTH) + PADDING_X + MARGIN_RIGHT; + + if (currentMinimumWidth < MIN_WIDTH) + currentMinimumWidth = MIN_WIDTH; + if (currentMinimumWidth != width) { prepareGeometryChange(); width = currentMinimumWidth; @@ -286,6 +273,7 @@ CardItem *TableZone::getCardFromGrid(const QPoint &gridPoint) const return 0; } + CardItem *TableZone::getCardFromCoords(const QPointF &point) const { QPoint gridPoint = mapToGrid(point); @@ -293,57 +281,110 @@ CardItem *TableZone::getCardFromCoords(const QPointF &point) const } +void TableZone::computeCardStackWidths() +{ + // Each card stack is three grid points worth of card locations. + // First pass: compute the number of cards at each card stack. + QMap cardStackCount; + for (int i = 0; i < cards.size(); ++i) { + const QPoint &gridPoint = cards[i]->getGridPos(); + if (gridPoint.x() == -1) + continue; + + const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); + cardStackCount.insert(key, cardStackCount.value(key, 0) + 1); + } + + // Second pass: compute the width at each card stack. + cardStackWidth.clear(); + for (int i = 0; i < cards.size(); ++i) { + const QPoint &gridPoint = cards[i]->getGridPos(); + if (gridPoint.x() == -1) + continue; + + const int key = getCardStackMapKey(gridPoint.x() / 3, gridPoint.y()); + const int stackCount = cardStackCount.value(key, 0); + if (stackCount == 1) + cardStackWidth.insert(key, CARD_WIDTH + cards[i]->getAttachedCards().size() * STACKED_CARD_OFFSET_X); + else + cardStackWidth.insert(key, CARD_WIDTH + (stackCount - 1) * STACKED_CARD_OFFSET_X); + } +} + + QPointF TableZone::mapFromGrid(QPoint gridPoint) const { qreal x, y; - x = MARGIN_X + (gridPoint.x() % 3) * CARD_WIDTH / 3.0; + + // Start with margin plus stacked card offset + x = MARGIN_LEFT + (gridPoint.x() % 3) * STACKED_CARD_OFFSET_X; + + // Add in width of card stack plus padding for each column for (int i = 0; i < gridPoint.x() / 3; ++i) - x += gridPointWidth.value(gridPoint.y() * 1000 + i, CARD_WIDTH) + PADDING_X; + { + const int key = getCardStackMapKey(i, gridPoint.y()); + x += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + } if (isInverted()) - gridPoint.setY(2 - gridPoint.y()); + gridPoint.setY(TABLEROWS - 1 - gridPoint.y()); - y = BOX_LINE_WIDTH + gridPoint.y() * (CARD_HEIGHT + PADDING_Y + 20) + (gridPoint.x() % 3) * 10; -/* - if (isInverted()) - y = height - CARD_HEIGHT - y; -*/ + // Start with margin plus stacked card offset + y = MARGIN_TOP + (gridPoint.x() % 3) * STACKED_CARD_OFFSET_Y; + + // Add in card size and padding for each row + for (int i = 0; i < gridPoint.y(); ++i) + y += CARD_HEIGHT + PADDING_Y; + return QPointF(x, y); } QPoint TableZone::mapToGrid(const QPointF &mapPoint) const { - qreal x = mapPoint.x() - MARGIN_X; - qreal y = mapPoint.y(); -/* if (isInverted()) - y = height - y; -*/ y -= BOX_LINE_WIDTH; - - if (x < 0) - x = 0; - else if (x > width - CARD_WIDTH - MARGIN_X) - x = width - CARD_WIDTH - MARGIN_X; - if (y < 0) - y = 0; - else if (y > height - CARD_HEIGHT) - y = height - CARD_HEIGHT; - - int resultY = round(y / (CARD_HEIGHT + PADDING_Y + 20)); - if (isInverted()) - resultY = 2 - resultY; + // Begin by calculating the y-coordinate of the grid space, which will be + // used for the x-coordinate. - int baseX = -1; - qreal oldTempX = 0, tempX = 0; - do { - ++baseX; - oldTempX = tempX; - tempX += gridPointWidth.value(resultY * 1000 + baseX, CARD_WIDTH) + PADDING_X; - } while (tempX < x + 1); - - qreal xdiff = x - oldTempX; - int resultX = baseX * 3 + qMin((int) floor(xdiff * 3 / CARD_WIDTH), 2); - return QPoint(resultX, resultY); + // Offset point by the margin amount to reference point within grid area. + int y = mapPoint.y() - MARGIN_TOP; + + // Below calculation effectively rounds to the nearest grid point. + const int gridPointHeight = CARD_HEIGHT + PADDING_Y; + int gridPointY = (y + gridPointHeight / 2) / gridPointHeight; + + gridPointY = clampValidTableRow(gridPointY); + + if (isInverted()) + gridPointY = TABLEROWS - 1 - gridPointY; + + // Calculating the x-coordinate of the grid space requires adding up the + // widths of each card stack along the row. + + // Offset point by the margin amount to reference point within grid area. + int x = mapPoint.x() - MARGIN_LEFT; + + // Maximum value is a card width from the right margin, referenced to the + // grid area. + const int xMax = width - MARGIN_LEFT - MARGIN_RIGHT - CARD_WIDTH; + + int xStack = 0; + int xNextStack = 0; + int nextStackCol = 0; + while ((xNextStack <= x) && (xNextStack <= xMax)) { + xStack = xNextStack; + const int key = getCardStackMapKey(nextStackCol, gridPointY); + xNextStack += cardStackWidth.value(key, CARD_WIDTH) + PADDING_X; + nextStackCol++; + } + int stackCol = qMax(nextStackCol - 1, 0); + + // Have the stack column, need to refine to the grid column. Take the + // difference between the point and the stack point and divide by stacked + // card offsets. + int xDiff = x - xStack; + int gridPointX = stackCol * 3 + qMin(xDiff / STACKED_CARD_OFFSET_X, 2); + + return QPoint(gridPointX, gridPointY); } @@ -357,3 +398,12 @@ QPointF TableZone::closestGridPoint(const QPointF &point) gridPoint.setX(gridPoint.x() + 1); return mapFromGrid(gridPoint); } + +int TableZone::clampValidTableRow(const int row) +{ + if(row < 0) + return 0; + if(row >= TABLEROWS) + return TABLEROWS - 1; + return row; +} diff --git a/cockatrice/src/tablezone.h b/cockatrice/src/tablezone.h index 6eeae7f1..55b12b8b 100644 --- a/cockatrice/src/tablezone.h +++ b/cockatrice/src/tablezone.h @@ -20,12 +20,34 @@ signals: void sizeChanged(); private: - static const int BOX_LINE_WIDTH = 10; + static const int TABLEROWS = 3; + + /* + Margins between table edges and cards, paddings between cards + */ + static const int MARGIN_LEFT = 20; + static const int MARGIN_RIGHT = 15; + static const int MARGIN_TOP = 10; + static const int MARGIN_BOTTOM = 30; static const int PADDING_X = 35; - static const int PADDING_Y = 10; - static const int MARGIN_X = 20; - static const int MIN_WIDTH = 15 * CARD_WIDTH / 2; - + static const int PADDING_Y = 30; + + /* + Minimum width of the table zone including margins. + */ + static const int MIN_WIDTH = MARGIN_LEFT + (5 * CARD_WIDTH) + MARGIN_RIGHT; + + /* + Offset sizes when cards are stacked on each other in the grid + */ + static const int STACKED_CARD_OFFSET_X = CARD_WIDTH / 3; + static const int STACKED_CARD_OFFSET_Y = PADDING_Y / 3; + + /* + Width of the box line drawn in the margin around the active player's area + */ + static const int BOX_LINE_WIDTH = 10; + /* Default inactive mask and border gradient */ @@ -37,11 +59,20 @@ private: /* Size and shape variables */ - QMap gridPointWidth; int width; int height; int currentMinimumWidth; + /* + Internal cache for widths of stacks of cards by row and column. + */ + QMap cardStackWidth; + + /* + Holds any custom background image for the TableZone + */ + QPixmap backgroundPixelMap; + /* If this TableZone is currently active */ @@ -108,10 +139,10 @@ public: */ CardItem *getCardFromCoords(const QPointF &point) const; - QPointF mapFromGrid(QPoint gridPoint) const; - QPoint mapToGrid(const QPointF &mapPoint) const; QPointF closestGridPoint(const QPointF &point); + static int clampValidTableRow(const int row); + /** Removes a card from view. @@ -129,7 +160,7 @@ public: void resizeToContents(); int getMinimumWidth() const { return currentMinimumWidth; } - void setWidth(qreal _width){ prepareGeometryChange(); width = _width;}; + void setWidth(qreal _width) { prepareGeometryChange(); width = _width; } qreal getWidth() const { return width; } void setActive(bool _active) { active = _active; update(); } @@ -139,6 +170,22 @@ protected: private: void paintZoneOutline(QPainter *painter); void paintLandDivider(QPainter *painter); + + /* + Calculates card stack widths so mapping functions work properly + */ + void computeCardStackWidths(); + + /* + Mapping functions for points to/from gridpoints. + */ + QPointF mapFromGrid(QPoint gridPoint) const; + QPoint mapToGrid(const QPointF &mapPoint) const; + + /* + Helper function to create a single key from a card stack location. + */ + int getCardStackMapKey (int x, int y) const { return x + (y * 1000); } }; #endif diff --git a/cockatrice/src/user_context_menu.cpp b/cockatrice/src/user_context_menu.cpp index ad3372d1..9bf0f484 100644 --- a/cockatrice/src/user_context_menu.cpp +++ b/cockatrice/src/user_context_menu.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "user_context_menu.h" #include "tab_supervisor.h" #include "tab_userlists.h" @@ -31,6 +32,8 @@ UserContextMenu::UserContextMenu(const TabSupervisor *_tabSupervisor, QWidget *p aRemoveFromIgnoreList = new QAction(QString(), this); aKick = new QAction(QString(), this); aBan = new QAction(QString(), this); + aPromoteToMod = new QAction(QString(), this); + aDemoteFromMod = new QAction(QString(), this); retranslateUi(); } @@ -46,6 +49,8 @@ void UserContextMenu::retranslateUi() aRemoveFromIgnoreList->setText(tr("Remove from &ignore list")); aKick->setText(tr("Kick from &game")); aBan->setText(tr("Ban from &server")); + aPromoteToMod->setText(tr("&Promote user to moderator")); + aDemoteFromMod->setText(tr("Dem&ote user from moderator")); } void UserContextMenu::gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer) @@ -89,6 +94,27 @@ void UserContextMenu::banUser_processUserInfoResponse(const Response &r) dlg->show(); } +void UserContextMenu::adjustMod_processUserResponse(const Response &resp, const CommandContainer &commandContainer) +{ + + const Command_AdjustMod &cmd = commandContainer.admin_command(0).GetExtension(Command_AdjustMod::ext); + + if (resp.response_code() == Response::RespOk) { + if (cmd.should_be_mod()) { + QMessageBox::information(static_cast(parent()), tr("Success"), tr("Successfully promoted user.")); + } else { + QMessageBox::information(static_cast(parent()), tr("Success"), tr("Successfully demoted user.")); + } + + } else { + if (cmd.should_be_mod()) { + QMessageBox::information(static_cast(parent()), tr("Failed"), tr("Failed to promote user.")); + } else { + QMessageBox::information(static_cast(parent()), tr("Failed"), tr("Failed to demote user.")); + } + } +} + void UserContextMenu::banUser_dialogFinished() { BanDialog *dlg = static_cast(sender()); @@ -99,6 +125,7 @@ void UserContextMenu::banUser_dialogFinished() cmd.set_minutes(dlg->getMinutes()); cmd.set_reason(dlg->getReason().toStdString()); cmd.set_visible_reason(dlg->getVisibleReason().toStdString()); + cmd.set_clientid(dlg->getBanId().toStdString()); client->sendCommand(client->prepareModeratorCommand(cmd)); } @@ -131,6 +158,14 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName if (!tabSupervisor->getAdminLocked()) { menu->addSeparator(); menu->addAction(aBan); + + menu->addSeparator(); + if (userLevel.testFlag(ServerInfo_User::IsModerator) && (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { + menu->addAction(aDemoteFromMod); + + } else if (userLevel.testFlag(ServerInfo_User::IsRegistered) && (tabSupervisor->getUserInfo()->user_level() & ServerInfo_User::IsAdmin)) { + menu->addAction(aPromoteToMod); + } } bool anotherUser = userName != QString::fromStdString(tabSupervisor->getUserInfo()->name()); aDetails->setEnabled(online); @@ -142,10 +177,12 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName aRemoveFromIgnoreList->setEnabled(anotherUser); aKick->setEnabled(anotherUser); aBan->setEnabled(anotherUser); + aPromoteToMod->setEnabled(anotherUser); + aDemoteFromMod->setEnabled(anotherUser); QAction *actionClicked = menu->exec(pos); if (actionClicked == aDetails) { - UserInfoBox *infoWidget = new UserInfoBox(client, true, static_cast(parent()), Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); + UserInfoBox *infoWidget = new UserInfoBox(client, false, static_cast(parent()), Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); infoWidget->setAttribute(Qt::WA_DeleteOnClose); infoWidget->updateInfo(userName); } else if (actionClicked == aChat) @@ -185,6 +222,7 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName } else if (actionClicked == aKick) { Command_KickFromGame cmd; cmd.set_player_id(playerId); + game->sendGameCommand(cmd); } else if (actionClicked == aBan) { Command_GetUserInfo cmd; @@ -192,7 +230,14 @@ void UserContextMenu::showContextMenu(const QPoint &pos, const QString &userName PendingCommand *pend = client->prepareSessionCommand(cmd); connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(banUser_processUserInfoResponse(Response))); + client->sendCommand(pend); + } else if (actionClicked == aPromoteToMod || actionClicked == aDemoteFromMod) { + Command_AdjustMod cmd; + cmd.set_user_name(userName.toStdString()); + cmd.set_should_be_mod(actionClicked == aPromoteToMod); + PendingCommand *pend = client->prepareAdminCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(adjustMod_processUserResponse(Response, CommandContainer))); client->sendCommand(pend); } diff --git a/cockatrice/src/user_context_menu.h b/cockatrice/src/user_context_menu.h index 9aeadfd4..dd0d1ed5 100644 --- a/cockatrice/src/user_context_menu.h +++ b/cockatrice/src/user_context_menu.h @@ -27,10 +27,12 @@ private: QAction *aAddToIgnoreList, *aRemoveFromIgnoreList; QAction *aKick; QAction *aBan; + QAction *aPromoteToMod, *aDemoteFromMod; signals: void openMessageDialog(const QString &userName, bool focus); private slots: void banUser_processUserInfoResponse(const Response &resp); + void adjustMod_processUserResponse(const Response &resp, const CommandContainer &commandContainer); void banUser_dialogFinished(); void gamesOfUserReceived(const Response &resp, const CommandContainer &commandContainer); public: diff --git a/cockatrice/src/userinfobox.cpp b/cockatrice/src/userinfobox.cpp index 2f367212..6e590be5 100644 --- a/cockatrice/src/userinfobox.cpp +++ b/cockatrice/src/userinfobox.cpp @@ -1,10 +1,15 @@ #include "userinfobox.h" #include "pixmapgenerator.h" #include "abstractclient.h" +#include "dlg_edit_user.h" +#include "dlg_edit_password.h" +#include "dlg_edit_avatar.h" + #include #include #include - +#include +#include #include "pending_command.h" #include "pb/session_commands.pb.h" @@ -14,13 +19,16 @@ const qint64 SIXTY = 60; const qint64 HOURS_IN_A_DAY = 24; const qint64 DAYS_IN_A_YEAR = 365; -UserInfoBox::UserInfoBox(AbstractClient *_client, bool _fullInfo, QWidget *parent, Qt::WindowFlags flags) - : QWidget(parent, flags), client(_client), fullInfo(_fullInfo) +UserInfoBox::UserInfoBox(AbstractClient *_client, bool _editable, QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags), client(_client), editable(_editable) { QFont nameFont = nameLabel.font(); nameFont.setBold(true); nameFont.setPointSize(nameFont.pointSize() * 1.5); nameLabel.setFont(nameFont); + + avatarLabel.setMaximumWidth(400); + avatarLabel.setMaximumHeight(200); QGridLayout *mainLayout = new QGridLayout; mainLayout->addWidget(&avatarLabel, 0, 0, 1, 3, Qt::AlignCenter); @@ -38,6 +46,19 @@ UserInfoBox::UserInfoBox(AbstractClient *_client, bool _fullInfo, QWidget *paren mainLayout->addWidget(&accountAgeLebel1, 6, 0, 1, 1); mainLayout->addWidget(&accountAgeLabel2, 6, 2, 1, 1); mainLayout->setColumnStretch(2, 10); + + if(editable) + { + QHBoxLayout * buttonsLayout = new QHBoxLayout; + buttonsLayout->addWidget(&editButton); + buttonsLayout->addWidget(&passwordButton); + buttonsLayout->addWidget(&avatarButton); + mainLayout->addLayout(buttonsLayout, 7, 0, 1, 3); + + connect(&editButton, SIGNAL(clicked()), this, SLOT(actEdit())); + connect(&passwordButton, SIGNAL(clicked()), this, SLOT(actPassword())); + connect(&avatarButton, SIGNAL(clicked()), this, SLOT(actAvatar())); + } setWindowTitle(tr("User information")); setLayout(mainLayout); @@ -47,10 +68,14 @@ UserInfoBox::UserInfoBox(AbstractClient *_client, bool _fullInfo, QWidget *paren void UserInfoBox::retranslateUi() { realNameLabel1.setText(tr("Real name:")); - genderLabel1.setText(tr("Gender:")); + genderLabel1.setText(tr("Pronouns:")); countryLabel1.setText(tr("Location:")); userLevelLabel1.setText(tr("User level:")); accountAgeLebel1.setText(tr("Account Age:")); + + editButton.setText(tr("Edit")); + passwordButton.setText(tr("Change password")); + avatarButton.setText(tr("Change avatar")); } void UserInfoBox::updateInfo(const ServerInfo_User &user) @@ -61,7 +86,7 @@ void UserInfoBox::updateInfo(const ServerInfo_User &user) const std::string bmp = user.avatar_bmp(); if (!avatarPixmap.loadFromData((const uchar *) bmp.data(), bmp.size())) avatarPixmap = UserLevelPixmapGenerator::generatePixmap(64, userLevel, false); - avatarLabel.setPixmap(avatarPixmap); + avatarLabel.setPixmap(avatarPixmap.scaled(avatarLabel.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); nameLabel.setText(QString::fromStdString(user.name())); realNameLabel2.setText(QString::fromStdString(user.real_name())); @@ -137,3 +162,126 @@ void UserInfoBox::processResponse(const Response &r) setFixedSize(sizeHint()); show(); } + +void UserInfoBox::actEdit() +{ + Command_GetUserInfo cmd; + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(actEditInternal(const Response &))); + + client->sendCommand(pend); +} + +void UserInfoBox::actEditInternal(const Response &r) +{ + const Response_GetUserInfo &response = r.GetExtension(Response_GetUserInfo::ext); + const ServerInfo_User &user = response.user_info(); + + QString email = QString::fromStdString(user.email()); + int gender = user.gender(); + QString country = QString::fromStdString(user.country()); + QString realName = QString::fromStdString(user.real_name()); + + DlgEditUser dlg(this, email, gender, country, realName); + if(!dlg.exec()) + return; + + Command_AccountEdit cmd; + cmd.set_real_name(dlg.getRealName().toStdString()); + cmd.set_email(dlg.getEmail().toStdString()); + cmd.set_gender((ServerInfo_User_Gender) dlg.getGender()); + cmd.set_country(dlg.getCountry().toStdString()); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(processEditResponse(const Response &))); + + client->sendCommand(pend); +} + +void UserInfoBox::actPassword() +{ + DlgEditPassword dlg(this); + if(!dlg.exec()) + return; + + Command_AccountPassword cmd; + cmd.set_old_password(dlg.getOldPassword().toStdString()); + cmd.set_new_password(dlg.getNewPassword().toStdString()); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(processPasswordResponse(const Response &))); + + client->sendCommand(pend); +} + +void UserInfoBox::actAvatar() +{ + DlgEditAvatar dlg(this); + if(!dlg.exec()) + return; + + Command_AccountImage cmd; + cmd.set_image(dlg.getImage().data(), dlg.getImage().size()); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(processAvatarResponse(const Response &))); + + client->sendCommand(pend); +} + +void UserInfoBox::processEditResponse(const Response &r) +{ + switch (r.response_code()) { + case Response::RespOk: + updateInfo(nameLabel.text()); + QMessageBox::information(this, tr("Information"), tr("User information updated.")); + break; + case Response::RespFunctionNotAllowed: + QMessageBox::critical(this, tr("Error"), tr("This server does not permit you to update your user informations.")); + break; + case Response::RespInternalError: + default: + QMessageBox::critical(this, tr("Error"), tr("An error occured while trying to update your user informations.")); + break; + } +} + +void UserInfoBox::processPasswordResponse(const Response &r) +{ + switch (r.response_code()) { + case Response::RespOk: + QMessageBox::information(this, tr("Information"), tr("Password changed.")); + break; + case Response::RespFunctionNotAllowed: + QMessageBox::critical(this, tr("Error"), tr("This server does not permit you to change your password.")); + break; + case Response::RespPasswordTooShort: + QMessageBox::critical(this, tr("Error"), tr("The new password is too short.")); + break; + case Response::RespWrongPassword: + QMessageBox::critical(this, tr("Error"), tr("The old password is incorrect.")); + break; + case Response::RespInternalError: + default: + QMessageBox::critical(this, tr("Error"), tr("An error occured while trying to update your user informations.")); + break; + } +} + +void UserInfoBox::processAvatarResponse(const Response &r) +{ + switch (r.response_code()) { + case Response::RespOk: + updateInfo(nameLabel.text()); + QMessageBox::information(this, tr("Information"), tr("Avatar updated.")); + break; + case Response::RespFunctionNotAllowed: + QMessageBox::critical(this, tr("Error"), tr("This server does not permit you to update your avatar.")); + break; + case Response::RespInternalError: + default: + QMessageBox::critical(this, tr("Error"), tr("An error occured while trying to updater your avatar.")); + break; + } +} \ No newline at end of file diff --git a/cockatrice/src/userinfobox.h b/cockatrice/src/userinfobox.h index e144cd4c..43ae7034 100644 --- a/cockatrice/src/userinfobox.h +++ b/cockatrice/src/userinfobox.h @@ -3,8 +3,8 @@ #include #include +#include -class QLabel; class ServerInfo_User; class AbstractClient; class Response; @@ -13,14 +13,24 @@ class UserInfoBox : public QWidget { Q_OBJECT private: AbstractClient *client; - bool fullInfo; + bool editable; QLabel avatarLabel, nameLabel, realNameLabel1, realNameLabel2, genderLabel1, genderLabel2, countryLabel1, countryLabel2, countryLabel3, userLevelLabel1, userLevelLabel2, userLevelLabel3, accountAgeLebel1, accountAgeLabel2; + QPushButton editButton, passwordButton, avatarButton; + public: - UserInfoBox(AbstractClient *_client, bool fullInfo, QWidget *parent = 0, Qt::WindowFlags flags = 0); + UserInfoBox(AbstractClient *_client, bool editable, QWidget *parent = 0, Qt::WindowFlags flags = 0); void retranslateUi(); private slots: void processResponse(const Response &r); + void processEditResponse(const Response &r); + void processPasswordResponse(const Response &r); + void processAvatarResponse(const Response &r); + + void actEdit(); + void actEditInternal(const Response &r); + void actPassword(); + void actAvatar(); public slots: void updateInfo(const ServerInfo_User &user); void updateInfo(const QString &userName); diff --git a/cockatrice/src/userlist.cpp b/cockatrice/src/userlist.cpp index a4c7d7dc..59717608 100644 --- a/cockatrice/src/userlist.cpp +++ b/cockatrice/src/userlist.cpp @@ -3,7 +3,6 @@ #include "tab_supervisor.h" #include "abstractclient.h" #include "pixmapgenerator.h" -#include "userinfobox.h" #include "user_context_menu.h" #include "gameselector.h" #include @@ -37,11 +36,19 @@ BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent) ipBanCheckBox = new QCheckBox(tr("ban &IP address")); ipBanCheckBox->setChecked(true); ipBanEdit = new QLineEdit(QString::fromStdString(info.address())); + idBanCheckBox = new QCheckBox(tr("ban client I&D")); + idBanCheckBox->setChecked(true); + idBanEdit = new QLineEdit(QString::fromStdString(info.clientid())); + if (QString::fromStdString(info.clientid()).isEmpty()) + idBanCheckBox->setChecked(false); + QGridLayout *banTypeGrid = new QGridLayout; banTypeGrid->addWidget(nameBanCheckBox, 0, 0); banTypeGrid->addWidget(nameBanEdit, 0, 1); banTypeGrid->addWidget(ipBanCheckBox, 1, 0); banTypeGrid->addWidget(ipBanEdit, 1, 1); + banTypeGrid->addWidget(idBanCheckBox, 2, 0); + banTypeGrid->addWidget(idBanEdit, 2, 1); QGroupBox *banTypeGroupBox = new QGroupBox(tr("Ban type")); banTypeGroupBox->setLayout(banTypeGrid); @@ -111,8 +118,8 @@ BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent) void BanDialog::okClicked() { - if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked()) { - QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based or IP-based ban, or both.")); + if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked() && !idBanCheckBox->isChecked()) { + QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban.")); return; } accept(); @@ -128,6 +135,11 @@ void BanDialog::enableTemporaryEdits(bool enabled) minutesEdit->setEnabled(enabled); } +QString BanDialog::getBanId() const +{ + return idBanCheckBox->isChecked() ? idBanEdit->text() : QString(); +} + QString BanDialog::getBanName() const { return nameBanCheckBox->isChecked() ? nameBanEdit->text() : QString(); diff --git a/cockatrice/src/userlist.h b/cockatrice/src/userlist.h index fc043b1d..3ac47a08 100644 --- a/cockatrice/src/userlist.h +++ b/cockatrice/src/userlist.h @@ -24,8 +24,8 @@ class BanDialog : public QDialog { Q_OBJECT private: QLabel *daysLabel, *hoursLabel, *minutesLabel; - QCheckBox *nameBanCheckBox, *ipBanCheckBox; - QLineEdit *nameBanEdit, *ipBanEdit; + QCheckBox *nameBanCheckBox, *ipBanCheckBox, *idBanCheckBox; + QLineEdit *nameBanEdit, *ipBanEdit, *idBanEdit; QSpinBox *daysEdit, *hoursEdit, *minutesEdit; QRadioButton *permanentRadio, *temporaryRadio; QPlainTextEdit *reasonEdit, *visibleReasonEdit; @@ -36,6 +36,7 @@ public: BanDialog(const ServerInfo_User &info, QWidget *parent = 0); QString getBanName() const; QString getBanIP() const; + QString getBanId() const; int getMinutes() const; QString getReason() const; QString getVisibleReason() const; diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 710291cf..e405a720 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -30,6 +30,9 @@ #include #include #include +#if QT_VERSION < 0x050000 + #include // for Qt::escape() +#endif #include "main.h" #include "window_main.h" @@ -43,7 +46,6 @@ #include "localclient.h" #include "settingscache.h" #include "tab_game.h" - #include "version_string.h" #include "pb/game_replay.pb.h" @@ -51,6 +53,14 @@ #include "pb/event_connection_closed.pb.h" #include "pb/event_server_shutdown.pb.h" +#define GITHUB_CONTRIBUTORS_URL "https://github.com/Cockatrice/Cockatrice/graphs/contributors?type=c" +#define GITHUB_CONTRIBUTE_URL "https://github.com/Cockatrice/Cockatrice#cockatrice" +#define GITHUB_TRANSLATOR_RECOGNIZE_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translators" +#define GITHUB_TRANSLATOR_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ" +#define GITHUB_ISSUES_URL "https://github.com/Cockatrice/Cockatrice/issues" +#define GITHUB_TROUBLESHOOTING_URL "https://github.com/Cockatrice/Cockatrice/wiki/Troubleshooting" +#define GITHUB_FAQ_URL "https://github.com/Cockatrice/Cockatrice/wiki/Frequently-Asked-Questions" + const QString MainWindow::appName = "Cockatrice"; void MainWindow::updateTabMenu(const QList &newMenuList) @@ -67,6 +77,7 @@ void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &even client->disconnectFromServer(); QString reasonStr; switch (event.reason()) { + case Event_ConnectionClosed::USER_LIMIT_REACHED: reasonStr = tr("The server has reached its maximum user capacity, please check back later."); break; case Event_ConnectionClosed::TOO_MANY_CONNECTIONS: reasonStr = tr("There are too many concurrent connections from your address."); break; case Event_ConnectionClosed::BANNED: { reasonStr = tr("Banned by moderator"); @@ -79,7 +90,8 @@ void MainWindow::processConnectionClosedEvent(const Event_ConnectionClosed &even break; } case Event_ConnectionClosed::SERVER_SHUTDOWN: reasonStr = tr("Scheduled server shutdown."); break; - case Event_ConnectionClosed::USERNAMEINVALID: reasonStr = tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username."); break; + case Event_ConnectionClosed::USERNAMEINVALID: reasonStr = tr("Invalid username."); break; + case Event_ConnectionClosed::LOGGEDINELSEWERE: reasonStr = tr("You have been logged out due to logging in at another location."); break; default: reasonStr = QString::fromStdString(event.reason_str()); } QMessageBox::critical(this, tr("Connection closed"), tr("The server has terminated your connection.\nReason: %1").arg(reasonStr)); @@ -177,24 +189,24 @@ void MainWindow::actSinglePlayer() int numberPlayers = QInputDialog::getInt(this, tr("Number of players"), tr("Please enter the number of players."), 1, 1, 8, 1, &ok); if (!ok) return; - + aConnect->setEnabled(false); aRegister->setEnabled(false); aSinglePlayer->setEnabled(false); - + localServer = new LocalServer(this); LocalServerInterface *mainLsi = localServer->newConnection(); - LocalClient *mainClient = new LocalClient(mainLsi, tr("Player %1").arg(1), this); + LocalClient *mainClient = new LocalClient(mainLsi, tr("Player %1").arg(1), settingsCache->getClientID(), this); QList localClients; localClients.append(mainClient); - + for (int i = 0; i < numberPlayers - 1; ++i) { LocalServerInterface *slaveLsi = localServer->newConnection(); - LocalClient *slaveClient = new LocalClient(slaveLsi, tr("Player %1").arg(i + 2), this); + LocalClient *slaveClient = new LocalClient(slaveLsi, tr("Player %1").arg(i + 2), settingsCache->getClientID(), this); localClients.append(slaveClient); } tabSupervisor->startLocal(localClients); - + Command_CreateGame createCommand; createCommand.set_max_players(numberPlayers); mainClient->sendCommand(mainClient->prepareRoomCommand(createCommand, 0)); @@ -207,17 +219,17 @@ void MainWindow::actWatchReplay() dlg.setNameFilters(QStringList() << QObject::tr("Cockatrice replays (*.cor)")); if (!dlg.exec()) return; - + QString fileName = dlg.selectedFiles().at(0); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) return; QByteArray buf = file.readAll(); file.close(); - + GameReplay *replay = new GameReplay; replay->ParseFromArray(buf.data(), buf.size()); - + tabSupervisor->openReplay(replay); } @@ -225,7 +237,7 @@ void MainWindow::localGameEnded() { delete localServer; localServer = 0; - + aConnect->setEnabled(true); aRegister->setEnabled(true); aSinglePlayer->setEnabled(true); @@ -263,15 +275,15 @@ void MainWindow::actAbout() + "

" + tr("Project Manager:") + "
Gavin Bisesi

" + "" + tr("Past Project Managers:") + "
Max-Wilhelm Bruker
Marcus Schütz

" + "" + tr("Developers:") + "
" - + "" + tr("Our Developers") + "
" - + "" + tr("Help Develop!") + "

" + + "" + tr("Our Developers") + "
" + + "" + tr("Help Develop!") + "

" + "" + tr("Translators:") + "
" - + "" + tr("Recognition Page") + "
" - + "" + tr("Help Translate!") + "

" + + "" + tr("Recognition Page") + "
" + + "" + tr("Help Translate!") + "

" + "" + tr("Support:") + "
" - + "" + tr("Report an Issue") + "
" - + "" + tr("Suggest an Improvement") + "
" - + + "" + tr("Report an Issue") + "
" + + "" + tr("Troubleshooting") + "
" + + "" + tr("F.A.Q.") + "
" )); } @@ -298,21 +310,28 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 bannedStr = tr("You are banned indefinitely."); if (!reasonStr.isEmpty()) bannedStr.append("\n\n" + reasonStr); - + QMessageBox::critical(this, tr("Error"), bannedStr); break; } - case Response::RespUsernameInvalid: - QMessageBox::critical(this, tr("Error"), tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username.")); + case Response::RespUsernameInvalid: { + QMessageBox::critical(this, tr("Error"), extractInvalidUsernameMessage(reasonStr)); break; + } case Response::RespRegistrationRequired: if (QMessageBox::question(this, tr("Error"), tr("This server requires user registration. Do you want to register now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { actRegister(); } break; + case Response::RespClientIdRequired: + QMessageBox::critical(this, tr("Error"), tr("This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client.\nPlease close and reopen your client to try again.")); + break; + case Response::RespContextError: + QMessageBox::critical(this, tr("Error"), tr("An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider.")); + break; case Response::RespAccountNotActivated: { bool ok = false; - QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\n You need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok); + QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\nYou need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok); if(ok && !token.isEmpty()) { client->activateToServer(token); @@ -322,12 +341,47 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32 break; } default: - QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast(r))); + QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast(r)) + tr("\nThis usually means that your client version is out of date, and the server sent a reply your client doesn't understand.")); break; } actConnect(); } +QString MainWindow::extractInvalidUsernameMessage(QString & in) +{ + QString out = tr("Invalid username.") + "
"; + QStringList rules = in.split(QChar('|')); + if (rules.size() == 7) + { + out += tr("Your username must respect these rules:") + "
    "; + + out += "
  • " + tr("is %1 - %2 characters long").arg(rules.at(0)).arg(rules.at(1)) + "
  • "; + out += "
  • " + tr("can %1 contain lowercase characters").arg((rules.at(2).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + out += "
  • " + tr("can %1 contain uppercase characters").arg((rules.at(3).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + out += "
  • " + tr("can %1 contain numeric characters").arg((rules.at(4).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + + if (rules.at(6).size() > 0) + { + out += "
  • " + tr("can contain the following punctuation: %1").arg( + #if QT_VERSION < 0x050000 + Qt::escape(rules.at(6)) + #else + rules.at(6).toHtmlEscaped() + #endif + ) + "
  • "; + } + + out += "
  • " + tr("first character can %1 be a punctuation mark").arg((rules.at(5).toInt() > 0) ? "" : tr("NOT")) + "
  • "; + out += "
"; + } + else + { + out += tr("You may only use A-Z, a-z, 0-9, _, ., and - in your username."); + } + + return out; +} + void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quint32 endTime) { switch (r) { @@ -354,18 +408,19 @@ void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quin bannedStr = tr("You are banned indefinitely."); if (!reasonStr.isEmpty()) bannedStr.append("\n\n" + reasonStr); - + QMessageBox::critical(this, tr("Error"), bannedStr); break; } - case Response::RespUsernameInvalid: - QMessageBox::critical(this, tr("Error"), tr("Invalid username.\nYou may only use A-Z, a-z, 0-9, _, ., and - in your username.")); + case Response::RespUsernameInvalid: { + QMessageBox::critical(this, tr("Error"), extractInvalidUsernameMessage(reasonStr)); break; + } case Response::RespRegistrationFailed: QMessageBox::critical(this, tr("Error"), tr("Registration failed for a technical problem on the server.")); break; default: - QMessageBox::critical(this, tr("Error"), tr("Unknown login error: %1").arg(static_cast(r))); + QMessageBox::critical(this, tr("Error"), tr("Unknown registration error: %1").arg(static_cast(r)) + tr("\nThis usually means that your client version is out of date, and the server sent a reply your client doesn't understand.")); } actRegister(); } @@ -398,7 +453,7 @@ void MainWindow::setClientStatusTitle() case StatusRegistering: setWindowTitle(appName + " - " + tr("Registering to %1 as %2...").arg(client->peerName()).arg(client->getUserName())); break; case StatusDisconnected: setWindowTitle(appName + " - " + tr("Disconnected")); break; case StatusLoggingIn: setWindowTitle(appName + " - " + tr("Connected, logging in at %1").arg(client->peerName())); break; - case StatusLoggedIn: setWindowTitle(appName + " - " + tr("Logged in as %1 at %2").arg(client->getUserName()).arg(client->peerName())); break; + case StatusLoggedIn: setWindowTitle(client->getUserName() + "@" + client->peerName()); break; default: setWindowTitle(appName); } } @@ -406,27 +461,26 @@ void MainWindow::setClientStatusTitle() void MainWindow::retranslateUi() { setClientStatusTitle(); - + aConnect->setText(tr("&Connect...")); aDisconnect->setText(tr("&Disconnect")); aSinglePlayer->setText(tr("Start &local game...")); aWatchReplay->setText(tr("&Watch replay...")); aDeckEditor->setText(tr("&Deck editor")); aFullScreen->setText(tr("&Full screen")); - aFullScreen->setShortcut(QKeySequence("Ctrl+F")); aRegister->setText(tr("&Register to server...")); aSettings->setText(tr("&Settings...")); aExit->setText(tr("&Exit")); - + #if defined(__APPLE__) /* For OSX */ cockatriceMenu->setTitle(tr("A&ctions")); #else cockatriceMenu->setTitle(tr("&Cockatrice")); #endif + aAbout->setText(tr("&About Cockatrice")); helpMenu->setTitle(tr("&Help")); aCheckCardUpdates->setText(tr("Check for card updates...")); - tabSupervisor->retranslateUi(); } @@ -452,7 +506,7 @@ void MainWindow::createActions() connect(aSettings, SIGNAL(triggered()), this, SLOT(actSettings())); aExit = new QAction(this); connect(aExit, SIGNAL(triggered()), this, SLOT(actExit())); - + aAbout = new QAction(this); connect(aAbout, SIGNAL(triggered()), this, SLOT(actAbout())); @@ -493,7 +547,7 @@ void MainWindow::createMenus() cockatriceMenu->addAction(aCheckCardUpdates); cockatriceMenu->addSeparator(); cockatriceMenu->addAction(aExit); - + helpMenu = menuBar()->addMenu(QString()); helpMenu->addAction(aAbout); } @@ -526,17 +580,17 @@ MainWindow::MainWindow(QWidget *parent) createActions(); createMenus(); - + tabSupervisor = new TabSupervisor(client); connect(tabSupervisor, SIGNAL(setMenu(QList)), this, SLOT(updateTabMenu(QList))); connect(tabSupervisor, SIGNAL(localGameEnded()), this, SLOT(localGameEnded())); connect(tabSupervisor, SIGNAL(maximize()), this, SLOT(maximize())); - tabSupervisor->addDeckEditorTab(0); - + tabSupervisor->addDeckEditorTab(0); + setCentralWidget(tabSupervisor); retranslateUi(); - + resize(900, 700); restoreGeometry(settingsCache->getMainWindowGeometry()); aFullScreen->setChecked(windowState() & Qt::WindowFullScreen); @@ -545,6 +599,9 @@ MainWindow::MainWindow(QWidget *parent) createTrayActions(); createTrayIcon(); } + + connect(&settingsCache->shortcuts(), SIGNAL(shortCutchanged()),this,SLOT(refreshShortcuts())); + refreshShortcuts(); } MainWindow::~MainWindow() @@ -558,7 +615,7 @@ MainWindow::~MainWindow() void MainWindow::createTrayIcon() { QMenu *trayIconMenu = new QMenu(this); trayIconMenu->addAction(closeAction); - + trayIcon = new QSystemTrayIcon(this); trayIcon->setContextMenu(trayIconMenu); trayIcon->setIcon(QIcon("theme:cockatrice.svg")); @@ -668,7 +725,7 @@ void MainWindow::actCheckCardUpdates() binaryName = getCardUpdaterBinaryName() + ".exe"; #else binaryName = getCardUpdaterBinaryName(); -#endif +#endif if(dir.exists(binaryName)) updaterCmd = dir.absoluteFilePath(binaryName); @@ -724,3 +781,17 @@ void MainWindow::cardUpdateFinished(int, QProcess::ExitStatus) // this will force a database reload settingsCache->setCardDatabasePath(settingsCache->getCardDatabasePath()); } + +void MainWindow::refreshShortcuts() +{ + aConnect->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aConnect")); + aDisconnect->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aDisconnect")); + aSinglePlayer->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aSinglePlayer")); + aWatchReplay->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aWatchReplay")); + aDeckEditor->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aDeckEditor")); + aFullScreen->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aFullScreen")); + aRegister->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aRegister")); + aSettings->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aSettings")); + aExit->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aExit")); + aCheckCardUpdates->setShortcuts(settingsCache->shortcuts().getShortcut("MainWindow/aCheckCardUpdates")); +} diff --git a/cockatrice/src/window_main.h b/cockatrice/src/window_main.h index 57b9e780..5a12676e 100644 --- a/cockatrice/src/window_main.h +++ b/cockatrice/src/window_main.h @@ -73,6 +73,7 @@ private slots: void actCheckCardUpdates(); void cardUpdateError(QProcess::ProcessError err); void cardUpdateFinished(int exitCode, QProcess::ExitStatus exitStatus); + void refreshShortcuts(); private: static const QString appName; void setClientStatusTitle(); @@ -109,6 +110,7 @@ public: protected: void closeEvent(QCloseEvent *event); void changeEvent(QEvent *event); + QString extractInvalidUsernameMessage(QString & in); }; #endif diff --git a/cockatrice/src/window_sets.cpp b/cockatrice/src/window_sets.cpp index d529ad06..4668c55d 100644 --- a/cockatrice/src/window_sets.cpp +++ b/cockatrice/src/window_sets.cpp @@ -92,7 +92,7 @@ WndSets::WndSets(QWidget *parent) QLabel *labNotes = new QLabel; - labNotes->setText("" + tr("hints:") + "" + "
  • " + tr("Enable the sets that you want to have available in the deck editor") + "
  • " + tr("Move sets around to change their order, or click on a column header to sort sets on that field") + "
  • " + tr("Sets order decides the source that will be used when loading images for a specific card") + "
  • " + tr("Disabled sets will still be used for loading images") + "
"); + labNotes->setText("" + tr("hints:") + "" + "
  • " + tr("Enable the sets that you want to have available in the deck editor") + "
  • " + tr("Move sets around to change their order, or click on a column header to sort sets on that field") + "
  • " + tr("Sets order decides the source that will be used when loading images for a specific card") + "
  • " + tr("Disabled sets will be used for loading images only if all the enabled sets failed") + "
"); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(actSave())); @@ -123,6 +123,7 @@ WndSets::~WndSets() void WndSets::actSave() { model->save(db); + db->clearPixmapCache(); QMessageBox::information(this, tr("Success"), tr("The sets database has been saved successfully.")); close(); } diff --git a/cockatrice/translations/cockatrice_cs.ts b/cockatrice/translations/cockatrice_cs.ts index d34608e7..b0289f17 100644 --- a/cockatrice/translations/cockatrice_cs.ts +++ b/cockatrice/translations/cockatrice_cs.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... &Nastavit žeton... - + Set counter Nastavit žeton - + New value for counter '%1': Nová hodnota pro žeton '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Pozadí zón - + Hand background: Pozadí ruky - + Stack background: Pozadí stacku - + Table background: Pozadí stolu - + Player info background: Pozadí sekce s informacemi o hráči - + Card back: Pozadí karet - + Card rendering Vykreslování karet - + Display card names on cards having a picture Zobrazit jména karet na kartách s obrázky - + Scale cards on mouse over - + Hand layout Rozvržení ruky - + Display hand horizontally (wastes space) Zobrazit ruku horizontálně (plýtvá místem) - + Enable left justification - + Table grid layout Rozložení herní mřížky - + Invert vertical coordinate Převrátit vertikální souřadnice - + Minimum player count for multi-column layout: Minimální počet hráčů pro víceřádkové rozvržení: - - - - - + + + + + Choose path Vyberte cestu @@ -107,97 +107,102 @@ BanDialog - + ban &user name - + ban &IP address - - Ban type - Typ banu - - - - &permanent ban - - - - - &temporary ban + + ban client I&D + Ban type + Typ banu + + + + &permanent ban + + + + + &temporary ban + + + + &Days: - + &Hours: - + &Minutes: - + Duration of the ban Trvání banu - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Uveďte prosím důvod pro ban. Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. - + Please enter the reason for the ban that will be visible to the banned person. Uveďte prosám důvod pro ban, který uvidí banovaná osoba. - + &OK &OK - + &Cancel - + Ban user from server Zabanovat uživatele ze serveru - + Error Error - - You have to select a name-based or IP-based ban, or both. - Musíte vybrat ban na jméno nebo ban na IP adresu. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí.S/O + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: Jméno: - + Mana cost: Sesílací cena: - + + Color(s): + + + + Card type: Typ karty: - + P / T: S / O: - + Loyalty: Loajalita: @@ -276,27 +304,32 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí.Zobrazit všechny informace - + Name: Jméno: - + Mana cost: Sesílací cena: - + + Color(s): + + + + Card type: Typ karty: - + P / T: S / O: - + Loyalty: @@ -560,12 +593,12 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. DeckEditorSettingsPage - + Nothing is here... yet - + General Obecné @@ -573,17 +606,17 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. DeckListModel - + Number Počet - + Card Karta - + Price Cena @@ -605,42 +638,42 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. DeckViewContainer - - Load &local deck - Nahrát &lokální balíček + + Load local deck + - - Load d&eck from server - Nahrát &balíček ze serveru + + Load deck from server + - + Ready to s&tart Připraven ke &hře - + S&ideboard unlocked - + S&ideboard locked - + Load deck Nahrát balíček - + Error - + The selected file could not be loaded. Vybraný soubor se nepodařilo načíst. @@ -686,37 +719,52 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Hostitel: - + + Enter host name + + + + &Port: &Port: - + Player &name: Jméno &hráče: - + P&assword: H&eslo: - + &Save password - + A&uto connect at start - + Connect to server Připojit k serveru @@ -724,82 +772,92 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. DlgCreateGame - + &Description: &Popis: - + &Password: &Heslo: - + P&layers: H&ráči: - + + Re&member settings + + + + Game type Formát - + Only &buddies can join Jen pro &přátele - + Only &registered users can join Jen pro &registrované - + Joining restrictions Omezení připojení - + &Spectators can watch - + Spectators &need a password to watch - + Spectators can see &hands - + Spectators can &chat Diváci mohou &chatovat - + Spectators Diváci - + + &Clear + + + + Create game Vytvořit hru - + Game information Informace o hře - + Error Chyba - + Server error. Chyba serveru. @@ -807,96 +865,169 @@ Toto je uloženo pouze pro moderátory a banovaná osoba to neuvidí. DlgCreateToken - + &Name: &Jméno: - + Token Token - + C&olor: B&arva: - + white bílá - + blue modrá - + black černá - + red červená - + green zelená - + multicolor vícebarevný - + colorless bezbarvý - + &P/T: &S/O: - + &Annotation: &Poznámka: - + &Destroy token when it leaves the table &Token se při odchodu z bojiště zničí - + Token data Data tokenů - + Show &all tokens - + Show tokens from this &deck - + Choose token from list Vybrat token ze seznamu. - + Create token Vytvořit token + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Obnovit @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Nahrát balíček ze schránky - + + Error Chyba - + + Invalid deck list. Neplatný formát balíčku. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Nahrát balíček + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error Chyba - + Unknown Error loading card database Neznámý error při nahrávání databáze karet. - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1100,7 +1375,7 @@ Je možné že bude nutné znovu spustit Oracle pro obnovení databáze karet. Chtěl/a by jste změnit nastavení lokace databáze? - + Your card database version is too old. This can cause problems loading card information or images @@ -1111,21 +1386,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1134,7 +1409,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1143,63 +1418,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Cesta k adresáři s balíčky je neplatná. Chcete se vrátit a nastavit správnou? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Cesta k adresáři s obrázky je neplatná. Chcete se vrátit a nastavit správnou? - + Settings Nastavení - + General Obecné - + Appearance Vzhled - + User Interface - + Deck Editor - + Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - V&ytvořit - - - - &Join - &Připojit - @@ -1209,7 +1479,7 @@ Would you like to change your database location setting? - + Error Chyba @@ -1254,37 +1524,47 @@ Would you like to change your database location setting? Zakladatel hry vás ignoruje. - + Join game Připojit ke hře - + Password: Heslo: - + Please join the respective room first. - + Games Hry - + &Filter games - + C&lear filter - + + C&reate + + + + + &Join + + + + J&oin as spectator P&řipojit se jako divák @@ -1401,97 +1681,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path Vyberte cestu - + Success - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Personal settings Osobní nastavení - + Language: Jazyk: - + Download card pictures on the fly Stahovat obrázky karet za běhu - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths Cesty - + Decks directory: Adresář s balíčky: - + Replays directory: - + Pictures directory: Adresář s obrázky: - + Card database: - + Token database: - + Picture cache size: - - + + English Česky (Czech) @@ -1499,361 +1789,533 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Z vaší adresy jde mnoho současných připojení. - + Scheduled server shutdown. - + Banned by moderator - + Expected end time: %1 - + This ban lasts indefinitely. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Připojení uzavřeno - + The server has terminated your connection. Reason: %1 Server přerušil spojení. Důvod: %1 - + Scheduled server shutdown - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 - + Number of players Počet hráčů - + Please enter the number of players. Vložte počet hráčů. - - + + Player %1 Hráč %1 - + Load replay - + About Cockatrice O Cockatrice - + Version %1 Verze %1 - + Translators: Překlad: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error Chyba - + Server timeout Vypršel časový limit - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. S tímto uživatelským jménem jste již připojeni. Přerušte spojení a znovu se přihlašte. - + + You are banned until %1. - + + You are banned indefinitely. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 Chyba socketu: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Snažíte se připojit na zastaralý server. Prosíme, stáhněte si nižší verzi Cockatrice, nebo se připojte k odpovídajícímu serveru. Lokální verze je %1, verze serveru je %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Váš klient je zastaralý. Prosíme, aktualizujte Cockatrice na vyšší verzi. Lokální verze je %1, verze serveru je %2. - + Connecting to %1... Připojování k %1... - + + Registering to %1 as %2... + + + + Disconnected Odpojeno - + Connected, logging in at %1 - - Logged in as %1 at %2 - - - - + &Connect... &Připojit... - + &Disconnect &Odpojit - + Start &local game... Spustit &lokální hru... - + &Watch replay... - + &Deck editor &Editor balíčků - + &Full screen &Celá obrazovka - + + &Register to server... + + + + &Settings... &Nastavení... - - + + &Exit &Konec - + A&ctions - + &Cockatrice &Cockatrice - + &About Cockatrice &O Cockatrice - + &Help &Nápověda - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2092,62 +2554,62 @@ Lokální verze je %1, verze serveru je %2. z exilnutých karet - + the bottom card of %1's library - + the bottom card of his library spodní kartu knihovny - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library ze spodku knihovny - + from the bottom of her library - + the top card of %1's library - + the top card of his library vrchní kartu knihovny - + the top card of her library - + from the top of %1's library - + from the top of his library z vršku knihovny - + from the top of her library @@ -2550,304 +3012,304 @@ Lokální verze je %1, verze serveru je %2. - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - + %1 sets %2 to not untap normally. male - + %1 sets %2 to untap normally. female - + %1 sets %2 to untap normally. male - + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male + + + %1 is looking at %2. + female + + %1 is looking at %2. - female + male + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + - - %1 is looking at %2. - male - - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - - - %1 is looking at the top %n card(s) %2. - male - + %1 stops looking at %2. - female - - - - - %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male + + + %1 reveals %2. + female + + %1 reveals %2. - female - - - - - %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - + %1 randomly reveals %2%3 to %4. p1 male, p2 female - + %1 randomly reveals %2%3 to %4. p1 male, p2 male - + %1 randomly reveals %2%3. female - + %1 randomly reveals %2%3. male - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - + a card kartu @@ -2910,12 +3372,12 @@ Lokální verze je %1, verze serveru je %2. - + ending phase Konec kola - + untap step Odtapovací fáze @@ -3025,64 +3487,64 @@ Lokální verze je %1, verze serveru je %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step Upkeep - + draw step Lízací fáze - + first main phase První hlavní fáze - + beginning of combat step Začátek bojové fáze - + declare attackers step Oznámení útočníků - + declare blockers step Oznámení blokujících - + combat damage step Udělení bojového zranění - + end of combat step Konec bojové fáze - + second main phase Druhá hlavní fáze - + It is now the %1. Nyní je %1. @@ -3090,62 +3552,74 @@ Lokální verze je %1, verze serveru je %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message Přidat zprávu - + Message: Zpráva: @@ -3211,336 +3685,337 @@ Lokální verze je %1, verze serveru je %2. Player - + &View library &Zobrazit knihovnu - + Move top cards to &graveyard... Přesunout vrchní karty do &hřbitova... - + View &top cards of library... Zobrazit &vrchní karty knihovny... - + &View graveyard &Zobrazit hřbitov - + &View sideboard &Zobrazit sideboard - + Player "%1" Hráč "%1" - - - + + + + &Hand &Ruka - + &Reveal hand to... - + Reveal r&andom card to... - + &Library &Knihovna - - - + + + &Graveyard &Hřbitov - + &Sideboard &Sideboard - + Red - + Yellow - + Green - + View top cards of library Zobrazit vrchní karty knihovny - + Number of cards: Počet karet: - + &Draw card &Líznout kartu - + Reveal top cards of library - + Number of cards: (max. %1) - + &View exile &Zobrazit exilnuté karty - - - + + + &Exile &Exilnuté karty - + Reveal t&op cards to... - + D&raw cards... L&íznout karty... - + Take &mulligan &Mulliganovat - + &Shuffle &Zamíchat - + &Counters &Žetony - + &Untap all permanents &Odtapnout všechny permanenty - + R&oll die... H&odit kostkou... - + &Create token... &Vytvořit token... - + C&reate another token V&ytvořit další token - + S&ay &Chat - + &Move hand to... - - - - + + + + &Top of library - - - - + + + + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card - + O&pen deck in deck editor - + &Undo last draw &Vrátit zpět poslední líznutí - + Play top card &face down - + Move top cards to &exile... &Exilnout vrchní karty... - + Put top card on &bottom Dát kartu na &spodek - + Put bottom card &in graveyard - + Cr&eate predefined token - + C&ard K&arta - + &All players &Všem hráčům - + &Play - + &Hide - + Play &Face Down - + &Tap - + &Untap - + Toggle &normal untapping - + &Flip - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Increase power - + &Decrease power - + I&ncrease toughness - + D&ecrease toughness @@ -3550,22 +4025,22 @@ Lokální verze je %1, verze serveru je %2. - + Dec&rease power and toughness - + Set &power and toughness... - + &Set annotation... - + &Add counter (%1) @@ -3575,103 +4050,108 @@ Lokální verze je %1, verze serveru je %2. - + &Set counters (%1)... - + Draw cards Líznout karty - - - - + + + + Number: Počet: - + Move top cards to grave Přesunout vrchní karty do hřbitova - + Move top cards to exile Exilnout vrchní karty - + Roll die Hodit kostkou - + Number of sides: Počet stran: - + Set power/toughness Nastavit sílu/odolnost - + Please enter the new PT: Vložte novou odolnost/sílu: - + Set annotation Nastavit poznámku - + Please enter the new annotation: Vložte novou poznámku: - + Set counters Nastavit žetony + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3679,7 +4159,7 @@ Lokální verze je %1, verze serveru je %2. QObject - + Cockatrice replays (*.cor) @@ -3769,14 +4249,43 @@ Lokální verze je %1, verze serveru je %2. + Permissions + + + + Players Hráči - + Games Hry + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3819,7 +4328,7 @@ Lokální verze je %1, verze serveru je %2. - + Shut down server @@ -3827,37 +4336,37 @@ Lokální verze je %1, verze serveru je %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3865,42 +4374,47 @@ Lokální verze je %1, verze serveru je %2. TabAdmin - + Update server &message Aktualizovat &zprávu serveru - + &Shut down server - + + &Reload configuration + + + + Server administration functions Administrační funkce serveru - + &Unlock functions &Odemknout funkce - + &Lock functions &Zamknout funkce - + Unlock administration functions Odemknout administrační funkce - + Do you really want to unlock the administration functions? Opravdu chcete odemknout administrační funkce? - + Administration @@ -3908,179 +4422,219 @@ Lokální verze je %1, verze serveru je %2. TabDeckEditor - + &Print deck... - + &Close - + &Edit sets... - - &Clear search + + Filters - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: - + &Comments: - + Hash: - + &New deck - + &Load deck... - + &Save deck - + Save deck &as... - + Load deck from cl&ipboard... - + Save deck to clip&board - + &Analyze deck on deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck - + Add card to &sideboard - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row - + &Increment number - + &Decrement number - + Edit &tokens... - + Deck: %1 - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4178,95 +4732,100 @@ Prosím vložte jméno: TabGame - + &Phases &Fáze - + &Game &Hra - + Next &phase Další &fáze - + Next &turn Další &kolo - + &Remove all local arrows &Odstranit všechny lokální šipky - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information - + &Concede &Ukončit hru - + &Leave game &Opustit hru - + C&lose replay - + &Say: &Chat: - + Concede Ukončit hru - + Are you sure you want to concede this game? Opravdu chcete ukončit tuto hru? - + Leave game Opustit hru - + Are you sure you want to leave this game? Opravdu chcete opustit tuto hru? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 + + REPLAY - - - Game %1: %2 - Hra %1: %2 - TabMessage @@ -4309,54 +4868,54 @@ Prosím vložte jméno: TabReplays - + Local file system - + Server replay storage - + Watch replay - - + + Delete - + Download replay - + Toggle expiration lock - + Delete local file - + Are you sure you want to delete "%1"? - + Delete remote replay - + Are you sure you want to delete the replay of game %1? @@ -4369,47 +4928,47 @@ Prosím vložte jméno: TabRoom - + &Say: &Chat: - + Chat Chatovat - + &Room &Místnost - + &Leave room &Opustit místnost - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. Píšete příliš intenzivně. Počkejte pár sekund. @@ -4425,15 +4984,25 @@ Prosím vložte jméno: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4456,169 +5025,301 @@ Prosím vložte jméno: UserContextMenu - + User &details - + Private &chat - + Show this user's &games - + Add to &buddy list - + Remove from &buddy list - + Add to &ignore list - + Remove from &ignore list - + Kick from &game - + Ban from &server - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Informace o uživateli - + Real name: Pravé jméno: - - Gender: + + Pronouns: - + Location: Místo: - + User level: Úroveň: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator Administrátor - + Moderator - + Registered user Registrovaný - - + + Unregistered user Neregistrovaný - + Unknown - + Year - + Years - + Day - + Days + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Obecné - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) &Pro zahraní karty je třeba dvojklik - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings Animace - + &Tap/untap animation &Animace tapnutí/odtapnutí @@ -4626,22 +5327,22 @@ Prosím vložte jméno: UserList - + Users connected to server: %1 - + Users in this room: %1 Uživatelů v této místnosti: %1 - + Buddies online: %1 / %2 Přátelé online: %1 / %2 - + Ignored users online: %1 / %2 Ignorovaných online: %1 / %2 @@ -4663,27 +5364,44 @@ Prosím vložte jméno: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4692,12 +5410,12 @@ Disabled sets will still be used for loading images. Upravit edice - + Success - + The sets database has been saved successfully. @@ -4725,4 +5443,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_de.ts b/cockatrice/translations/cockatrice_de.ts index 3df17867..9aa31529 100644 --- a/cockatrice/translations/cockatrice_de.ts +++ b/cockatrice/translations/cockatrice_de.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... - Zähler &setzen... + Marken &setzen... - + Set counter Zähler setzen - + New value for counter '%1': Neuer Wert für den Zähler: '%1' @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Hintergrundbilder für Kartenzonen - + Hand background: Hintergrund für die Hand: - + Stack background: Hintergrund für den Stapel: - + Table background: Hintergrund für das Spielfeld: - + Player info background: Hintergrund für die Spielerinfo: - + Card back: Kartenrückseite: - + Card rendering Kartendarstellung - + Display card names on cards having a picture Kartennamen darstellen auch bei Karten, die Bilder haben - + Scale cards on mouse over Karten beim Darüberfahren mit der Maus vergrößern - + Hand layout Handdarstellung - + Display hand horizontally (wastes space) Hand horizontal anzeigen (verschwendet Platz) - + Enable left justification Linksbündige Ausrichtung aktivieren - + Table grid layout Spielfeldraster - + Invert vertical coordinate Vertikale Koordinate umkehren - + Minimum player count for multi-column layout: Mindestspieleranzahl für mehrspaltige Anordnung: - - - - - + + + + + Choose path Dateipfad auswählen @@ -107,97 +107,102 @@ BanDialog - + ban &user name &Benutzername - + ban &IP address &IP-Adresse - + + ban client I&D + Client I&D bannen + + + Ban type Art des Banns - + &permanent ban &permanenter Bann - + &temporary ban &temporärer Bann - + &Days: &Tage: - + &Hours: &Stunden: - + &Minutes: &Minuten: - + Duration of the ban Länge des Banns - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Bitte geben Sie den Grund für den Bann ein. Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nicht gesehen werden. - + Please enter the reason for the ban that will be visible to the banned person. Bitte geben Sie den Grund ein, den die gebannte Person sehen kann. - + &OK &OK - + &Cancel &Abbrechen - + Ban user from server Benutzer vom Server bannen - + Error Fehler - - You have to select a name-based or IP-based ban, or both. - Bitte wählen Sie einen Namens- oder Adressbann. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + Bitte wählen Sie einen Namens-, Adress- oder Client-ID-Bann oder eine Kombination aus diesen. CardDatabase - + New sets found Neue Editionen gefunden - + %1 new set(s) have been found in the card database. Do you want to enable them? %1 neue Edition(en) wurden in der Kartendatenbank gefunden. Sollen diese aktiviert werden? @@ -230,30 +235,53 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic S/W + + CardFrame + + + Image + Bild + + + + Description + Beschreibung + + + + Both + Beide + + CardInfoText - + Name: Name: - + Mana cost: Manakosten: - + + Color(s): + Farbe(n): + + + Card type: Kartentyp: - + P / T: S/W: - + Loyalty: Loyalität: @@ -276,27 +304,32 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic Alles anzeigen - + Name: Name: - + Mana cost: Manakosten: - + + Color(s): + Farbe(n) + + + Card type: Kartentyp: - + P / T: S/W: - + Loyalty: Loyalität: @@ -560,12 +593,12 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic DeckEditorSettingsPage - + Nothing is here... yet Hier gibt es noch nichts. - + General Allgemeines @@ -573,17 +606,17 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic DeckListModel - + Number Nummer - + Card Karte - + Price Preis @@ -605,42 +638,42 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic DeckViewContainer - - Load &local deck - &Lokales Deck laden + + Load local deck + Lokales Deck laden - - Load d&eck from server - Deck vom Server l&aden + + Load deck from server + Deck von Server laden - + Ready to s&tart Bereit zum S&tarten - + S&ideboard unlocked S&ideboard entsperrt - + S&ideboard locked S&ideboard gesperrt - + Load deck Deck laden - + Error Fehler - + The selected file could not be loaded. Die gewählte Datei konnte nicht geladen werden. @@ -686,37 +719,52 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic DlgConnect - + + Previous Host + Vorheriger Host + + + + New Host + Neuer Host + + + &Host: &Server: - + + Enter host name + Host-Name eingeben + + + &Port: &Port: - + Player &name: Spieler&name: - + P&assword: P&asswort: - + &Save password Passwort &speichern - + A&uto connect at start beim Start automatisch verbinden - + Connect to server Verbinde zum Server @@ -724,82 +772,92 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic DlgCreateGame - + &Description: &Beschreibung: - + &Password: &Passwort: - + P&layers: &Spieler: - + + Re&member settings + + + + Game type Spieltyp - + Only &buddies can join Nur &Freunde können teilnehmen - + Only &registered users can join Nur &registrierte Benutzer können teilnehmen - + Joining restrictions Teilnahmebedingungen - + &Spectators can watch Zuschauer erlauben - + Spectators &need a password to watch Zuschauer benötigen das Passwort - + Spectators can see &hands Zuschauer sehen die Hände - + Spectators can &chat Zuschauer können &chatten - + Spectators Zuschauer - + + &Clear + Löschen + + + Create game Spiel erstellen - + Game information &Spielinformationen - + Error Fehler - + Server error. Serverfehler. @@ -807,96 +865,170 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic DlgCreateToken - + &Name: &Name: - + Token Spielstein - + C&olor: &Farbe: - + white weiß - + blue blau - + black schwarz - + red rot - + green grün - + multicolor mehrfarbig - + colorless farblos - + &P/T: &Kampfwerte: - + &Annotation: &Hinweis: - + &Destroy token when it leaves the table Spielstein &zerstören, wenn er das Spielfeld verlässt - + Token data Spielstein-Daten - + Show &all tokens &Alle möglichen Spielsteine zeigen - + Show tokens from this &deck Spielsteine dieses &Decks zeigen - + Choose token from list Spielstein aus Liste auswählen - + Create token Spielstein erstellen + + DlgEditAvatar + + + + No image chosen. + Kein Bild gewählt. + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + Um deinen Avatar zu wechseln, wähle ein neues Bild. +Um deinen derzeitigen Avatar zu entfernen, bestätige, ohne ein neues Bild zu wählen. + + + + Browse... + Durchsuchen... + + + + Change avatar + Avatar wechseln + + + + Open Image + Bild öffnen + + + + Image Files (*.png *.jpg *.bmp) + Bild-Dateien (*.png *.jpg *.bmp) + + + + Invalid image chosen. + Ungültiges Bild gewählt. + + + + DlgEditPassword + + + Old password: + Altes Passwort: + + + + New password: + Neues Passwort: + + + + Confirm new password: + Bestätige neues Passwort: + + + + Change password + Passwort ändern + + + + Error + Fehler + + + + The new passwords don't match. + Die neuen Passwörter stimmen nicht überein. + + DlgEditTokens @@ -989,7 +1121,56 @@ Dies wird nur für Moderatoren gespeichert und kann von der gebannten Person nic The chosen name conflicts with an existing card or token. Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. - + Es existiert bereits eine Karte oder ein Spielstein mit dem gewählten Namen. +Aktivieren Sie die Edition „TK“ im „Editionen bearbeiten...“-Menu, damit Spielsteine korrekt angezeigt werden. + + + + DlgEditUser + + + Email: + Email: + + + + Pronouns: + Pronomen: + + + + Neutral + Neutrum + + + + Masculine + Maskulinum + + + + Feminine + Femininum + + + + Country: + Land: + + + + Undefined + Undefiniert + + + + Real name: + Wahrer Name: + + + + Edit user profile + Benutzerprofil bearbeiten @@ -1043,7 +1224,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Aktualisieren @@ -1053,12 +1234,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Deck aus der Zwischenablage laden - + + Error Fehler - + + Invalid deck list. Ungültige Deckliste. @@ -1071,22 +1254,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Deck laden + + DlgRegister + + + &Host: + &Host: + + + + &Port: + &Port: + + + + Player &name: + Spieler &Name: + + + + P&assword: + P&asswort + + + + Password (again): + Passwort (Wiederholung) + + + + Email: + Email: + + + + Email (again): + Email (Wiederholung) + + + + Pronouns: + Pronomen: + + + + Neutral + Neutrum + + + + Masculine + Maskulinum + + + + Feminine + Femininum + + + + Undefined + Undefiniert + + + + + Registration Warning + Registrierungswarnung + + + + Your passwords do not match, please try again. + Ihre Passwörter stimmen nicht überein, bitte versuchen Sie es erneut. + + + + Your email addresses do not match, please try again. + Ihre Mailadressen stimmen nicht überein, bitte versuchen Sie es erneut. + + + + Country: + Land: + + + + Real name: + Wahrer Name: + + + + Register to server + Auf Server registrieren + + DlgSettings - - - + + + Error Fehler - + Unknown Error loading card database Unbekannter Fehler beim Laden der Kartendatenbank - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1380,7 @@ Sie müssen Oracle unter Umständen nochmals ausführen um Ihre Kartendatenbank Möchten Sie Ihren Speicherort der Datenbank aktualisieren?? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1397,7 @@ Normalerweise kann dies durch einen erneuten Start von Oracle, um die Kartendate Möchten Sie Ihren Speicherort der Datenbank aktualisieren? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1406,7 @@ Would you like to change your database location setting? Möchten Sie Ihren Speicherort der Datenbank aktualisieren? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1415,7 @@ Would you like to change your database location setting? Möchten Sie Ihren Speicherort der Datenbank aktualisieren? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1151,7 +1428,7 @@ Bitte erstellen Sie ein Ticket auf http://github.com/Cockatrice/Cockatrice/issue Möchten Sie Ihren Speicherort der Datenbank aktualisieren? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1164,63 +1441,58 @@ Bitte erstellen Sie ein Ticket auf http://github.com/Cockatrice/Cockatrice/issue Möchten Sie Ihren Speicherort der Datenbank aktualisieren? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Der Pfad zu Ihrem Deckordner ist ungültig. Möchten Sie zurückgehen und den korrekten Pfad einstellen? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Der Pfad zu Ihrem Kartenbilderordner ist ungültig. Möchten Sie zurückgehen und den korrekten Pfad einstellen? - + Settings Einstellungen - + General Allgemeines - + Appearance Erscheinungsbild - + User Interface Benutzeroberfläche - + Deck Editor Deckeditor - + Chat Chat - + Sound Töne + + + Shortcuts + Shortcuts + GameSelector - - - C&reate - E&rstellen - - - - &Join - &Beitreten - @@ -1230,7 +1502,7 @@ Möchten Sie Ihren Speicherort der Datenbank aktualisieren? - + Error Fehler @@ -1275,37 +1547,47 @@ Möchten Sie Ihren Speicherort der Datenbank aktualisieren? Der Ersteller dieses Spiels ignoriert Sie. - + Join game Spiel beitreten - + Password: Passwort: - + Please join the respective room first. Bitte betreten Sie zuerst den entsprechenden Raum. - + Games Spiele - + &Filter games Spiele &filtern - + C&lear filter Filter &zurücksetzen - + + C&reate + E&rstellen + + + + &Join + &Beitreten + + + J&oin as spectator Als Zuschauer beitreten @@ -1422,97 +1704,107 @@ Möchten Sie Ihren Speicherort der Datenbank aktualisieren? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Heruntergeladene Kartenbilder zurücksetzen/löschen - - - - - + + + + + Choose path Pfad auswählen - + Success Erfolgreich - + Downloaded card pictures have been reset. Heruntergeladene Kartenbilder wurden zurückgesetzt. - + Error Fehler - + One or more downloaded card pictures could not be cleared. Mindestens ein heruntergeladenes Kartenbild konnte nicht gelöscht werden. - + Personal settings Persönliche Einstellungen - + Language: Sprache: - + Download card pictures on the fly Kartenbilder dynamisch herunterladen - - Download high-quality card pictures - Kartenbilder in hoher Qualität herunterladen + + Download card pictures from a custom URL + Kartenbilder von einer benutzerdefinierten URL herunterladen - + + Custom Card Download URL: + Benutzerdefinierte Karten-Download-URL + + + + Linking FAQ + Linking FAQ + + + Paths Pfade - + Decks directory: Verzeichnis mit Decklisten: - + Replays directory: Verzeichnis mit aufgezeichneten Spielen: - + Pictures directory: Verzeichnis mit Bilddateien: - + Card database: Kartendatenbank: - + Token database: Spielsteindatenbank: - + Picture cache size: Cachegröße für Bilder: - - + + English Deutsch (German) @@ -1520,57 +1812,49 @@ Möchten Sie Ihren Speicherort der Datenbank aktualisieren? MainWindow - + There are too many concurrent connections from your address. Es gibt zu viele gleichzeitige Verbindungen von Ihrer Adresse. - + Scheduled server shutdown. Planmäßige Serverabschaltung. - + Banned by moderator Gebannt von einem Moderator - + Expected end time: %1 Voraussichtliches Ende: %1 - + This ban lasts indefinitely. Dieser Bann ist unbefristet. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - Ungültiger Benutzername. -Sie können nur A-Z, a-z, 0-9, _, ., und - in Ihrem Benutzernamen verwenden. - - - + Connection closed Verbindung geschlossen - + The server has terminated your connection. Reason: %1 Der Server hat Ihre Verbindung beendet. Grund: %1 - + Scheduled server shutdown Planmäßige Serverabschaltung - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1581,304 +1865,488 @@ Alle laufenden Spiele werden beendet. Grund für die Abschaltung: %1 - + Number of players Spieleranzahl - + Please enter the number of players. Bitte die Spieleranzahl eingeben: - - + + Player %1 Spieler %1 - + Load replay Aufgezeichnetes Spiel laden - + About Cockatrice Über Cockatrice - + Version %1 Version %1 - + Translators: Übersetzer: - + Project Manager: Projektmanager: - + + The server has reached its maximum user capacity, please check back later. + Der Server hat seine maximale Benutzeranzahl erreicht. Bitte versuchen Sie es später noch einmal. + + + + + Invalid username. + Ungültiger Benutzername. + + + + You have been logged out due to logging in at another location. + + + + + + Success + Erfolgreich + + + + Registration accepted. +Will now login. + Registrierung angenommen. +Login läuft. + + + + Account activation accepted. +Will now login. + Accountaktivierung angenommen. +Login läuft. + + + Past Project Managers: frühere Projektmanager: - + Developers: Entwickler: - + Our Developers unsere Entwickler - + Help Develop! Hilf bei der Entwicklung! - + Recognition Page Anerkennungsseite - + Help Translate! Hilf bei der Übersetzung! - + Support: Unterstützung: - + Report an Issue Problem melden - - Suggest an Improvement - Vorschlag machen - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error Fehler - + Server timeout Server Zeitüberschreitung - + Incorrect username or password. Please check your authentication information and try again. Falscher Benutzername oder Passwort. Bitte überprüfe deine Login Informationen und versuche es erneut. - + There is already an active session using this user name. Please close that session first and re-login. Es gibt bereits eine aktive Verbindung mit diesem Benutzernamen. Bitte schließen Sie diese Verbindung zuerst und versuchen Sie es dann erneut. - + + You are banned until %1. Sie sind gebannt bis: %1. - + + You are banned indefinitely. Sie sind auf unbestimmte Zeit gebannt. - - This server requires user registration. - Dieser Server setzt eine Anmeldung voraus. + + This server requires user registration. Do you want to register now? + Diese Server benötigt eine Benutzerregistrierung. Möchten Sie sich jetzt registrieren? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + Diese Server erfordert eine Client-ID. Enweder schlägt das Generieren einer ID ihres Clients fehl, oder Sie verwenden einen modifizierten Client. +Schließen Sie bitte Ihren Client und öffnen Sie ihn erneut. + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + Ein interner Fehler ist aufgetreten. Schließen Sie bitte Ihren Client, öffnen Sie ihn erneut und versuchen Sie es noch einmal. Falls der Fehler bestehen bleibt, aktualisieren Sie Ihren Client auf den aktuellsten Stand und kontaktieren Sie Ihren Software Provider. + + + + Account activation + Accountaktivierung + + + Unknown login error: %1 Unbekannter Login-Fehler: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + +Dies bedeutet normalerweise, dass Ihre Clientversion veraltet ist und der Server eine Antwort geschickt hat, die Ihr Client nicht versteht. + + + + Your username must respect these rules: + Ihr Benutzername muss diesen Regeln entsprechen: + + + + is %1 - %2 characters long + %1 - %2 Zeichen lang + + + + can %1 contain lowercase characters + kann %1 Kleinbuchstaben beinhalten + + + + + + + NOT + NICHT + + + + can %1 contain uppercase characters + kann %1 Großbuchstaben beinhalten + + + + can %1 contain numeric characters + kann %1 Zahlen beinhalten + + + + can contain the following punctuation: %1 + kann folgende Sonderzeichen beinhalten: %1 + + + + first character can %1 be a punctuation mark + Das erste Zeichen kann %1 ein Sonderzeichen sein + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + Sie können nur A-Z, a-z, 0-9, _, ., und - in Ihrem Benutzernamen verwenden. + + + + + + + + Registration denied + Registrierung verweigert + + + + Registration is currently disabled on this server + Momentan ist die Registrierung auf diesem Server deaktiviert + + + + There is already an existing account with the same user name. + Es existiert bereits ein Account mit dem gleichen Benutzernamen. + + + + It's mandatory to specify a valid email address when registering. + Es ist erforderlich, eine korrekte Emailadresse während der Registrierung anzugeben. + + + + Too many registration attempts from your IP address. + Zu viele Registrierungsversuche von Ihrer IP-Adresse. + + + + Password too short. + Passwort zu kurz. + + + + Registration failed for a technical problem on the server. + Registrierung aufgrund eines technischen Problems des Servers fehlgeschlagen. + + + + Unknown registration error: %1 + Unbekannter Registrierungsfehler: %1 + + + + Account activation failed + Accountaktivierung fehlgeschlagen + + + Socket error: %1 Netzwerkfehler: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Sie versuchen sich an einem veralteten Server anzumelden. Bitte verwenden Sie eine ältere Cockatrice-Version oder melden Sie sich an einem aktuellen Server an. Lokale Version ist %1, Serverversion ist %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Ihr Cockatrice-Client ist veraltet. Bitte laden Sie sich die neueste Version herunter. Lokale Version ist %1, Serverversion ist %2. - + Connecting to %1... Verbinde zu %1... - + + Registering to %1 as %2... + Registrierung bei %1 als %2... + + + Disconnected nicht verbunden - + Connected, logging in at %1 Verbunden, Anmeldung bei %1 - - Logged in as %1 at %2 - Angemeldet als %1 bei %2 - - - + &Connect... &Verbinden... - + &Disconnect Verbindung &trennen - + Start &local game... &Lokales Spiel starten... - + &Watch replay... &Aufgezeichnetes Spiel abspielen... - + &Deck editor &Deck-Editor - + &Full screen &Vollbild - + + &Register to server... + Beim Server &registrieren... + + + &Settings... &Einstellungen... - - + + &Exit &Beenden - + A&ctions Aktionen - + &Cockatrice &Cockatrice - + &About Cockatrice &Über Cockatrice - + &Help &Hilfe - - Check card updates... - + + Check for card updates... + Nach Kartenaktualisierungen suchen... - - + + A card database update is already running. + Eine Datenbankaktualisierung wird bereits durchgeführt. + + + + Unable to run the card database updater: + Kartendatenbankaktualisierung nicht ausführbar: + + + + The card database updater exited with an error: %1 + Die Kartendatenbankaktualisierung brach mit einem Fehler ab: %1 + + + + Update completed successfully. Cockatrice will now reload the card database. + Aktualisierung erfolgreich abgeschlossen. Kartendatenbank wird neu geladen. + + + + Information Information - - A card update is already ongoing. - + + Troubleshooting + Fehlersuche - - Unable to run the card updater: - + + F.A.Q. + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + Ihr Account wurde noch nicht aktiviert. +Sie müssen den Aktivierungstoken aus der Aktivierungsemail verwenden + + + failed to start. - + Start fehlgeschlagen. - + crashed. - + abgestürzt. - + timed out. - + Zeitüberschreitung. - + write error. - + Fehler beim Schreiben. - + read error. - + Fehler beim Lesen. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + Unbekannter Fehler. @@ -2118,62 +2586,62 @@ Lokale Version ist %1, Serverversion ist %2. aus dem Exil - + the bottom card of %1's library die unterste Karte von %1s Bibliothek - + the bottom card of his library die unterste Karte seiner Bibliothek - + the bottom card of her library die unterste Karte ihrer Bibliothek - + from the bottom of %1's library von der Unterseite von %1s Bibliothek - + from the bottom of his library , die unterste Karte seiner Bibliothek, - + from the bottom of her library , die unterste Karte ihrer Bibliothek, - + the top card of %1's library die oberste Karte von %1s Bibliothek - + the top card of his library die oberste Karte seiner Bibliothek - + the top card of her library die oberste Karte ihrer Bibliothek - + from the top of %1's library oben von %1s Bibliothek - + from the top of his library , die oberste Karte seiner Bibliothek, - + from the top of her library , die oberste Karte ihrer Bibliothek, @@ -2576,304 +3044,304 @@ Lokale Version ist %1, Serverversion ist %2. %1 entfernt eine %2 Marke von %3 (jetzt %4).%1 entfernt %n %2 Marken von %3 (jetzt %4). - + %1 taps her permanents. female %1 tappt ihre bleibenden Karten. - + %1 untaps her permanents. female %1 enttappt ihre bleibenden Karten. - + %1 taps his permanents. male %1 tappt seine bleibenden Karten. - + %1 untaps his permanents. male %1 enttappt seine bleibenden Karten. - + %1 taps %2. female %1 tappt %2. - + %1 untaps %2. female %1 enttappt %2. - + %1 taps %2. male %1 tappt %2. - + %1 untaps %2. male %1 enttappt %2. - + %1 sets counter %2 to %3 (%4%5). female %1 setzt Zähler %2 auf %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 setzt Zähler %2 auf %3 (%4%5). - + %1 sets %2 to not untap normally. female %1 setzt %2 auf explizites Enttappen. - + %1 sets %2 to not untap normally. male %1 setzt %2 auf explizites Enttappen. - + %1 sets %2 to untap normally. female %1 setzt %2 auf normales Enttappen. - + %1 sets %2 to untap normally. male %1 setzt %2 auf normales Enttappen. - + %1 sets PT of %2 to %3. female %1 setzt Kampfwerte von %2 auf %3. - + %1 sets PT of %2 to %3. male %1 setzt Kampfwerte von %2 auf %3. - + %1 sets annotation of %2 to %3. female %1 versieht %2 mit dem Hinweis %3. - + %1 sets annotation of %2 to %3. male %1 versieht %2 mit dem Hinweis %3. + + + %1 is looking at %2. + female + %1 sieht sich %2 an. + %1 is looking at %2. - female + male %1 sieht sich %2 an. + + %1 is looking at the top %n card(s) %2. + female + %1 sieht sich die oberste Karte %2 an.%1 sieht sich die obersten %n Karten %2 an. + + + %1 is looking at the top %n card(s) %2. + male + %1 sieht sich die oberste Karte %2 an.%1 sieht sich die obersten %n Karten %2 an. + - - %1 is looking at %2. - male - %1 sieht sich %2 an. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1 sieht sich die oberste Karte %2 an.%1 sieht sich die obersten %n Karten %2 an. - - - %1 is looking at the top %n card(s) %2. - male - %1 sieht sich die oberste Karte %2 an.%1 sieht sich die obersten %n Karten %2 an. + %1 sieht sich %2 nicht mehr an. %1 stops looking at %2. - female - %1 sieht sich %2 nicht mehr an. - - - - %1 stops looking at %2. male %1 sieht sich %2 nicht mehr an. - + %1 reveals %2 to %3. p1 female, p2 female %1 zeigt %3 %2. - + %1 reveals %2 to %3. p1 female, p2 male %1 zeigt %3 %2. - + %1 reveals %2 to %3. p1 male, p2 female %1 zeigt %3 %2. - + %1 reveals %2 to %3. p1 male, p2 male %1 zeigt %3 %2. + + + %1 reveals %2. + female + %1 zeigt %2 offen vor. + %1 reveals %2. - female - %1 zeigt %2 offen vor. - - - - %1 reveals %2. male %1 zeigt %2 offen vor. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 zeigt %4 zufällig %2%3 vor. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 zeigt %4 zufällig %2%3 vor. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 zeigt %4 zufällig %2%3 vor. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 zeigt %4 zufällig %2%3 vor. - + %1 randomly reveals %2%3. female %1 zeigt zufällig %2%3 offen vor. - + %1 randomly reveals %2%3. male %1 zeigt zufällig %2%3 offen vor. - + %1 peeks at face down card #%2. female %1 schaut sich die umgedrehte Karte #%2 an. - + %1 peeks at face down card #%2. male %1 schaut sich die umgedrehte Karte #%2 an. - + %1 peeks at face down card #%2: %3. female %1 schaut sich die umgedrehte Karte #%2 an: %3. - + %1 peeks at face down card #%2: %3. male %1 schaut sich die umgedrehte Karte #%2 an: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 zeigt %4 %2%3 vor. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 zeigt %4 %2%3 vor. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 zeigt %4 %2%3 vor. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 zeigt %4 %2%3 vor. - + %1 reveals %2%3. female %1 zeigt %2%3 offen vor. - + %1 reveals %2%3. male %1 zeigt %2%3 offen vor. - + %1 is now keeping the top card %2 revealed. %1 lässt nun die oberste Karte %2 aufgedeckt. - + %1 is not revealing the top card %2 any longer. %1 lässt die oberste Karte %2 nicht mehr aufgedeckt. - + It is now %1's turn. female %1 ist am Zug. - + It is now %1's turn. male %1 ist am Zug. - + a card eine Karte @@ -2936,12 +3404,12 @@ Lokale Version ist %1, Serverversion ist %2. - + ending phase Endphase - + untap step das Enttappsegment @@ -3051,64 +3519,64 @@ Lokale Version ist %1, Serverversion ist %2. %1 entfernt %2 %3 Marke(n) von %4 (jetzt %5). - + %1 is looking at the top %2 card(s) %3. female %1 sieht sich die oberste(n) %2 Karten %3 an. - + %1 is looking at the top %2 card(s) %3. male %1 sieht sich die oberste(n) %2 Karten %3 an. - + upkeep step das Versorgungssegment - + draw step das Ziehsegment - + first main phase die erste Hauptphase - + beginning of combat step das Anfangssegment der Kampfphase - + declare attackers step das Angreifer-Deklarieren-Segment - + declare blockers step das Blocker-Deklarieren-Segment - + combat damage step das Kampfschadenssegment - + end of combat step das Endsegment der Kampfphase - + second main phase die zweite Hauptphase - + It is now the %1. Es ist nun %1. @@ -3116,62 +3584,74 @@ Lokale Version ist %1, Serverversion ist %2. MessagesSettingsPage - + Chat settings Chat Einstellungen - + + Custom alert words + Benutzerdifinierte Warnungen + + + Enable chat mentions Chat Erwähnungen aktivieren - + + Enable mention completer + Autovervollständigung aktivieren + + + In-game message macros Makros für Nachrichten in Spielen - - Ignore unregistered users in main chat - Ignoriere unregistrierte Benutzer im Hauptchat + + Ignore chat room messages sent by unregistered users + Ignoriere Nachrichten von unregistrierten Benutzern im Chatroom. - - Ignore chat room messages sent by unregistered users. - Ignoriere Nachrichten von unregistrierten Benutzern in Räumen. - - - - Ignore private messages sent by unregistered users. + + Ignore private messages sent by unregistered users Ignoriere private Nachrichten von unregistrierten Benutzern. - + + Enable desktop notifications for private messages + Desktop Benachrichtigungen für private Nachrichten aktivieren. + + + + Separate words with a space, alphanumeric characters only + Wörter durch Leerzeichen trennen, nur alphanumerische Zeichen verwenden + + + + Invert text color Textfarbe invertieren - - Enable desktop notifications for private messages. - Desktop Benachrichtigungen für private Nachrichten aktivieren. - - - + Enable desktop notification for mentions. Desktop Benachrichtigungen für Erwähnungen aktivieren. - + + (Color is hexadecimal) (Farbcode in hexadezimal) - + Add message Nachricht hinzufügen - + Message: Nachricht: @@ -3237,336 +3717,337 @@ Lokale Version ist %1, Serverversion ist %2. Player - + &View library Bibliothek &ansehen - + Move top cards to &graveyard... Oberste Karten auf den F&riedhof legen... - + View &top cards of library... Oberste Karten der Bibliothek a&nsehen... - + &View graveyard &Zeige Friedhof - + &View sideboard Zeige &Sideboard - + Player "%1" Spieler "%1" - - - + + + + &Hand &Hand - + &Reveal hand to... Zeige meine Hand... - + Reveal r&andom card to... Zeige eine zufällige Karte... - + &Library Bib&liothek - - - + + + &Graveyard &Friedhof - + &Sideboard &Sideboard - + Red Rot - + Yellow Gelb - + Green Grün - + View top cards of library Zeige die obersten Karten der Bibliothek - + Number of cards: Anzahl der Karten: - + &Draw card Karte &ziehen - + Reveal top cards of library Zeige die obersten Karten der Bibliothek - + Number of cards: (max. %1) Anzahl an Karten: (max. %1) - + &View exile &Zeige Exil - - - + + + &Exile &Exil - + Reveal t&op cards to... Zeige die obersten Karten... - + D&raw cards... Ka&rten ziehen... - + Take &mulligan &Mulligan nehmen - + &Shuffle Mi&schen - + &Counters &Zähler - + &Untap all permanents &Enttappe alle bleibenden Karten - + R&oll die... &Würfeln... - + &Create token... Spiels&tein erstellen... - + C&reate another token &Noch einen Spielstein erstellen - + S&ay S&agen - + &Move hand to... Lege Handkarten ... - - - - + + + + &Top of library auf die Bibliothek - - - - + + + + &Bottom of library unter die Bibliothek - + &Move graveyard to... Verschiebe Friedhof... - + &Move exile to... Verschiebe Exil... - + Reveal &library to... Zeige Bibliothek... - + &Always reveal top card &Oberste Karte aufgedeckt lassen - + O&pen deck in deck editor Im &Deckeditor öffnen - + &Undo last draw Zuletzt gezogene Karte zur&ücklegen - + Play top card &face down oberste Karte verdeckt spielen - + Move top cards to &exile... Oberste Karten ins &Exil schicken... - + Put top card on &bottom Oberste Karte nach &unten legen - + Put bottom card &in graveyard Lege die unterste Karte in den Friedhof - + Cr&eate predefined token &Vordefinierten Spielstein erstellen - + C&ard &Karte - + &All players &allen Spielern - + &Play &Ausspielen - + &Hide &Verstecken - + Play &Face Down Karte verdeckt spielen - + &Tap &Tappen - + &Untap E&nttappen - + Toggle &normal untapping &Normales Enttappen umschalten - + &Flip &Umdrehen - + &Peek at card face &Vorderseite anschauen - + &Clone &Kopieren - + Attac&h to card... An Karte &anlegen... - + Unattac&h &Von Karte lösen - + &Draw arrow... &Pfeil zeichnen... - + &Increase power &Stärke erhöhen - + &Decrease power S&tärke verringern - + I&ncrease toughness &Widerstandskraft erhöhen - + D&ecrease toughness W&iderstandskraft verringern @@ -3576,22 +4057,22 @@ Lokale Version ist %1, Serverversion ist %2. Stärke und Widerstandskraft &erhöhen - + Dec&rease power and toughness Stärke und Widerstandskraft v&erringern - + Set &power and toughness... Stär&ke und Widerstandskraft setzen... - + &Set annotation... &Notiz setzen... - + &Add counter (%1) Marke &hinzufügen (%1) @@ -3601,103 +4082,108 @@ Lokale Version ist %1, Serverversion ist %2. Marke &entfernen (%1) - + &Set counters (%1)... Marken &setzen (%1)... - + Draw cards Karten ziehen - - - - + + + + Number: Anzahl: - + Move top cards to grave Oberste Karten in den Friedhof legen - + Move top cards to exile Oberste Karten ins Exil schicken - + Roll die Würfeln - + Number of sides: Anzahl der Seiten: - + Set power/toughness Stärke und Widerstandskraft setzen - + Please enter the new PT: Bitte die neue Stärke und Widerstandskraft eingeben: - + Set annotation Notiz setzen - + Please enter the new annotation: Bitte die Notiz eingeben: - + Set counters Setze Marken + + + Cr&eate related card + Zugehörige Karte kr&eieren + QMenuBar - + Services Dienste - + Hide %1 %1 ausblenden - + Hide Others Andere ausblenden - + Show All Alle anzeigen - + Preferences... Einstellungen... - + Quit %1 Schließe %1 - + About %1 Über %1 @@ -3705,7 +4191,7 @@ Lokale Version ist %1, Serverversion ist %2. QObject - + Cockatrice replays (*.cor) Aufgezeichnete Cockatrice-Spiele (*.cor) @@ -3795,14 +4281,43 @@ Lokale Version ist %1, Serverversion ist %2. + Permissions + Berechtigungen + + + Players Spieler - + Games Spiele + + + + Error + Fehler + + + + You do not have the proper permission to join this room. + Sie haben nicht die erforderlichen Berechtigungen, um diesem Raum beizutreten. + + + + Failed to join the room due to an unknown error. + Beitritt in den Raum aufgrund eines unbekannten Fehlers fehlgeschlagen. + + + + SequenceEdit + + + Shortcut already in use + Shortcut wird bereits benutzt + SetsModel @@ -3845,7 +4360,7 @@ Lokale Version ist %1, Serverversion ist %2. &Zeit bis zur Abschaltung (Minuten): - + Shut down server Server abschalten @@ -3853,80 +4368,85 @@ Lokale Version ist %1, Serverversion ist %2. SoundSettingsPage - + Choose path Dateipfad auswählen - + Enable &sounds Töne aktivieren - + Path to sounds directory: Pfad zum Verzeichnis mit den Tondateien: - + Test system sound engine - + Systemsoundeinstellungen testen - + Sound settings Toneinstellungen - + Master volume requires QT5 - + Masterlautstärke erfordert QT5 - + Master volume - + Masterlautstärke TabAdmin - + Update server &message Server&nachricht aktualisieren - + &Shut down server &Server abschalten - + + &Reload configuration + Konfigu&ration neuladen + + + Server administration functions Funktionen zur Serverwartung - + &Unlock functions &Sperre aufheben - + &Lock functions Funktionen s&perren - + Unlock administration functions Wartungsfunktionen entsperren - + Do you really want to unlock the administration functions? Möchten Sie wirklich die Sperre der Wartungsfunktionen aufheben? - + Administration Administration @@ -3934,181 +4454,223 @@ Lokale Version ist %1, Serverversion ist %2. TabDeckEditor - + &Print deck... Deck &drucken... - + &Close S&chließen - + &Edit sets... &Editionen bearbeiten... - - &Clear search - Suche a&ufheben + + Filters + Filter - + + &Clear all filters + Alle Filter entfernen + + + + Delete selected + Ausgewählten löschen + + + Deck &name: Deck&name: - + &Comments: &Kommentare: - + Hash: Hash: - + &New deck &Neues Deck - + &Load deck... Deck &laden... - + &Save deck Deck &speichern - + Save deck &as... Deck s&peichern unter... - + Load deck from cl&ipboard... Deck aus &Zwischenablage laden... - + Save deck to clip&board Deck in Z&wischenablage speichern - + &Analyze deck on deckstats.net Deck auf deckstats.net &analysieren - + Open custom image folder - Öffne den Ordner für eigene Kartenbilder + Öffne den Ordner für benutzerdefinierte Kartenbilder - + + Open custom sets folder + Öffne den Ordner für benutzerdifinierte Sets + + + Add card to &maindeck Karte zu&m Hauptdeck hinzufügen - + Add card to &sideboard Karte zum &Sideboard hinzufügen - + &Deck Editor &Deckeditor - + C&ard Database &Kartendatenbank - + + Show/Hide card information + Karteninformationen ein-/ausblenden + + + + Show/Hide deck + Deck ein-/ausblenden + + + + Show/Hide filters + Filter ein-/ausblenden + + + + Reset layout + Darstellung zurücksetzen + + + + Card Info + Karteninformationen + + + + Deck + Deck + + + Welcome Willkommen - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. - +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + Hi! Es scheint, als würden Sie diese Version von Cochatrice das erste Mal starten. +Alle Editionen der Kartendatenbank wurden aktiviert. +Lesen Sie mehr über das Ändern der Editionsreihenfolge oder die Deaktivierung bestimmter Editionen im „Editionen bearbeiten...“-Menu. - - Show card text only - nur Kartentext - - - + &Remove row Zeile entfe&rnen - + &Increment number Anzahl er&höhen - + &Decrement number Anzahl v&erringern - + Edit &tokens... Spielsteine &bearbeiten... - + Deck: %1 Deck: %1 - + Are you sure? Sind Sie sicher? - + The decklist has been modified. Do you want to save the changes? Die Deckliste wurde verändert. Möchten Sie die Änderungen speichern? - + Load deck Deck laden - - - + + + Error Fehler - + The deck could not be saved. Das Deck konnte nicht gespeichert werden. - - + + The deck could not be saved. Please check that the directory is writable and try again. Das Deck konnte nicht gespeichert werden. Bitte überprüfen Sie, dass Sie Schreibrechte in dem Verzeichnis haben, und versuchen Sie es erneut. - + Save deck Deck speichern @@ -4206,94 +4768,99 @@ Bitte geben Sie einen Namen ein: TabGame - + &Phases &Phasen - + &Game Spi&el - + Next &phase Nächste &Phase - + Next &turn Nächster &Zug - + &Remove all local arrows &Lokale Pfeile entfernen - + + Rotate View Cl&ockwise + Ansicht im Uhrzeigesinn drehen + + + + Rotate View Co&unterclockwise + Ansicht gegen den Uhrzeigersinn drehen + + + Game &information &Spielinformationen - + &Concede &Aufgeben - + &Leave game Spiel ver&lassen - + C&lose replay Wiederholung sch&ließen - + &Say: &Sagen: - + Concede Aufgeben - + Are you sure you want to concede this game? Sind Sie sicher, dass Sie das Spiel aufgeben möchten? - + Leave game Spiel verlassen - + Are you sure you want to leave this game? Sind Sie sicher, dass Sie das Spiel verlassen möchten? - + You are flooding the game. Please wait a couple of seconds. Sie senden zu viele Befehle. Bitte warte ein paar Sekunden. - + You have been kicked out of the game. Sie wurden aus dem Spiel geworfen. - - Replay %1: %2 - Wiederholung %1: %2 - - - - Game %1: %2 - Spiel %1: %2 + + REPLAY + Wiederholung @@ -4337,54 +4904,54 @@ Bitte geben Sie einen Namen ein: TabReplays - + Local file system Lokales Dateisystem - + Server replay storage Wiederholungsablage auf dem Server - + Watch replay Wiederholung abspielen - - + + Delete Löschen - + Download replay Wiederholung herunterladen - + Toggle expiration lock automatische Löschung umschalten - + Delete local file Lokale Datei löschen - + Are you sure you want to delete "%1"? Sind Sie sicher, dass Sie "%1" löschen möchten? - + Delete remote replay Wiederholung vom Server löschen - + Are you sure you want to delete the replay of game %1? Sind Sie sicher, dass Sie das Replay des Spiels %1 löschen möchten? @@ -4397,47 +4964,47 @@ Bitte geben Sie einen Namen ein: TabRoom - + &Say: &Sagen: - + Chat Chat - + &Room &Raum - + &Leave room Raum ver&lassen - + &Clear chat Chat leeren - + Chat Settings... Chat Einstellungen - + mentioned you. hat dich erwähnt. - + Click to view Zum Anzeigen klicken. - + You are flooding the chat. Please wait a couple of seconds. Sie überfluten den Chatraum. Bitte warten Sie ein paar Sekunden. @@ -4453,15 +5020,25 @@ Bitte geben Sie einen Namen ein: TabSupervisor - + Are you sure? Sind Sie sicher? - + There are still open games. Are you sure you want to quit? Es sind noch Spiele offen. Wollen Sie das Programm trotzdem beenden? + + + Promotion + Beförderung + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + Sie wurden zum Moderator ernannt. Bitte loggen Sie sich aus und wieder ein, damit die Änderungen in Kraft treten. + TabUserLists @@ -4484,169 +5061,301 @@ Bitte geben Sie einen Namen ein: UserContextMenu - + User &details Benutzer&details - + Private &chat Privater &Chat - + Show this user's &games Spiele dieses &Benutzers anzeigen - + Add to &buddy list Zur &Freundesliste hinzufügen - + Remove from &buddy list Von &Freundesliste entfernen - + Add to &ignore list &Ignorieren - + Remove from &ignore list Nicht mehr &ignorieren - + Kick from &game Aus dem &Spiel werfen - + Ban from &server Vom &Server bannen - + + &Promote user to moderator + Nutzer zum Moderator befördern + + + + Dem&ote user from moderator + Moderator degradieren + + + %1's games %1s Spiele + + + + Success + Erfolgreich + + + + Successfully promoted user. + Nutzer erfolgreich befördert. + + + + Successfully demoted user. + Nutzer erfolgreich degradiert. + + + + + Failed + Fehlgeschlagen + + + + Failed to promote user. + Beförderung des Nutzers fehlgeschlagen. + + + + Failed to demote user. + Degradierung des Nutzers fehlgeschlagen. + UserInfoBox - + User information Benutzerinformationen - + Real name: Richtiger Name: - - Gender: - Geschlecht: + + Pronouns: + Pronomen: - + Location: Ort: - + User level: Nutzerstatus: - + Account Age: Accountalter: - + + Edit + Bearbeiten + + + + Change password + Passwort ändern + + + + Change avatar + Avatar wechseln + + + Administrator Administrator - + Moderator Moderator - + Registered user Registrierter Benutzer - - + + Unregistered user Unregistrierter Benutzer - + Unknown Unbekannt - + Year Jahr - + Years Jahre - + Day Tag - + Days Tage + + + + + Information + Information + + + + User information updated. + Benutzerinformationen aktualisiert. + + + + + + + + + + + Error + Fehler + + + + This server does not permit you to update your user informations. + Dieser Server erlaubt Ihnen nicht, Ihre Benutzerinformationen zu aktualisieren. + + + + + An error occured while trying to update your user informations. + Ein Fehler ist während der Aktualisierung Ihrer Benutzerinformationen aufgetreten. + + + + Password changed. + Passwort geändert. + + + + This server does not permit you to change your password. + Dieser Server erlaubt Ihnen nicht, Ihr Passwort zu ändern. + + + + The new password is too short. + Das neue Passwort ist zu kurz. + + + + The old password is incorrect. + Altes Passwort inkorrekt. + + + + Avatar updated. + Avatar gewechselt. + + + + This server does not permit you to update your avatar. + Dieser Server erlaubt Ihnen nicht, Ihren Avatar zu ändern. + + + + An error occured while trying to updater your avatar. + Ein Fehler ist während des Wechselns Ihres Avatars aufgetreten. + UserInterfaceSettingsPage - + General interface settings Allgemeine Bedienung - + Enable notifications in taskbar Taskbarbenachrichtigungen aktivieren - + Notify in the taskbar for game events while you are spectating Benachrichtigungen für Spielereignisse auch beim Zusehen anderer Spiele in der Taskbar anzeigen - + &Double-click cards to play them (instead of single-click) Karten durch &Doppelklick ausspielen (statt Einzelklick) - + &Play all nonlands onto the stack (not the battlefield) by default alle Nichtländer standardmäßig über den Stapel spielen (anstatt direkt auf den Tisch) - + + Annotate card text on tokens + Kartentext auf Spielstein kommentieren + + + Animation settings Animationseinstellungen - + &Tap/untap animation Animiertes &Tappen/Enttappen @@ -4654,22 +5363,22 @@ Bitte geben Sie einen Namen ein: UserList - + Users connected to server: %1 Mit dem Server verbundene Nutzer: %1 - + Users in this room: %1 Benutzer in diesem Raum: %1 - + Buddies online: %1 / %2 Freunde online: %1 / %2 - + Ignored users online: %1 / %2 Ignorierte Benutzer online: %1 / %2 @@ -4691,28 +5400,45 @@ Bitte geben Sie einen Namen ein: Move selected set up nach oben verschieben + + + Move selected set to the top + Ausgewählte Edition an die Spitze verschieben + Move selected set down nach unten verschieben - - - Move selected set to top - zum Anfang verschieben - - Move selected set to bottom - ans Ende verschieben + Move selected set to the bottom + Ausgewählte Edition ans Ende verschieben - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. - + hints: + Tipps: + + + + Enable the sets that you want to have available in the deck editor + Aktivieren Sie die Editionen, die Ihnen im Deckeditor zur Verfügung stehen sollen + + + + Move sets around to change their order, or click on a column header to sort sets on that field + Bewegen Sie die Editionen um deren Reihenfolge zu ändern oder sortieren Sie sie spaltenweise durch einen Klick auf die Spaltenüberschrift + + + + Sets order decides the source that will be used when loading images for a specific card + Die Reihenfolge der Editionen bestimmt die Quelle aus der Kartenbilder bestimmter Karten stammen + + + + Disabled sets will be used for loading images only if all the enabled sets failed + Deaktivierte Editionen werden nur für Bilder verwendet, wenn die aktivierten Editionen fehlschlagen @@ -4720,12 +5446,12 @@ Disabled sets will still be used for loading images. Editionen bearbeiten - + Success Erfolgreich - + The sets database has been saved successfully. Die Editionsdatenbank wurde erfolgreich gespeichert. @@ -4753,4 +5479,561 @@ Disabled sets will still be used for loading images. Stapelansicht + + shortcutsTab + + + Form + Form + + + + Main Window + Hauptfenster + + + + Deck editor + Deckeditor + + + + Local gameplay + lokales Gameplay + + + + Watch replay + Wiederholung abspielen + + + + Connect + Verbinden + + + + Register + Registrieren + + + + Full screen + Vollbild + + + + Settings + Einstellungen + + + + Check for card updates + Nach Kartenaktualisierungen suchen + + + + Exit + Beenden + + + + Deck Editor + Deckeditor + + + + Analyze deck + Deck analysieren + + + + Load deck (clipboard) + Deck laden (Zwischenablage) + + + + Clerar all filters + Alle Filter entfernen + + + + New deck + Neues Deck + + + + Clear one filter + Einen Filter entfernen + + + + Open custom folder + Öffne benutzerdefinierten Ordner + + + + Close + Schließen + + + + Print deck + Deck drucken + + + + Edit sets + Editionen bearbeiten + + + + Delete card + Karte löschen + + + + Edit tokens + Spielsteine bearbeiten + + + + Reset layout + Darstellung zurücksetzen + + + + Add card + Karte hinzufügen + + + + Save deck + Deck speichern + + + + Remove card + Karte entfernen + + + + Save deck as + Deck speichern unter... + + + + Load deck + Deck laden + + + + Save deck (clipboard) + Deck speichern (Zwischenablage) + + + + + Counters + Zähler + + + + Life + Leben + + + + + + + + Set + Setzen + + + + + + + Add + Hinzufügen + + + + + + + Remove + Entfernen + + + + Red + Rot + + + + Green + Grün + + + + Yellow + Gelb + + + + Mainwindow / Deck editor + Hauptfenster/Deckeditor + + + + Power / toughness + Stärke/Widerstandskraft + + + + Power and toughness + Stärke und Widerstandskraft + + + + Add (+1/+1) + (+1/+1) hinzufügen + + + + Remove (-1/-1) + (-1/-1) hinzufügen + + + + Toughness + Widerstandskraft + + + + Remove (-0/-1) + (-0/-1) hinzufügen + + + + Add (+0/+1) + (+0/+1) hinzufügen + + + + Power + Stärke + + + + Remove (-1/-0) + (-1/-0) hinzufügen + + + + Add (+1/+0) + (+1/+0) hinzufügen + + + + Game Phases + Spielphasen + + + + Untap + Enttappen + + + + Disconnect + + + + + Upkeep + + + + + + Draw + Ziehen + + + + Main 1 + 1. Hauptphase + + + + Start combat + Beginn des Kampfes + + + + Attack + Angriff + + + + Block + Blocken + + + + Damage + Schaden + + + + End combat + Ende des Kampfes + + + + Main 2 + 2. Hauptphase + + + + End + Endsegment + + + + Next phase + Nächste Phase + + + + Next turn + Nächster Zug + + + + Player + Spieler + + + + Tap Card + Karte tappen + + + + Untap Card + Karte enttappen + + + + Untap all + Alle enttappen + + + + Toogle untap + Enttappen umschalten + + + + Flip card + Karte drehen + + + + Peek card + Karte ansehen + + + + Play card + Karte spielen + + + + Attach card + Karte anlegen + + + + Unattach card + Karte lösen + + + + Clone card + Karte klonen + + + + Create token + Spielstein erstellen + + + + Create another token + Einen weiteren Spielstein erstellen + + + + Set annotation + Notiz setzen + + + + Phases / P/T / Player + Phasen / P/T /Player + + + + Move card to + Karte verschieben + + + + Bottom library + unter die Bibliothek + + + + Top library + auf die Bibliothek + + + + + Graveyard + in den Friedhof + + + + + Exile + ins Exil + + + + Hand + auf die Hand + + + + View + Ansehen + + + + Library + Bibliothek + + + + Tops card of library + Obersten Karten der Bibliothek + + + + Sideboard + Sideboard + + + + Close recent view + Schließe jüngste Ansicht + + + + Pre-play + Vorspiel + + + + Load remote deck + Deck vom Server laden + + + + Load local deck + Lokales Deck laden + + + + Game play + + + + + Draw arrow + Pfeil zeichnen + + + + Leave game + Spiel verlassen + + + + Remove local arrows + Lokale Pfeile entfernen + + + + Concede + Aufgeben + + + + Roll dice + Würfeln + + + + Rotate view CW + Ansicht im Uhrzeigersinn drehen + + + + Shuffle library + Bibliothek mischen + + + + Rotate view CCW + Ansicht gegen den Uhrzeigersinn drehen + + + + Mulligan + Mulligan + + + + Draw card + Karte ziehen + + + + Draw cards + Karten ziehen + + + + Undo draw + Ziehen rückgängig machen + + + + Always reveal top card + Oberste Karte aufgedeckt lassen + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index 6a7ba4bd..edc735b6 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -4,17 +4,17 @@ AbstractCounter - + &Set counter... - + Set counter - + New value for counter '%1': @@ -22,86 +22,86 @@ AppearanceSettingsPage - + Zone background pictures - + Hand background: - + Stack background: - + Table background: - + Player info background: - + Card back: - + Card rendering - + Display card names on cards having a picture - + Scale cards on mouse over - + Hand layout - + Display hand horizontally (wastes space) - + Enable left justification - + Table grid layout - + Invert vertical coordinate - + Minimum player count for multi-column layout: - - - - - + + + + + Choose path @@ -109,96 +109,101 @@ BanDialog - + ban &user name - + ban &IP address - - Ban type - - - - - &permanent ban - - - - - &temporary ban + + ban client I&D + Ban type + + + + + &permanent ban + + + + + &temporary ban + + + + &Days: - + &Hours: - + &Minutes: - + Duration of the ban - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. - + Please enter the reason for the ban that will be visible to the banned person. - + &OK - + &Cancel - + Ban user from server - + Error - - You have to select a name-based or IP-based ban, or both. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -231,30 +236,53 @@ This is only saved for moderators and cannot be seen by the banned person. + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: - + Mana cost: - + + Color(s): + + + + Card type: - + P / T: - + Loyalty: @@ -277,27 +305,32 @@ This is only saved for moderators and cannot be seen by the banned person. - + Name: - + Mana cost: - + + Color(s): + + + + Card type: - + P / T: - + Loyalty: @@ -561,12 +594,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - + Nothing is here... yet - + General @@ -574,17 +607,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Number - + Card - + Price @@ -606,42 +639,42 @@ This is only saved for moderators and cannot be seen by the banned person. DeckViewContainer - - Load &local deck + + Load local deck - - Load d&eck from server + + Load deck from server - + Ready to s&tart - + S&ideboard unlocked - + S&ideboard locked - + Load deck - + Error - + The selected file could not be loaded. @@ -686,38 +719,53 @@ This is only saved for moderators and cannot be seen by the banned person. DlgConnect - - - &Host: - - - &Port: - - - - - Player &name: - - - - - P&assword: + Previous Host + New Host + + + + + &Host: + + + + + Enter host name + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + &Save password - + A&uto connect at start - + Connect to server @@ -725,82 +773,92 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateGame - + &Description: - + &Password: - + P&layers: - + + Re&member settings + + + + Game type - + Only &buddies can join - + Only &registered users can join - + Joining restrictions - + &Spectators can watch - + Spectators &need a password to watch - + Spectators can see &hands - + Spectators can &chat - + Spectators - + + &Clear + + + + Create game - + Game information - + Error - + Server error. @@ -808,96 +866,169 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateToken - + &Name: - + Token - + C&olor: - + white - + blue - + black - + red - + green - + multicolor - + colorless - + &P/T: - + &Annotation: - + &Destroy token when it leaves the table - + Token data - + Show &all tokens - + Show tokens from this &deck - + Choose token from list - + Create token + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -993,6 +1124,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1044,7 +1223,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh @@ -1054,12 +1233,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di - + + Error - + + Invalid deck list. @@ -1072,22 +1253,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1098,7 +1373,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1109,21 +1384,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1132,7 +1407,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1141,63 +1416,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User Interface - + Deck Editor - + Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - - - - - &Join - - @@ -1207,7 +1477,7 @@ Would you like to change your database location setting? - + Error @@ -1252,37 +1522,47 @@ Would you like to change your database location setting? - + Join game - + Password: - + Please join the respective room first. - + Games - + &Filter games - + C&lear filter - + + C&reate + + + + + &Join + + + + J&oin as spectator @@ -1399,97 +1679,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path - + Success - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Personal settings - + Language: - + Download card pictures on the fly - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths - + Decks directory: - + Replays directory: - + Pictures directory: - + Card database: - + Token database: - + Picture cache size: - - + + English English @@ -1497,55 +1787,48 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. - + Scheduled server shutdown. - + Banned by moderator - + Expected end time: %1 - + This ban lasts indefinitely. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed - + The server has terminated your connection. Reason: %1 - + Scheduled server shutdown - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1555,302 +1838,481 @@ Reason for shutdown: %1 - + Number of players - + Please enter the number of players. - - + + Player %1 - + Load replay - + About Cockatrice - + Version %1 - + Translators: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error - + Server timeout - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. - + + You are banned until %1. - + + You are banned indefinitely. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. - + Connecting to %1... - + + Registering to %1 as %2... + + + + Disconnected - + Connected, logging in at %1 - - Logged in as %1 at %2 - - - - + &Connect... - + &Disconnect - + Start &local game... - + &Watch replay... - + &Deck editor - + &Full screen - + + &Register to server... + + + + &Settings... - - + + &Exit - + A&ctions - + &Cockatrice - + &About Cockatrice - + &Help - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2089,62 +2551,62 @@ Local version is %1, remote version is %2. - + the bottom card of %1's library - + the bottom card of his library - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library - + from the bottom of her library - + the top card of %1's library - + the top card of his library - + the top card of her library - + from the top of %1's library - + from the top of his library - + from the top of her library @@ -2565,310 +3027,310 @@ Local version is %1, remote version is %2. - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - + %1 sets %2 to not untap normally. male - + %1 sets %2 to untap normally. female - + %1 sets %2 to untap normally. male - + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male + + + %1 is looking at %2. + female + + %1 is looking at %2. - female + male + + %1 is looking at the top %n card(s) %2. + female + + %1 is looking at the top card %2. + %1 is looking at the top %n cards %2. + + + + %1 is looking at the top %n card(s) %2. + male + + %1 is looking at the top card %2. + %1 is looking at the top %n cards %2. + + - - %1 is looking at %2. - male - - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - %1 is looking at the top card %2. - %1 is looking at the top %n cards %2. - - - - %1 is looking at the top %n card(s) %2. - male - - %1 is looking at the top card %2. - %1 is looking at the top %n cards %2. - + %1 stops looking at %2. - female - - - - - %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male + + + %1 reveals %2. + female + + %1 reveals %2. - female - - - - - %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - + %1 randomly reveals %2%3 to %4. p1 male, p2 female - + %1 randomly reveals %2%3 to %4. p1 male, p2 male - + %1 randomly reveals %2%3. female - + %1 randomly reveals %2%3. male - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - + a card @@ -2940,12 +3402,12 @@ Local version is %1, remote version is %2. - + ending phase - + untap step @@ -3055,64 +3517,64 @@ Local version is %1, remote version is %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step - + draw step - + first main phase - + beginning of combat step - + declare attackers step - + declare blockers step - + combat damage step - + end of combat step - + second main phase - + It is now the %1. @@ -3120,62 +3582,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message - + Message: @@ -3241,336 +3715,337 @@ Local version is %1, remote version is %2. Player - + &View library - + Move top cards to &graveyard... - + View &top cards of library... - + &View graveyard - + &View sideboard - + Player "%1" + + + + + + &Hand + + + + + &Reveal hand to... + + + + + Reveal r&andom card to... + + + + + &Library + + + + + + + + &Graveyard + + + + + &Sideboard + + + + + Red + + + + + Yellow + + + + + Green + + + + + View top cards of library + + + + + Number of cards: + + + + + &Draw card + + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + - - &Hand - - - - - &Reveal hand to... - - - - - Reveal r&andom card to... - - - - - &Library - - - - - - - - &Graveyard - - - - - &Sideboard - - - - - Red - - - - - Yellow - - - - - Green - - - - - View top cards of library - - - - - Number of cards: - - - - - &Draw card - - - - - Reveal top cards of library - - - - - Number of cards: (max. %1) - - - - - &View exile - - - - - - - - &Exile - - - - Reveal t&op cards to... - - - - - D&raw cards... - - - - - Take &mulligan - - - - - &Shuffle - - - - - &Counters - - - - - &Untap all permanents - - - - - R&oll die... - - - - - &Create token... - - - - - C&reate another token - - - - - S&ay - - - - - &Move hand to... - - - - - - - - &Top of library - - - - - - - + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card - + O&pen deck in deck editor - + &Undo last draw - + Play top card &face down - + Move top cards to &exile... - + Put top card on &bottom - + Put bottom card &in graveyard - + Cr&eate predefined token - + C&ard - + &All players - + &Play - + &Hide - + Play &Face Down - + &Tap - + &Untap - + Toggle &normal untapping - + &Flip - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Increase power - + &Decrease power - + I&ncrease toughness - + D&ecrease toughness @@ -3580,22 +4055,22 @@ Local version is %1, remote version is %2. - + Dec&rease power and toughness - + Set &power and toughness... - + &Set annotation... - + &Add counter (%1) @@ -3605,103 +4080,108 @@ Local version is %1, remote version is %2. - + &Set counters (%1)... - + Draw cards - - - - + + + + Number: - + Move top cards to grave - + Move top cards to exile - + Roll die - + Number of sides: - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3709,7 +4189,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) @@ -3799,14 +4279,43 @@ Local version is %1, remote version is %2. - Players + Permissions + Players + + + + Games + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3849,7 +4358,7 @@ Local version is %1, remote version is %2. - + Shut down server @@ -3857,37 +4366,37 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3895,42 +4404,47 @@ Local version is %1, remote version is %2. TabAdmin - + Update server &message - + &Shut down server - + + &Reload configuration + + + + Server administration functions - + &Unlock functions - + &Lock functions - + Unlock administration functions - + Do you really want to unlock the administration functions? - + Administration @@ -3938,179 +4452,219 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... - + &Close - + &Edit sets... - - &Clear search + + Filters - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: - + &Comments: - + Hash: - + &New deck - + &Load deck... - + &Save deck - + Save deck &as... - + Load deck from cl&ipboard... - + Save deck to clip&board - + &Analyze deck on deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck - + Add card to &sideboard - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row - + &Increment number - + &Decrement number - + Edit &tokens... - + Deck: %1 - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4207,93 +4761,98 @@ Please enter a name: TabGame - + &Phases - + &Game - + Next &phase - + Next &turn - + &Remove all local arrows - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information - + &Concede - + &Leave game - + C&lose replay - + &Say: - + Concede - + Are you sure you want to concede this game? - + Leave game - + Are you sure you want to leave this game? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 - - - - - Game %1: %2 + + REPLAY @@ -4338,54 +4897,54 @@ Please enter a name: TabReplays - + Local file system - + Server replay storage - + Watch replay - - + + Delete - + Download replay - + Toggle expiration lock - + Delete local file - + Are you sure you want to delete "%1"? - + Delete remote replay - + Are you sure you want to delete the replay of game %1? @@ -4398,47 +4957,47 @@ Please enter a name: TabRoom - + &Say: - + Chat - + &Room - + &Leave room - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. @@ -4454,15 +5013,25 @@ Please enter a name: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4485,169 +5054,301 @@ Please enter a name: UserContextMenu - + User &details - + Private &chat - + Show this user's &games - + Add to &buddy list - + Remove from &buddy list - + Add to &ignore list - + Remove from &ignore list - + Kick from &game - + Ban from &server - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information - + Real name: - - Gender: + + Pronouns: - + Location: - + User level: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator - + Moderator - + Registered user - - + + Unregistered user - + Unknown - + Year - + Years - + Day - + Days + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings - + &Tap/untap animation @@ -4655,22 +5356,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 - + Buddies online: %1 / %2 - + Ignored users online: %1 / %2 @@ -4692,27 +5393,44 @@ Please enter a name: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4721,12 +5439,12 @@ Disabled sets will still be used for loading images. - + Success - + The sets database has been saved successfully. @@ -4754,4 +5472,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + diff --git a/cockatrice/translations/cockatrice_en@pirate.ts b/cockatrice/translations/cockatrice_en@pirate.ts new file mode 100644 index 00000000..e6850cb4 --- /dev/null +++ b/cockatrice/translations/cockatrice_en@pirate.ts @@ -0,0 +1,5997 @@ + + + AbstractCounter + + + &Set counter... + &Set piece o' eight... + + + + Set counter + Set piece o' eight + + + + New value for counter '%1': + Freshly formed value for piece o' eight '%1': + + + + AppearanceSettingsPage + + + Zone background pictures + + + + + Hand background: + + + + + Stack background: + + + + + Table background: + + + + + Player info background: + + + + + Card back: + + + + + Card rendering + + + + + Display card names on cards having a picture + + + + + Scale cards on mouse over + + + + + Hand layout + + + + + Display hand horizontally (wastes space) + + + + + Enable left justification + + + + + Table grid layout + + + + + Invert vertical coordinate + + + + + Minimum player count for multi-column layout: + + + + + + + + + Choose path + + + + + BanDialog + + + ban &user name + + + + + ban &IP address + + + + + ban client I&D + + + + + Ban type + + + + + &permanent ban + + + + + &temporary ban + + + + + &Days: + + + + + &Hours: + + + + + &Minutes: + + + + + Duration of the ban + + + + + Please enter the reason for the ban. +This is only saved for moderators and cannot be seen by the banned person. + Me hearty, enter t' reason for t' ban. +This be only saved for captains and cannot be seen by t' brutally banned scallywag. + + + + Please enter the reason for the ban that will be visible to the banned person. + Me hearty, enter t' reason for t' ban that be visible to t' brutally banned scallywag. + + + + &OK + Aye + + + + &Cancel + Belay that + + + + Ban user from server + Banish the scurvy dog from the ship! + + + + Error + Cap'n? Thar be a problem + + + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + + + + + CardDatabase + + + New sets found + Freshly formed fleets be found + + + + %1 new set(s) have been found in the card database. Do you want to enable them? + %1 freshly formed fleet(s) be found in t' dock. Does ye want to enable 'em? + + + + CardDatabaseModel + + + Name + Name + + + + Sets + Fleets + + + + Mana cost + Magic tax + + + + Card type + + + + + P/T + P/T + + + + CardFrame + + + Image + Pic + + + + Description + + + + + Both + + + + + CardInfoText + + + Name: + Name: + + + + Mana cost: + Magic tax: + + + + Color(s): + Color(s): + + + + Card type: + + + + + P / T: + P / T: + + + + Loyalty: + + + + + CardInfoWidget + + + Show card only + + + + + Show text only + + + + + Show full info + + + + + Name: + Name: + + + + Mana cost: + Magic tax: + + + + Color(s): + + + + + Card type: + + + + + P / T: + P / T: + + + + Loyalty: + + + + + CardItem + + + &Power / toughness + &Power / toughness + + + + &Move to + + + + + CardZone + + + her hand + nominative, female owner + the lass's hand + + + + %1's hand + nominative, female owner + %1's hand + + + + his hand + nominative, male owner + the buccaneer's hand + + + + %1's hand + nominative, male owner + %1's hand + + + + her library + look at zone, female owner + the lass's library + + + + %1's library + look at zone, female owner + %1's library + + + + his library + look at zone, male owner + the buccaneer's library + + + + %1's library + look at zone, male owner + %1's library + + + + of her library + top cards of zone, female owner + o' the lass's library + + + + of %1's library + top cards of zone, female owner + o' %1's library + + + + of his library + top cards of zone, male owner + o' the buccaneer's library + + + + of %1's library + top cards of zone, male owner + o' %1's library + + + + her library + reveal zone, female owner + the lass's library + + + + %1's library + reveal zone, female owner + %1's library + + + + his library + reveal zone, male owner + the buccaneer's library + + + + %1's library + reveal zone, male owner + %1's library + + + + her library + shuffle, female owner + the lass's library + + + + %1's library + shuffle, female owner + %1's library + + + + his library + shuffle, male owner + the buccaneer's library + + + + %1's library + shuffle, male owner + %1's library + + + + her sideboard + look at zone, female owner + the lass's sideplank + + + + %1's sideboard + look at zone, female owner + %1's sideplank + + + + his sideboard + look at zone, male owner + the buccaneer's sideplank + + + + %1's sideboard + look at zone, male owner + %1's sideplank + + + + her library + nominative, female owner + the lass's library + + + + %1's library + nominative, female owner + %1's library + + + + his library + nominative, male owner + the buccaneer's library + + + + %1's library + nominative, male owner + %1's library + + + + her graveyard + nominative, female owner + the lass's Davy Jones' Locker + + + + %1's graveyard + nominative, female owner + %1's Davy Jones' Locker + + + + his graveyard + nominative, male owner + the buccaneer's Davy Jones' Locker + + + + %1's graveyard + nominative, male owner + %1's Davy Jones' Locker + + + + her exile + nominative, female owner + the darkest depths o' the lass's Davy Jones' Locker + + + + %1's exile + nominative, female owner + the darkest depths o' %1's Davy Jones' Locker + + + + his exile + nominative, male owner + the darkest depths o' the buccaneer's Davy Jones' Locker + + + + %1's exile + nominative, male owner + the darkest depths o' %1's Davy Jones' Locker + + + + her sideboard + nominative, female owner + the lass's sideplank + + + + %1's sideboard + nominative, female owner + %1's sideplank + + + + his sideboard + nominative, male owner + the buccaneer's sideplank + + + + %1's sideboard + nominative, male owner + %1's sideplank + + + + DeckEditorSettingsPage + + + Nothing is here... yet + Thar be nothin'... yet + + + + General + + + + + DeckListModel + + + Number + + + + + Card + + + + + Price + + + + + DeckStatsInterface + + + + Error + Cap'n? Thar be a problem + + + + The reply from the server could not be parsed. + + + + + DeckViewContainer + + + Load local deck + Find a nearby flagship + + + + Load deck from server + Find a flagship from t' o'er ship + + + + Ready to s&tart + + + + + S&ideboard unlocked + + + + + S&ideboard locked + + + + + Load deck + Find a flagship + + + + Error + Cap'n? Thar be a problem + + + + The selected file could not be loaded. + + + + + DlgCardSearch + + + Card name: + Card name: + + + + Card text: + + + + + Card type (OR): + + + + + Color (OR): + Color (OR): + + + + O&K + Aye + + + + &Cancel + Belay that + + + + Card search + + + + + DlgConnect + + + Previous Host + + + + + New Host + + + + + &Host: + + + + + Enter host name + + + + + &Port: + + + + + Player &name: + Matey &name: + + + + P&assword: + &A stupendously secret code: + + + + &Save password + Record t' &stupendously secret code + + + + A&uto connect at start + + + + + Connect to server + + + + + DlgCreateGame + + + &Description: + + + + + &Password: + Stu&pendously secret code: + + + + P&layers: + Mateys: + + + + Re&member settings + + + + + Game type + + + + + Only &buddies can join + + + + + Only &registered users can join + + + + + Joining restrictions + + + + + &Spectators can watch + + + + + Spectators &need a password to watch + Landlubbers be &needin' a stupendously secret code to watch + + + + Spectators can see &hands + Landlubbers can see &hands + + + + Spectators can &chat + Landlubbers can &chat + + + + Spectators + Landlubbers + + + + &Clear + + + + + Create game + + + + + Game information + + + + + Error + Cap'n? Thar be a problem + + + + Server error. + Cap'n? Thar be a problem wi' t' ship. + + + + DlgCreateToken + + + &Name: + &Name: + + + + Token + + + + + C&olor: + C&olor + + + + white + pure white + + + + blue + sea blue + + + + black + deathly black + + + + red + blood red + + + + green + emerald green + + + + multicolor + multicolor + + + + colorless + colorless + + + + &P/T: + &P/T: + + + + &Annotation: + + + + + &Destroy token when it leaves the table + + + + + Token data + + + + + Show &all tokens + + + + + Show tokens from this &deck + + + + + Choose token from list + + + + + Create token + + + + + DlgEditAvatar + + + + No image chosen. + No pic be picked! + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + Open Pic + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + Invalid pic be picked! + + + + DlgEditPassword + + + Old password: + Old stupendously secret code: + + + + New password: + Freshly formed stupendously secret code: + + + + Confirm new password: + Confirm freshly formed stupendously secret code: + + + + Change password + Change t' stupendously secret code + + + + Error + Cap'n? Thar be a problem + + + + The new passwords don't match. + T' freshly formed stupendously secret codes be failin' to match! + + + + DlgEditTokens + + + &Name: + &Name: + + + + C&olor: + C&olor + + + + white + pure white + + + + blue + sea blue + + + + black + deathly black + + + + red + blood red + + + + green + emerald green + + + + multicolor + multicolor + + + + colorless + colorless + + + + &P/T: + &P/T: + + + + &Annotation: + + + + + Token data + + + + + + Add token + + + + + Remove token + + + + + Edit tokens + + + + + Please enter the name of the token: + + + + + Error + Cap'n? Thar be a problem + + + + The chosen name conflicts with an existing card or token. +Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. + + + + + DlgEditUser + + + Email: + + + + + Pronouns: + What we call ye: + + + + Neutral + Just me name + + + + Masculine + Buccaneer + + + + Feminine + Lass + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + + + DlgFilterGames + + + Show &unavailable games + + + + + Show &password protected games + + + + + Game &description: + + + + + &Creator name: + &Cap'n name: + + + + &Game types + + + + + at &least: + + + + + at &most: + + + + + Maximum player count + + + + + Filter games + + + + + DlgLoadDeckFromClipboard + + + &Refresh + + + + + Load deck from clipboard + Find a flagship from t' clipplank + + + + + Error + Cap'n? Thar be a problem + + + + + Invalid deck list. + Invalid flagship list. + + + + DlgLoadRemoteDeck + + + Load deck + Find a flagship + + + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + Matey &name: + + + + P&assword: + &A stupendously secret code: + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + What we call ye: + + + + Neutral + Just me name + + + + Masculine + Buccaneer + + + + Feminine + Lass + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + Board t' ship + + + + DlgSettings + + + + + Error + Cap'n? Thar be a problem + + + + Unknown Error loading card database + Cap'n? Thar be a unknown problem wi' t' dock + + + + Your card database is invalid. + +Cockatrice may not function correctly with an invalid database + +You may need to rerun oracle to update your card database. + +Would you like to change your database location setting? + + + + + Your card database version is too old. + +This can cause problems loading card information or images + +Usually this can be fixed by rerunning oracle to to update your card database. + +Would you like to change your database location setting? + + + + + File Error loading your card database. + +Would you like to change your database location setting? + + + + + Your card database was loaded but contains no cards. + +Would you like to change your database location setting? + + + + + Your card database did not finish loading + +Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached + +Would you like to change your database location setting? + + + + + Unknown card database load status + +Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues + +Would you like to change your database location setting? + + + + + The path to your deck directory is invalid. Would you like to go back and set the correct path? + + + + + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? + + + + + Settings + Cap'n's orders + + + + General + + + + + Appearance + + + + + User Interface + + + + + Deck Editor + Dock + + + + Chat + + + + + Sound + + + + + Shortcuts + + + + + GameSelector + + + + + + + + + + + Error + Cap'n? Thar be a problem + + + + Please join the appropriate room first. + + + + + Wrong password. + Ye don't know the stupendously secret code! + + + + Spectators are not allowed in this game. + Liliy-livered landlubbers - get off me ship! + + + + The game is already full. + T' ship be full now. + + + + The game does not exist any more. + T' ship be vanishin'! + + + + This game is only open to registered users. + + + + + This game is only open to its creator's buddies. + T' ship be only open for the cap'n's first mates. + + + + You are being ignored by the creator of this game. + T' cap'n be ignorin' ye. + + + + Join game + Board ship + + + + Password: + Stupendously secret code: + + + + Please join the respective room first. + + + + + Games + Ships + + + + &Filter games + + + + + C&lear filter + + + + + C&reate + + + + + &Join + + + + + J&oin as spectator + B&oard as landlubber + + + + GamesModel + + + Game Created + Ship built + + + + Creator + Cap'n + + + + Description + + + + + <1m ago + + + + + <5m ago + + + + + %1m ago + + + + + 1hr %1m ago + + + + + %1hr ago + + + + + 5+ hrs ago + + + + + password + stupendously secret code + + + + buddies only + first mates only + + + + reg. users only + + + + + + can chat + + + + + see hands + + + + + can see hands + + + + + not allowed + + + + + Room + + + + + Game Type + + + + + Restrictions + + + + + Players + Mateys + + + + Spectators + Landlubbers + + + + GeneralSettingsPage + + + Reset/Clear Downloaded Pictures + + + + + + + + + Choose path + + + + + Success + + + + + Downloaded card pictures have been reset. + + + + + Error + Cap'n? Thar be a problem + + + + One or more downloaded card pictures could not be cleared. + + + + + Personal settings + Cap'n's orders for t' cap'n + + + + Language: + + + + + Download card pictures on the fly + + + + + Download card pictures from a custom URL + + + + + Custom Card Download URL: + + + + + Linking FAQ + + + + + Paths + + + + + Decks directory: + + + + + Replays directory: + + + + + Pictures directory: + + + + + Card database: + Dock: + + + + Token database: + + + + + Picture cache size: + + + + + + English + English, arr! (Pirate English) + + + + MainWindow + + + There are too many concurrent connections from your address. + + + + + Scheduled server shutdown. + + + + + Banned by moderator + + + + + Expected end time: %1 + + + + + This ban lasts indefinitely. + + + + + Connection closed + + + + + The server has terminated your connection. +Reason: %1 + + + + + Scheduled server shutdown + + + + + The server is going to be restarted in %n minute(s). +All running games will be lost. +Reason for shutdown: %1 + + + + + Number of players + Number o' mateys + + + + Please enter the number of players. + Me hearty, enter t' number o' mateys + + + + + Player %1 + Matey %1 + + + + Load replay + + + + + About Cockatrice + + + + + Version %1 + + + + + Translators: + + + + + Project Manager: + + + + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + + Past Project Managers: + + + + + Developers: + + + + + Our Developers + + + + + Help Develop! + + + + + Recognition Page + + + + + Help Translate! + + + + + Support: + + + + + Report an Issue + + + + + + + + + + + + + + + + + + + + + + + Error + Cap'n? Thar be a problem + + + + Server timeout + T' ship be timin' out! + + + + Incorrect username or password. Please check your authentication information and try again. + + + + + There is already an active session using this user name. +Please close that session first and re-login. + + + + + + You are banned until %1. + + + + + + You are banned indefinitely. + + + + + This server requires user registration. Do you want to register now? + + + + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + + Unknown login error: %1 + + + + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + The code be too piddly to be stupendously secret. + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + + Socket error: %1 + + + + + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. +Local version is %1, remote version is %2. + + + + + Your Cockatrice client is obsolete. Please update your Cockatrice version. +Local version is %1, remote version is %2. + + + + + Connecting to %1... + Boarding %1 + + + + Registering to %1 as %2... + + + + + Disconnected + Disembarked + + + + Connected, logging in at %1 + + + + + &Connect... + + + + + &Disconnect + &Disembark + + + + Start &local game... + Built &local ship + + + + &Watch replay... + + + + + &Deck editor + &Dock + + + + &Full screen + + + + + &Register to server... + + + + + &Settings... + Cap'n'&s orders + + + + + &Exit + Dis&embark + + + + A&ctions + + + + + &Cockatrice + + + + + &About Cockatrice + + + + + &Help + + + + + Check for card updates... + + + + + A card database update is already running. + A dock update be runnin' already! + + + + Unable to run the card database updater: + Cap'n! We can't run the dock updater: + + + + The card database updater exited with an error: %1 + The dock updater sunk. She be havin' a problem: %1 + + + + Update completed successfully. Cockatrice will now reload the card database. + Update be successfully completin'. Cockatrice now be reloadin' the dock. + + + + + Information + + + + + Troubleshooting + + + + + F.A.Q. + + + + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + + failed to start. + + + + + crashed. + + + + + timed out. + be timin' out! + + + + write error. + Cap'n? Thar be a writin' problem wi' t' ship. + + + + read error. + Cap'n? Thar be a readin' problem wi' t' ship. + + + + unknown error. + Cap'n? Thar be a unknown problem wi' t' ship. + + + + MessageLogWidget + + + The game has been closed. + T' ship has been sunk! + + + + %1 is now watching the game. + + + + + %1 is not watching the game any more. + + + + %1 draws %n card(s). + + %1 draws a card. + %1 draws %n cards. + + + + + You have joined game #%1. + female + + + + + You have joined game #%1. + male + + + + + You are watching a replay of game #%1. + female + + + + + You are watching a replay of game #%1. + male + + + + + %1 has joined the game. + female + %1 has boarded t' ship. + + + + %1 has joined the game. + male + %1 has boarded t' ship. + + + + %1 has left the game. + female + + + + + %1 has left the game. + male + + + + + You have been kicked out of the game. + + + + + %1 has loaded a deck (%2). + %1 be loadin' a flagship (%2). + + + + %1 has loaded a deck with %2 sideboard cards (%3). + %1 be loadin' a flagship wi' %2 sideplank cards (%3). + + + + %1 is ready to start the game. + female + + + + + %1 is ready to start the game. + male + + + + + %1 is not ready to start the game any more. + female + + + + + %1 is not ready to start the game any more. + male + + + + + %1 has locked her sideboard. + female + %1 be lockin' the lass's sideplank. + + + + %1 has locked his sideboard. + male + %1 be lockin' the buccaneer's sideplank. + + + + %1 has unlocked her sideboard. + female + %1 be unlockin' the lass's sideplank. + + + + %1 has unlocked his sideboard. + male + %1 be unlockin' the buccaneer's sideplank. + + + + %1 has conceded the game. + female + %1 be shootin' the lass's own ship. + + + + %1 has conceded the game. + male + %1 be shootin' the buccaneer's own ship. + + + + %1 has restored connection to the game. + female + + + + + %1 has restored connection to the game. + male + + + + + %1 has lost connection to the game. + female + %1 be abandonin' ship! + + + + %1 has lost connection to the game. + male + %1 be abandonin' ship! + + + + %1 shuffles %2. + female + + + + + %1 shuffles %2. + male + + + + + %1 rolls a %2 with a %3-sided die. + female + + + + + %1 rolls a %2 with a %3-sided die. + male + + + + %1 draws %n card(s). + female + + %1 draws a card. + %1 draws %n cards. + + + + %1 draws %n card(s). + male + + %1 draws a card. + %1 draws %n cards. + + + + + %1 undoes his last draw. + + + + + %1 undoes her last draw. + + + + + %1 undoes his last draw (%2). + + + + + %1 undoes her last draw (%2). + + + + + from exile + from the darkest depths o' the lass's Davy Jones' Locker + + + + the bottom card of %1's library + the bottom card o' %1's library + + + + the bottom card of his library + the bottom card o' the buccaneer's library + + + + the bottom card of her library + the bottom card o' the lass's library + + + + from the bottom of %1's library + from the bottom o' %1's library + + + + from the bottom of his library + from the bottom o' the buccaneer's library + + + + from the bottom of her library + from the bottom o' the lass's library + + + + the top card of %1's library + the top card o' %1's library + + + + the top card of his library + the top card o' the buccaneer's library + + + + the top card of her library + the top card o' the lass's library + + + + from the top of %1's library + from the top of %1's library + + + + from the top of his library + from the top o' the buccaneer's library + + + + from the top of her library + from the top o' the lass's library + + + + from %1's library + from %1's library + + + + from library + from library + + + + from sideboard + from sideplank + + + + from the stack + + + + + %1 gives %2 control over %3. + + + + + %1 puts %2 into play tapped%3. + + + + + %1 puts %2 into play%3. + + + + + %1 exiles %2%3. + + + + + %1 puts %2%3 into his library. + + + + + %1 puts %2%3 into her library. + + + + + %1 puts %2%3 on bottom of his library. + + + + + %1 puts %2%3 on bottom of her library. + + + + + %1 puts %2%3 on top of his library. + + + + + %1 puts %2%3 on top of her library. + + + + + %1 puts %2%3 into his library at position %4. + + + + + %1 puts %2%3 into her library at position %4. + + + + + %1 moves %2%3 to sideboard. + + + + + %1 plays %2%3. + + + + + %1 takes a mulligan to %n. + female + + + + + %1 takes a mulligan to %n. + male + + + + + %1 flips %2 face-down. + female + + + + + %1 flips %2 face-down. + male + + + + + %1 flips %2 face-up. + female + + + + + %1 flips %2 face-up. + male + + + + + %1 destroys %2. + female + %1 be firin' a broadside at %2! + + + + %1 destroys %2. + male + %1 be firin' a broadside at %2! + + + + %1 unattaches %2. + female + %1 be removin' %2. + + + + %1 unattaches %2. + male + %1 be removin' %2. + + + + %1 creates token: %2%3. + female + + + + + %1 creates token: %2%3. + male + + + + + %1 points from her %2 to herself. + female + %1 be pointin' from the lass's %2 to the lass herself. + + + + %1 points from his %2 to himself. + male + %1 be pointin' from the buccaneer's %2 to the buccaneer himself. + + + + %1 points from her %2 to %3. + p1 female, p2 female + + + + + %1 points from her %2 to %3. + p1 female, p2 male + + + + + %1 points from his %2 to %3. + p1 male, p2 female + + + + + %1 points from his %2 to %3. + p1 male, p2 male + + + + + %1 points from %2's %3 to herself. + card owner female, target female + + + + + %1 points from %2's %3 to herself. + card owner male, target female + + + + + %1 points from %2's %3 to himself. + card owner female, target male + + + + + %1 points from %2's %3 to himself. + card owner male, target male + + + + + %1 points from %2's %3 to %4. + p1 female, p2 female, p3 female + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 female, p2 female, p3 male + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 female, p2 male, p3 female + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 female, p2 male, p3 male + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 male, p2 female, p3 female + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 male, p2 female, p3 male + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 male, p2 male, p3 female + %1 be pointin' from %2's %3 to %4. + + + + %1 points from %2's %3 to %4. + p1 male, p2 male, p3 male + %1 be pointin' from %2's %3 to %4. + + + + %1 points from her %2 to her %3. + female + + + + + %1 points from his %2 to his %3. + male + + + + + %1 points from her %2 to %3's %4. + p1 female, p2 female + + + + + %1 points from her %2 to %3's %4. + p1 female, p2 male + + + + + %1 points from his %2 to %3's %4. + p1 male, p2 female + + + + + %1 points from his %2 to %3's %4. + p1 male, p2 male + + + + + %1 points from %2's %3 to her own %4. + card owner female, target female + + + + + %1 points from %2's %3 to her own %4. + card owner male, target female + + + + + %1 points from %2's %3 to his own %4. + card owner female, target male + + + + + %1 points from %2's %3 to his own %4. + card owner male, target male + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 female, p3 female + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 female, p3 male + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 male, p3 female + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 male, p3 male + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 female, p3 female + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 female, p3 male + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 male, p3 female + %1 be pointin' from %2's %3 to %4's %5. + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 male, p3 male + %1 be pointin' from %2's %3 to %4's %5. + + + %1 places %n %2 counter(s) on %3 (now %4). + female + + + + %1 places %n %2 counter(s) on %3 (now %4). + male + + + + %1 removes %n %2 counter(s) from %3 (now %4). + female + %1 be removin' a %2 piece o' eight from %3 (now %4).%1 be removin' %n %2 pieces o' eight from %3 (now %4). + + + %1 removes %n %2 counter(s) from %3 (now %4). + male + %1 be removin' a %2 piece o' eight from %3 (now %4).%1 be removin' %n %2 pieces o' eight from %3 (now %4). + + + + %1 taps her permanents. + female + %1 be tappin' the lass's permanents. + + + + %1 untaps her permanents. + female + %1 be untappin' the lass's permanents. + + + + %1 taps his permanents. + male + %1 be tappin' the buccaneer's permanents. + + + + %1 untaps his permanents. + male + %1 be untappin' the buccaneer's permanents. + + + + %1 taps %2. + female + %1 be tappin' %2. + + + + %1 untaps %2. + female + %1 be untappin' %2. + + + + %1 taps %2. + male + %1 be tappin' %2. + + + + %1 untaps %2. + male + %1 be untappin' %2. + + + + %1 sets counter %2 to %3 (%4%5). + female + + + + + %1 sets counter %2 to %3 (%4%5). + male + + + + + %1 sets %2 to not untap normally. + female + + + + + %1 sets %2 to not untap normally. + male + + + + + %1 sets %2 to untap normally. + female + + + + + %1 sets %2 to untap normally. + male + + + + + %1 sets PT of %2 to %3. + female + + + + + %1 sets PT of %2 to %3. + male + + + + + %1 sets annotation of %2 to %3. + female + + + + + %1 sets annotation of %2 to %3. + male + + + + + %1 is looking at %2. + female + %1 be lookin' at %2. + + + + %1 is looking at %2. + male + %1 be lookin' at %2. + + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + + + + %1 stops looking at %2. + female + + + + + %1 stops looking at %2. + male + + + + + %1 reveals %2 to %3. + p1 female, p2 female + + + + + %1 reveals %2 to %3. + p1 female, p2 male + + + + + %1 reveals %2 to %3. + p1 male, p2 female + + + + + %1 reveals %2 to %3. + p1 male, p2 male + + + + + %1 reveals %2. + female + + + + + %1 reveals %2. + male + + + + + %1 randomly reveals %2%3 to %4. + p1 female, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 female, p2 male + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 randomly reveals %2%3. + female + + + + + %1 randomly reveals %2%3. + male + + + + + %1 peeks at face down card #%2. + female + + + + + %1 peeks at face down card #%2. + male + + + + + %1 peeks at face down card #%2: %3. + female + + + + + %1 peeks at face down card #%2: %3. + male + + + + + %1 reveals %2%3 to %4. + p1 female, p2 female + + + + + %1 reveals %2%3 to %4. + p1 female, p2 male + + + + + %1 reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 reveals %2%3. + female + + + + + %1 reveals %2%3. + male + + + + + %1 is now keeping the top card %2 revealed. + + + + + %1 is not revealing the top card %2 any longer. + + + + + It is now %1's turn. + female + + + + + It is now %1's turn. + male + + + + + + a card + + + + %1 places %n counter(s) (%2) on %3 (now %4). + + %1 places a counter (%2) on %3 (now %4). + %1 places %n counters (%2) on %3 (now %4). + + + + %1 removes %n counter(s) (%2) from %3 (now %4). + + %1 removes a counter (%2) from %3 (now %4). + %1 removes %n counters (%2) from %3 (now %4). + + + + + red + blood redblood red + + + + yellow + fool's goldfool's gold + + + + green + emerald greenemerald green + + + + The game has started. + + + + + %1 draws his initial hand. + + + + + %1 draws her initial hand. + + + + %1 places %n %2 counter(s) on %3 (now %4). + + %1 places a %2 counter on %3 (now %4). + %1 places %n %2 counters on %3 (now %4). + + + + %1 removes %n %2 counter(s) from %3 (now %4). + + %1 removes a %2 counter from %3 (now %4). + %1 removes %n %2 counters from %3 (now %4). + + + + + ending phase + + + + + untap step + + + + + %1 draws %2 card(s). + female + + + + + %1 draws %2 card(s). + male + + + + + from play + + + + + from her graveyard + + + + + from his graveyard + + + + + from her hand + + + + + from his hand + + + + + %1 puts %2%3 into her graveyard. + + + + + %1 puts %2%3 into his graveyard. + + + + + %1 moves %2%3 to her hand. + + + + + %1 moves %2%3 to his hand. + + + + + %1 attaches %2 to %3's %4. + p1 female, p2 female + + + + + %1 attaches %2 to %3's %4. + p1 female, p2 male + + + + + %1 attaches %2 to %3's %4. + p1 male, p2 female + + + + + %1 attaches %2 to %3's %4. + p1 male, p2 male + + + + + %1 places %2 %3 counter(s) on %4 (now %5). + female + + + + + %1 places %2 %3 counter(s) on %4 (now %5). + male + + + + + %1 removes %2 %3 counter(s) from %4 (now %5). + female + + + + + %1 removes %2 %3 counter(s) from %4 (now %5). + male + + + + + %1 is looking at the top %2 card(s) %3. + female + + + + + %1 is looking at the top %2 card(s) %3. + male + + + + + upkeep step + step where ye be upkeepin' + + + + draw step + step where ye be drawin' + + + + first main phase + first main phase + + + + beginning of combat step + step where ye begin fightin' + + + + declare attackers step + step where ye be orderin' ye men to board + + + + declare blockers step + step where ye be orderin' ye men to repel boarders + + + + combat damage step + step where people get hurt + + + + end of combat step + step where ye stop fightin' + + + + second main phase + second main phase + + + + It is now the %1. + Now it be t' %1. + + + + MessagesSettingsPage + + + Chat settings + Cap'n's orders for t' chat + + + + Custom alert words + + + + + Enable chat mentions + + + + + Enable mention completer + + + + + In-game message macros + + + + + Ignore chat room messages sent by unregistered users + + + + + Ignore private messages sent by unregistered users + + + + + Enable desktop notifications for private messages + + + + + Separate words with a space, alphanumeric characters only + + + + + + Invert text color + + + + + Enable desktop notification for mentions. + + + + + + (Color is hexadecimal) + + + + + Add message + + + + + Message: + + + + + PhasesToolbar + + + Untap step + Step where ye be untappin' + + + + Upkeep step + Step where ye be upkeepin' + + + + Draw step + Step where ye be drawin' + + + + First main phase + First main phase + + + + Beginning of combat step + Step where ye begin fightin' + + + + Declare attackers step + Step where ye be orderin' ye men to board + + + + Declare blockers step + Step where ye be orderin' ye men to repel boarders + + + + Combat damage step + Step where people get hurt + + + + End of combat step + Step where ye stop fightin' + + + + Second main phase + Second main phase + + + + End of turn step + Step where ye be endin' ye turn + + + + Player + + + &View library + &View library + + + + Move top cards to &graveyard... + Move top cards to Davy Jones' Locker + + + + View &top cards of library... + View &top cards o' library... + + + + &View graveyard + &View Davy Jones' Locker + + + + &View sideboard + &View sideplank + + + + Player "%1" + Matey "%1" + + + + + + + &Hand + &Hand + + + + &Reveal hand to... + &Reveal hand to... + + + + Reveal r&andom card to... + + + + + &Library + &Library + + + + + + + &Graveyard + Davy Jones' Locker + + + + &Sideboard + &Sideplank + + + + Red + Blood red + + + + Yellow + Fool's gold + + + + Green + Emerald green + + + + View top cards of library + View top cards o' library + + + + Number of cards: + Number o' cards: + + + + &Draw card + &Draw card + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + Pie&ces o' eight + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + + + + + + + &Bottom of library + + + + + &Move graveyard to... + + + + + &Move exile to... + + + + + Reveal &library to... + + + + + &Always reveal top card + + + + + O&pen deck in deck editor + O&pen flagship in dock + + + + &Undo last draw + + + + + Play top card &face down + + + + + Move top cards to &exile... + + + + + Put top card on &bottom + + + + + Put bottom card &in graveyard + + + + + Cr&eate predefined token + + + + + C&ard + + + + + &All players + + + + + &Play + + + + + &Hide + + + + + Play &Face Down + + + + + &Tap + + + + + &Untap + + + + + Toggle &normal untapping + + + + + &Flip + + + + + &Peek at card face + + + + + &Clone + + + + + Attac&h to card... + + + + + Unattac&h + + + + + &Draw arrow... + + + + + &Increase power + + + + + &Decrease power + + + + + I&ncrease toughness + + + + + D&ecrease toughness + + + + + In&crease power and toughness + + + + + Dec&rease power and toughness + + + + + Set &power and toughness... + + + + + &Set annotation... + + + + + &Add counter (%1) + &Add piece o' eight (%1) + + + + &Remove counter (%1) + &Remove piece o' eight (%1) + + + + &Set counters (%1)... + &Set pieces o' eight (%1)... + + + + Draw cards + + + + + + + + Number: + + + + + Move top cards to grave + + + + + Move top cards to exile + + + + + Roll die + + + + + Number of sides: + + + + + Set power/toughness + + + + + Please enter the new PT: + + + + + Set annotation + + + + + Please enter the new annotation: + + + + + Set counters + Set pieces o' eight + + + + Cr&eate related card + + + + + QMenuBar + + + Services + + + + + Hide %1 + + + + + Hide Others + + + + + Show All + + + + + Preferences... + + + + + Quit %1 + + + + + About %1 + + + + + QObject + + + Cockatrice replays (*.cor) + + + + + Common deck formats (*.cod *.dec *.mwDeck) + Common flagship materials (*.cod *.dec *.mwDeck) + + + + All files (*.*) + + + + + RemoteDeckList_TreeModel + + + Name + Name + + + + ID + + + + + Upload time + + + + + RemoteReplayList_TreeModel + + + ID + + + + + Name + Name + + + + Players + + + + + Keep + + + + + Time started + + + + + Duration (sec) + + + + + RoomSelector + + + Rooms + + + + + Joi&n + + + + + Room + + + + + Description + + + + + Permissions + + + + + Players + + + + + Games + + + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + + + + SetsModel + + + Enabled + + + + + Set type + + + + + Set code + + + + + Long name + + + + + Release date + + + + + ShutdownDialog + + + &Reason for shutdown: + + + + + &Time until shutdown (minutes): + + + + + Shut down server + + + + + SoundSettingsPage + + + Choose path + + + + + Enable &sounds + + + + + Path to sounds directory: + + + + + Test system sound engine + + + + + Sound settings + Cap'n's orders for t' shanties + + + + Master volume requires QT5 + + + + + Master volume + + + + + TabAdmin + + + Update server &message + + + + + &Shut down server + + + + + &Reload configuration + + + + + Server administration functions + + + + + &Unlock functions + + + + + &Lock functions + + + + + Unlock administration functions + + + + + Do you really want to unlock the administration functions? + + + + + Administration + + + + + TabDeckEditor + + + &Print deck... + &Print flagship... + + + + &Close + + + + + &Edit sets... + &Edit fleets... + + + + Filters + + + + + &Clear all filters + + + + + Delete selected + + + + + Deck &name: + Flagship &name: + + + + &Comments: + + + + + Hash: + + + + + &New deck + Freshly formed flagship + + + + &Load deck... + Find a flagship... + + + + &Save deck + Record flag&ship + + + + Save deck &as... + Record flagship &as... + + + + Load deck from cl&ipboard... + Find a flagship from t' cl&ipplank + + + + Save deck to clip&board + Record flagship on t' clipplank + + + + &Analyze deck on deckstats.net + + + + + Open custom image folder + + + + + Open custom sets folder + + + + + Add card to &maindeck + Add card to t' &mainplank + + + + Add card to &sideboard + + + + + &Deck Editor + &Dock + + + + C&ard Database + + + + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + + Welcome + + + + + Hi! It seems like you're running this version of Cockatrice for the first time. +All the sets in the card database have been enabled. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + Ahoy, me matey! It be seemin' like ye be runnin' this version o' Cockatrice for t' first time! +All t' fleets in the dock be enabled. +Read more about changing the fleet order or decommissioning specific fleet and consequent effects in the "Edit Fleets" window. + + + + &Remove row + + + + + &Increment number + + + + + &Decrement number + + + + + Edit &tokens... + + + + + Deck: %1 + Flagship: %1 + + + + Are you sure? + + + + + The decklist has been modified. +Do you want to save the changes? + + + + + Load deck + Find a flagship + + + + + + Error + Cap'n? Thar be a problem + + + + The deck could not be saved. + T' flagship be failin' to be recordin'! + + + + + The deck could not be saved. +Please check that the directory is writable and try again. + + + + + Save deck + Record flagship + + + + TabDeckStorage + + + Local file system + + + + + Server deck storage + O'er ship's flagship port + + + + + Open in deck editor + Open in dock + + + + Upload deck + + + + + Download deck + + + + + + New folder + + + + + + Delete + + + + + Enter deck name + Enter flagship name + + + + This decklist does not have a name. +Please enter a name: + + + + + Unnamed deck + Unnamed flagship + + + + Delete local file + + + + + + + Are you sure you want to delete "%1"? + + + + + Name of new folder: + + + + + Delete remote folder + + + + + Delete remote deck + + + + + Deck storage + Flagship port + + + + TabGame + + + &Phases + + + + + &Game + + + + + Next &phase + + + + + Next &turn + + + + + &Remove all local arrows + + + + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + + Game &information + + + + + &Concede + + + + + &Leave game + + + + + C&lose replay + + + + + &Say: + + + + + Concede + + + + + Are you sure you want to concede this game? + + + + + Leave game + + + + + Are you sure you want to leave this game? + + + + + You are flooding the game. Please wait a couple of seconds. + + + + + You have been kicked out of the game. + + + + + REPLAY + + + + + TabMessage + + + Private &chat + + + + + &Leave + + + + + %1 - Private chat + + + + + This user is ignoring you. + + + + + Private message from + + + + + %1 has left the server. + + + + + %1 has joined the server. + + + + + TabReplays + + + Local file system + + + + + Server replay storage + + + + + + Watch replay + + + + + + Delete + + + + + Download replay + + + + + Toggle expiration lock + + + + + Delete local file + + + + + Are you sure you want to delete "%1"? + + + + + Delete remote replay + + + + + Are you sure you want to delete the replay of game %1? + + + + + Game replays + + + + + TabRoom + + + &Say: + + + + + Chat + + + + + &Room + + + + + &Leave room + + + + + &Clear chat + + + + + Chat Settings... + Cap'n's orders for t' chat... + + + + mentioned you. + + + + + Click to view + + + + + You are flooding the chat. Please wait a couple of seconds. + + + + + TabServer + + + Server + + + + + TabSupervisor + + + Are you sure? + + + + + There are still open games. Are you sure you want to quit? + + + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + + + + TabUserLists + + + Add to Buddy List + + + + + Add to Ignore List + + + + + Account + + + + + UserContextMenu + + + User &details + + + + + Private &chat + + + + + Show this user's &games + + + + + Add to &buddy list + + + + + Remove from &buddy list + + + + + Add to &ignore list + + + + + Remove from &ignore list + + + + + Kick from &game + + + + + Ban from &server + + + + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + + %1's games + + + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + + + + UserInfoBox + + + User information + + + + + Real name: + + + + + Pronouns: + + + + + Location: + + + + + User level: + + + + + Account Age: + + + + + Edit + + + + + Change password + + + + + Change avatar + + + + + Administrator + + + + + Moderator + + + + + Registered user + + + + + + Unregistered user + + + + + Unknown + + + + + Year + + + + + Years + + + + + Day + + + + + Days + + + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + + + + UserInterfaceSettingsPage + + + General interface settings + + + + + Enable notifications in taskbar + + + + + Notify in the taskbar for game events while you are spectating + + + + + &Double-click cards to play them (instead of single-click) + + + + + &Play all nonlands onto the stack (not the battlefield) by default + + + + + Annotate card text on tokens + + + + + Animation settings + Cap'n's orders for t' things that be movin' + + + + &Tap/untap animation + + + + + UserList + + + Users connected to server: %1 + + + + + Users in this room: %1 + + + + + Buddies online: %1 / %2 + + + + + Ignored users online: %1 / %2 + + + + + WndSets + + + Enable all sets + Enable all fleets + + + + Disable all sets + Disable all fleets + + + + Move selected set up + Move selected fleet up + + + + Move selected set to the top + Move selected fleet to the top + + + + Move selected set down + Move selected fleet down + + + + Move selected set to the bottom + Move selected fleet to the bottom + + + + hints: + + + + + Enable the sets that you want to have available in the deck editor + Enable t' fleets that ye want to be ready in the dock + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + Fleet formation be decidin' t' source that be used to load pics for a specific card + + + + Disabled sets will be used for loading images only if all the enabled sets failed + + + + + Edit sets + Edit fleets + + + + Success + + + + + The sets database has been saved successfully. + + + + + ZoneViewWidget + + + sort by name + + + + + sort by type + + + + + shuffle when closing + + + + + pile view + + + + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_es.ts b/cockatrice/translations/cockatrice_es.ts index 9f308307..b2e64af7 100644 --- a/cockatrice/translations/cockatrice_es.ts +++ b/cockatrice/translations/cockatrice_es.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... - &Establecer contador... + Poner contadore&s... - + Set counter - Establecer contador + Poner contadores - + New value for counter '%1': Nuevo valor para el contador '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Imagenes de la zona de fondo - + Hand background: - + Fondo para la mano - + Stack background: - + Fondo para la pila - + Table background: - + Fondo para la mesa - + Player info background: - + Fondo para la información del jugador - + Card back: - + Reverso de la carta - + Card rendering Renderizado de las cartas - + Display card names on cards having a picture Mostrar nombre de las cartas en aquellas que tengan imagen - + Scale cards on mouse over Cambiar tamaño de las cartas al pasar el ratón por encima - + Hand layout Disposición de la mano - + Display hand horizontally (wastes space) Mostrar la mano horizontalmente (desperdicia espacio) - + Enable left justification - + Habilitar justificación izquierda - + Table grid layout Disposición de la rejilla de la mesa - + Invert vertical coordinate Invertir coordenada vertical - + Minimum player count for multi-column layout: Número minimo de jugadores para usar la rejilla multicolumna: - - - - - + + + + + Choose path Elija ruta @@ -107,99 +107,104 @@ BanDialog - + ban &user name &usuario baneado - + ban &IP address dirección &IP baneada - + + ban client I&D + + + + Ban type Tipo de Ban - + &permanent ban - ban &permanente + acceso prohibido &permanentemente - + &temporary ban - ban &temporal + acceso prohibido &temporalmente - + &Days: &Días: - + &Hours: &Horas: - + &Minutes: &Minutos: - + Duration of the ban Duración del ban - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. - Por favor, introduce el motivo del ban -Se almacenará unicamente para moderadores y no podrá ser visto por la persona baneada. + Por favor, introduce el motivo del restricción al acceso +Se almacenará unicamente para moderadores y no podrá ser visto por la persona restringida. - + Please enter the reason for the ban that will be visible to the banned person. - Por favor, introduce el motivo del ban que será visible para la persona baneada. + Por favor, introduce el motivo de la restricción que será visible para la persona restringida. - + &OK - &Aceptar + Aceptar (&O) - + &Cancel &Cancelar - + Ban user from server Banear usuario del servidor - + Error Error - - You have to select a name-based or IP-based ban, or both. - Debes seleccionar banear por nombre, por IP o por ambos. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + Nuevos sets encontrados - + %1 new set(s) have been found in the card database. Do you want to enable them? - + %1 nuevo(s) set(s) han sido encontrados en la base de datos de cartas. ¿Quieres habilitarlos? @@ -230,30 +235,53 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona F/R + + CardFrame + + + Image + Imagen + + + + Description + Descripción + + + + Both + Ambos + + CardInfoText - + Name: Nombre: - + Mana cost: Coste de mana: - + + Color(s): + Color(es) + + + Card type: Tipo de carta: - + P / T: F / R: - + Loyalty: Lealtad: @@ -276,27 +304,32 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona Mostrar información completa - + Name: Nombre: - + Mana cost: Coste de mana: - + + Color(s): + + + + Card type: Tipo de carta: - + P / T: F / R: - + Loyalty: Lealtad: @@ -306,7 +339,7 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona &Power / toughness - &Fuerza / resistencia + Fuerza / resistencia (&P) @@ -560,12 +593,12 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona DeckEditorSettingsPage - + Nothing is here... yet No hay nada aquí... aún - + General General @@ -573,17 +606,17 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona DeckListModel - + Number Número - + Card Carta - + Price Precio @@ -605,42 +638,42 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona DeckViewContainer - - Load &local deck - Cargar mazo &local + + Load local deck + Cargar mazo desde la PC local - - Load d&eck from server - Cargar mazo del &servidor + + Load deck from server + Cargar mazo desde el servidor - + Ready to s&tart - Listo para &empezar + Listo para empezar (&T) - + S&ideboard unlocked - &Reserva desbloqueada - - - - S&ideboard locked - R&eserva bloqueada + Banquillo desbloqueado (&I) + S&ideboard locked + Banquillo bloqueado (&I) + + + Load deck Cargar mazo - + Error Error - + The selected file could not be loaded. El fichero seleccionado no puede cargarse. @@ -670,7 +703,7 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona O&K - &Aceptar + Aceptar (&K) @@ -686,37 +719,52 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Dirección: - + + Enter host name + + + + &Port: &Puerto: - + Player &name: &Nombre del jugador: - + P&assword: &Contraseña: - + &Save password &Guardar contraseña - + A&uto connect at start Conectarse a&utomaticamente al iniciar - + Connect to server Conectar con el servidor @@ -724,82 +772,92 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona DlgCreateGame - + &Description: &Descripción: - + &Password: &Contraseña: - + P&layers: &Jugadores: - + + Re&member settings + + + + Game type Tipo de partida - + Only &buddies can join Sólo los &amigos pueden participar - + Only &registered users can join Sólo los usuarios &registrados pueden participar - + Joining restrictions Restricciones de participación - + &Spectators can watch E&spectadores pueden observar - + Spectators &need a password to watch Los espectadores &necesitan contraseña para unirse - + Spectators can see &hands - + Los espectadores pueden ver las &manos - + Spectators can &chat Los espectadores pueden &chatear - + Spectators Espectadores - + + &Clear + + + + Create game Crear partida - + Game information Información de la partida - + Error Error - + Server error. Error del servidor. @@ -807,96 +865,170 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona DlgCreateToken - + &Name: &Nombre: - + Token Ficha - + C&olor: &Color: - + white blanco - + blue azul - + black negro - + red rojo - + green verde - + multicolor multicolor - + colorless incoloro - + &P/T: &F/R: - + &Annotation: &Anotación: - + &Destroy token when it leaves the table &Destruir la ficha cuando deje la mesa - + Token data Información de la ficha - + Show &all tokens Mostrar &todas los ficha - + Show tokens from this &deck Mostrar todas las fichas de este &mazo - + Choose token from list Elegir ficha de la lista - + Create token Crear ficha + + DlgEditAvatar + + + + No image chosen. + Ninguna imagen seleccionada + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + Para cambiar tu avatar, elige una nueva imagen. +Para eliminar tu avatar actual, confirma sin elegir una nueva imagen. + + + + Browse... + Buscar + + + + Change avatar + Cambiar avatar + + + + Open Image + Abrir imagen + + + + Image Files (*.png *.jpg *.bmp) + Archivos de imagen (*.png *.jpg *.bmp) + + + + Invalid image chosen. + La imagen seleccionada no es válida. + + + + DlgEditPassword + + + Old password: + Contraseña anterior: + + + + New password: + Nueva contraseña: + + + + Confirm new password: + Confirmar la nueva contraseña: + + + + Change password + Cambiar contraseña + + + + Error + Error + + + + The new passwords don't match. + Las nuevas contraseñas no coinciden. + + DlgEditTokens @@ -989,7 +1121,55 @@ Se almacenará unicamente para moderadores y no podrá ser visto por la persona The chosen name conflicts with an existing card or token. Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. - + El nombre elegido entra en conflicto con una carta o token ya existente. Asegúrate de activar el 'set de tokens' en el menú 'Editar sets..." para mostrarlos correctamente. + + + + DlgEditUser + + + Email: + Email: + + + + Pronouns: + Género: + + + + Neutral + Neutro + + + + Masculine + Masculino + + + + Feminine + Femenino + + + + Country: + País: + + + + Undefined + Indefinido + + + + Real name: + Nombre real: + + + + Edit user profile + Editar perfil de usuario @@ -1002,7 +1182,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Show &password protected games - + Mostrar juegos protegidos con contraseña @@ -1043,7 +1223,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Refrescar @@ -1053,12 +1233,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Cargar mazo del portapapeles - + + Error Error - + + Invalid deck list. Lista de mazo invalida. @@ -1071,22 +1253,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Cargar mazo + + DlgRegister + + + &Host: + &Host: + + + + &Port: + &Puerto: + + + + Player &name: + &Nombre del jugador: + + + + P&assword: + &Contraseña: + + + + Password (again): + + + + + Email: + Email: + + + + Email (again): + + + + + Pronouns: + Genero: + + + + Neutral + Neutro + + + + Masculine + Masculino + + + + Feminine + Femenino + + + + Undefined + Indefinido + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + País: + + + + Real name: + Nombre real: + + + + Register to server + Registrar en el servidor + + DlgSettings - - - + + + Error Error - + Unknown Error loading card database Error desconocido cargando la base de datos de cartas. - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1094,10 +1370,16 @@ Cockatrice may not function correctly with an invalid database You may need to rerun oracle to update your card database. Would you like to change your database location setting? - + Tu base de datos de cartas no es válida. + +Cockatrice puede no funcionar correctamente con una base de datos inválida. + +Podrías necesitar volver a ejecutar oracle para actualizar tu base de datos de cartas. + +¿Quieres cambiar la ubicación de tu base de datos? - + Your card database version is too old. This can cause problems loading card information or images @@ -1105,98 +1387,111 @@ This can cause problems loading card information or images Usually this can be fixed by rerunning oracle to to update your card database. Would you like to change your database location setting? - + Tu base de datos de cartas es demasiado antigua. + +Esto puede causar problemas al cargar la información o las imágenes de las cartas. + +Normalmente esto se soluciona volviendo a ejecutar oracle para actualizar tu base de datos. + +¿Quieres cambiar la ubicación de tu base de datos? - + File Error loading your card database. Would you like to change your database location setting? - + Error de archivo al cargar la base de datos de cartas. + +¿Quieres cambiar la ubicación de tu base de datos? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + La base de datos ha sido cargada pero no contiene cartas. + +¿Quieres cambiar la ubicación de tu base de datos? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached Would you like to change your database location setting? - + Tu base de datos de cartas no ha podido cargarse. + +Por favor, abre un ticket en http://github.com/Cockatrice/Cockatrice/issues adjuntando tu archivo cards.xml. + +¿Quieres cambiar la ubicación de tu base de datos? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues Would you like to change your database location setting? - + Estado de carga de base de datos desconocido. + +Por favor, abre un ticket en http://github.com/Cockatrice/Cockatrice/issues. + +¿Quieres cambiar la ubicación de tu base de datos? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? La ruta a tu directorio de mazos es invalida. ¿Deseas volver y seleccionar la ruta correcta? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? La ruta a tu directorio de imagenes de las cartas es invalida.¿Deseas volver y seleccionar la ruta correcta? - + Settings Preferencias - + General General - + Appearance Apariencia - + User Interface Interfaz de usuario - + Deck Editor Editor de mazos - + Chat Chat - + Sound + Sonido + + + + Shortcuts GameSelector - - - C&reate - C&rear - - - - &Join - E&ntrar - @@ -1206,7 +1501,7 @@ Would you like to change your database location setting? - + Error Error @@ -1251,37 +1546,47 @@ Would you like to change your database location setting? Estas siendo ignorado por el creador de la partida. - + Join game Entrar en la partida - + Password: Contraseña: - + Please join the respective room first. Por favor, entre primero en la sala respectiva. - + Games Partidas - + &Filter games - &Filtrar partidas + - + C&lear filter - &Limpiar filtros + - + + C&reate + + + + + &Join + + + + J&oin as spectator Entrar como e&spectador @@ -1306,32 +1611,32 @@ Would you like to change your database location setting? <1m ago - + hace <1m <5m ago - + hace <5m %1m ago - + hace %1m 1hr %1m ago - + hace 1h %1m %1hr ago - + hace %1h 5+ hrs ago - + hace >5h @@ -1352,7 +1657,7 @@ Would you like to change your database location setting? can chat - + pueden usar el chat @@ -1398,97 +1703,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Reajustar/Borrar Imagenes Descargadas - - - - - + + + + + Choose path Elija ruta - + Success Éxito - + Downloaded card pictures have been reset. - + Las imágenes descargadas de las cartas han sido reseteadas. - + Error Error - + One or more downloaded card pictures could not be cleared. - + Una o más imágenes descargadas de las cartas no han podido eliminarse. - + Personal settings Preferencias personales - + Language: Idioma: - + Download card pictures on the fly Descargar imagenes de las cartas al vuelo - - Download high-quality card pictures - Descargar imagenes de alta calidad + + Download card pictures from a custom URL + Descargar imágenes de las cartas desde una URL distinta - + + Custom Card Download URL: + URL de descarga de cartas personalizadas: + + + + Linking FAQ + Enlace a Preguntas Frecuentes + + + Paths Rutas - + Decks directory: Directorio de mazos: - + Replays directory: Directorio de replays: - + Pictures directory: Directorio de imagenes: - + Card database: - + Base de datos de cartas: - + Token database: - + Base de datos de tokens: - + Picture cache size: Tamaño del cache de imágenes: - - + + English Español (Spanish) @@ -1496,56 +1811,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Hay demasiadas conexiones simultaneas desde tu dirección. - + Scheduled server shutdown. Desconexión programada del servidor. - + Banned by moderator Baneado por el moderador - + Expected end time: %1 Tiempo de finalización estimado: %1 - + This ban lasts indefinitely. Este ban permanecerá indefinidamente. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Conexión cerrada - + The server has terminated your connection. Reason: %1 El servidor ha finalizado tu conexión. Motivo: %1 - + Scheduled server shutdown Desconexión programada del servidor - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1556,304 +1864,485 @@ Todos los juegos en curso se cerraran. Motivo para la desconexión: %1 - + Number of players Número de jugadores - + Please enter the number of players. Por favor, introduzca el número de jugadores. - - + + Player %1 Jugador %1 - + Load replay Cargar replay - + About Cockatrice Acerca de Cockatrice - + Version %1 Versión %1 - + Translators: Traductores: - + Project Manager: + Jefe de Proyecto: + + + + The server has reached its maximum user capacity, please check back later. - + + + Invalid username. + El nombre de usuario no es valido. + + + + You have been logged out due to logging in at another location. + + + + + + Success + Éxito + + + + Registration accepted. +Will now login. + Resgistro aceptado. +Iniciando sesión. + + + + Account activation accepted. +Will now login. + Activación de la cuenta aceptada. +Iniciando sesión. + + + Past Project Managers: - + Antiguos Jefes de Proyecto: - - Developers: - - - - - Our Developers - - - - - Help Develop! - - - - - Recognition Page - - - - - Help Translate! - - - - - Support: - - - - - Report an Issue - - - - - Suggest an Improvement - - - - - - - - - + Developers: + Desarrolladores: + + + + Our Developers + Nuestros desarrolladores + + + + Help Develop! + ¡Ayuda a desarrollar! + + + + Recognition Page + Página de identificación + + + + Help Translate! + ¡Ayuda a traducir! + + + + Support: + Soporte: + + - - - - + Report an Issue + Reporta un problema + + + + + + + + + + + + + + + + + + + + + Error Error - + Server timeout Tiempo de espera del servidor agotado - + Incorrect username or password. Please check your authentication information and try again. Nombre de usuario o contraseña incorrecto. Por favor, verifica tu información de autentificación y vuelve a intentarlo. - + There is already an active session using this user name. Please close that session first and re-login. Ya existe una sesión activa usando ese nombre de usuario. Por favor, cierra esa sesión primero y reintentalo. - + + You are banned until %1. Estás baneado hasta %1. - + + You are banned indefinitely. Estás baneado indefinidamente. - - This server requires user registration. - Este servidor requiere el registro de usuarios. + + This server requires user registration. Do you want to register now? + Este servidor requiere el registro de usuarios. ¿Quieres registrarte ahora? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + Activación de cuenta + + + Unknown login error: %1 Error de login desconocido: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + Tu nombre de usuario debe: + + + + is %1 - %2 characters long + Tener de %1 a %2 caracteres de longitud. + + + + can %1 contain lowercase characters + %1 Puede tener minúsculas. + + + + + + + NOT + NO + + + + can %1 contain uppercase characters + %1 Puede tener mayúsculas. + + + + can %1 contain numeric characters + %1 Puede tener caracteres númericos. + + + + can contain the following punctuation: %1 + Puede tener los siguientes signos de puntuación: %1 + + + + first character can %1 be a punctuation mark + El primer caracter %1 puede ser un signo de puntuación. + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + Sólo puedes usar A-Z, a-z, 0-9, _, ., y - en tu nombre de usuario + + + + + + + + Registration denied + Registro denegado + + + + Registration is currently disabled on this server + El registro está actualmente deshabilitado en este servidor + + + + There is already an existing account with the same user name. + Ya existe una cuenta con ese nombre + + + + It's mandatory to specify a valid email address when registering. + Es obligatorio especificar una dirección de correo válida al registrarse. + + + + Too many registration attempts from your IP address. + Demasiados intentos de registro desde tu dirección IP. + + + + Password too short. + La contraseña es demasiado corta. + + + + Registration failed for a technical problem on the server. + El registro falló debido a un problema técnico en el servidor. + + + + Unknown registration error: %1 + + + + + Account activation failed + La activación de cuenta falló. + + + Socket error: %1 Error del Socket: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Estás intentando conectar a un servidor obsoleto. Por favor, usa una versión anterior de Cockatrice o conecta a un servidor apropiado. La versión local es %1, la versión remota es %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Tu cliente de Cockatrice esta obsoleto. Por favor, actualiza tu versión de Cockatrice. La versión local es %1, la versión remota es %2. - + Connecting to %1... Conectando a %1... - + + Registering to %1 as %2... + Registrando en %1 como %2... + + + Disconnected Desconectado - + Connected, logging in at %1 Conectado, hora de login: %1 - - Logged in as %1 at %2 - Connectado como %1 en %2 - - - + &Connect... &Conectar... - + &Disconnect &Desconectar - + Start &local game... Empezar partida &local... - + &Watch replay... &Ver replay... - + &Deck editor Editor de &mazos - + &Full screen &Pantalla completa - + + &Register to server... + &Registrarse en el servidor... + + + &Settings... &Preferencias... - - + + &Exit &Salir - + A&ctions A&cciones - + &Cockatrice &Cockatrice - + &About Cockatrice &Acerca de Cockatrice - + &Help A&yuda - - Check card updates... - + + Check for card updates... + Comprueba las actualizaciones de las cartas... - - + + A card database update is already running. + La actualización de la carta ya está en marcha. + + + + Unable to run the card database updater: + Imposible iniciar el actualizador de cartas: + + + + The card database updater exited with an error: %1 + El actualizador de cartas terminó con el error: %1 + + + + Update completed successfully. Cockatrice will now reload the card database. + La actualización de cartas ha terminado correctamente. La base de datos de cartas se reiniciará ahora. + + + + Information + Información + + + + Troubleshooting + Problemas frecuentes + + + + F.A.Q. + Preguntas frecuentes + + + + Your account has not been activated yet. +You need to provide the activation token received in the activation email - - A card update is already ongoing. - - - - - Unable to run the card updater: - - - - + failed to start. - + falló al iniciar. - + crashed. - + falló. - + timed out. - + tiempo agotado. - + write error. - + error de escritura. - + read error. - + error de lectura. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + error desconocido. @@ -1941,7 +2430,7 @@ La versión local es %1, la versión remota es %2. %1 has loaded a deck with %2 sideboard cards (%3). - + %1 ha cargado un mazo con %2 cartas en el banquillo (%3). @@ -2093,62 +2582,62 @@ La versión local es %1, la versión remota es %2. del exilio - + the bottom card of %1's library la carta de la parte inferior de la biblioteca de %1 - + the bottom card of his library la carta de la parte inferior de su biblioteca - + the bottom card of her library la carta de la parte inferior de su biblioteca - + from the bottom of %1's library desde la parte inferior de la biblioteca de %1 - + from the bottom of his library de la parte inferior de su biblioteca - + from the bottom of her library de la parte inferior de su biblioteca - + the top card of %1's library la carta superior de la biblioteca de %1 - + the top card of his library la parte superior de su biblioteca - + the top card of her library la carta superior de su biblioteca - + from the top of %1's library desde la parte superior de la biblioteca de %1 - + from the top of his library desde la parte superior de su biblioteca - + from the top of her library desde la parte superior de su biblioteca @@ -2551,304 +3040,304 @@ La versión local es %1, la versión remota es %2. %1 remueve %n %2 contador en %3 (ahora %4).%1 remueve %n %2 contadores en %3 (ahora %4). - + %1 taps her permanents. female %1 gira sus permanentes. - + %1 untaps her permanents. female %1 endereza sus permanentes. - + %1 taps his permanents. male %1 gira sus permanentes. - + %1 untaps his permanents. male %1 endereza sus permanentes. - + %1 taps %2. female %1 gira %2. - + %1 untaps %2. female %1 endereza %2. - + %1 taps %2. male %1 gira %2. - + %1 untaps %2. male %1 endereza %2. - + %1 sets counter %2 to %3 (%4%5). female %1 establece los contadores de %2 a %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 establece los contadores de %2 a %3 (%4%5). - + %1 sets %2 to not untap normally. female %1 establece que %2 no se endereze normalmente. - + %1 sets %2 to not untap normally. male %1 establece que %2 no se endereze normalmente. - + %1 sets %2 to untap normally. female %1 establece que %2 se endereze normalmente. - + %1 sets %2 to untap normally. male %1 establece que %2 se endereze normalmente. - + %1 sets PT of %2 to %3. female %1 establece la F/R de %2 a %3. - + %1 sets PT of %2 to %3. male %1 establece la F/R de %2 a %3. - + %1 sets annotation of %2 to %3. female %1 establece la anotación de %2 a %3. - + %1 sets annotation of %2 to %3. male %1 establece la anotación de %2 a %3. + + + %1 is looking at %2. + female + %1 está mirando a %2. + %1 is looking at %2. - female + male %1 está mirando a %2. + + %1 is looking at the top %n card(s) %2. + female + %1 esta mirando la primera %n carta de %2.%1 esta mirando las primeras %n cartas de %2. + + + %1 is looking at the top %n card(s) %2. + male + %1 esta mirando la primera %n carta de %2.%1 esta mirando las primeras %n cartas de %2. + - - %1 is looking at %2. - male - %1 está mirando a %2. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1 esta mirando la primera %n carta de %2.%1 esta mirando las primeras %n cartas de %2. - - - %1 is looking at the top %n card(s) %2. - male - %1 esta mirando la primera %n carta de %2.%1 esta mirando las primeras %n cartas de %2. + %1 termina de mirar a %2. %1 stops looking at %2. - female - %1 termina de mirar a %2. - - - - %1 stops looking at %2. male %1 termina de mirar a %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 revela %2 a %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 revela %2 a %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 revela %2 a %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 revela %2 a %3. + + + %1 reveals %2. + female + %1 revela %2. + %1 reveals %2. - female - %1 revela %2. - - - - %1 reveals %2. male %1 revela %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 revela aleatoriamente %2%3 a %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 revela aleatoriamente %2%3 a %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 revela aleatoriamente %2%3 a %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 revela aleatoriamente %2%3 a %4. - + %1 randomly reveals %2%3. female %1 revela aleatoriamente %2%3. - + %1 randomly reveals %2%3. male %1 revela aleatoriamente %2%3. - + %1 peeks at face down card #%2. female %1 mira la carta bocaabajo #%2. - + %1 peeks at face down card #%2. male %1 mira la carta bocaabajo #%2. - + %1 peeks at face down card #%2: %3. female %1 mira la carta bocaabajo #%2: %3. - + %1 peeks at face down card #%2: %3. male %1 mira la carta bocaabajo #%2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 revela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 revela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 revela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 revela %2%3 a %4. - + %1 reveals %2%3. female %1 revela %2%3. - + %1 reveals %2%3. male %1 revela %2%3. - + %1 is now keeping the top card %2 revealed. %1 está manteniendo la carta superior %2 revelada. - + %1 is not revealing the top card %2 any longer. %1 ya no mantiene revelada la carta superior %2. - + It is now %1's turn. female Es el turno de %1. - + It is now %1's turn. male Es el turno de %1. - + a card una carta @@ -2911,12 +3400,12 @@ La versión local es %1, la versión remota es %2. - + ending phase fase de fin de turno - + untap step paso de enderezar @@ -2924,58 +3413,58 @@ La versión local es %1, la versión remota es %2. %1 draws %2 card(s). female - + %1 roba %2 carta(s). %1 draws %2 card(s). male - + %1 roba %2 carta(s). from play - + del juego from her graveyard - + de su cementerio from his graveyard - + de su cementerio from her hand - + de su mano from his hand - + de su mano %1 puts %2%3 into her graveyard. - + %1 pone %2%3 en su cementerio. %1 puts %2%3 into his graveyard. - + %1 pone %2%3 en su cementerio. %1 moves %2%3 to her hand. - + %1 mueve %2%3 a su mano. %1 moves %2%3 to his hand. - + %1 mueve %2%3 a su mano. @@ -3005,85 +3494,85 @@ La versión local es %1, la versión remota es %2. %1 places %2 %3 counter(s) on %4 (now %5). female - + %1 coloca %2 %3 contador(es) en %4 (ahora %5). %1 places %2 %3 counter(s) on %4 (now %5). male - + %1 coloca %2 %3 contador(es) en %4 (ahora %5). %1 removes %2 %3 counter(s) from %4 (now %5). female - + %1 quita %2 %3 contador(es) de %4 (ahora %5). %1 removes %2 %3 counter(s) from %4 (now %5). male - + %1 quita %2 %3 contador(es) de %4 (ahora %5). + + + + %1 is looking at the top %2 card(s) %3. + female + %1 está mirando las %2 primera(s) carta(s) %3. %1 is looking at the top %2 card(s) %3. - female - - - - - %1 is looking at the top %2 card(s) %3. male - + %1 está mirando las %2 primera(s) carta(s) %3. - + upkeep step paso de mantenimiento - + draw step paso de robar - + first main phase primera fase principal - + beginning of combat step paso de inicio de combate - + declare attackers step paso de declarar atacantes - + declare blockers step paso de declarar bloqueadores - + combat damage step paso de daño de combate - + end of combat step paso de fin de combate - + second main phase segunda fase principal - + It is now the %1. Ahora es el %1. @@ -3091,62 +3580,74 @@ La versión local es %1, la versión remota es %2. MessagesSettingsPage - + Chat settings Preferencias de Chat - + + Custom alert words + + + + Enable chat mentions + Habilitar las menciones en el chat + + + + Enable mention completer - + In-game message macros + Macros para mensajes durante la partida + + + + Ignore chat room messages sent by unregistered users - - Ignore unregistered users in main chat - Ignorar usuarios no registrados en el chat principal. + + Ignore private messages sent by unregistered users + - - Ignore chat room messages sent by unregistered users. - Ignorar mensajes enviados por usuarios no registrados. + + Enable desktop notifications for private messages + - - Ignore private messages sent by unregistered users. - Ignorar mensajes privados enviados por usuarios no registrados. + + Separate words with a space, alphanumeric characters only + - + + Invert text color Invertir el color del texto - - Enable desktop notifications for private messages. - Activar notificaciones de escritorio para mensajes privados. - - - + Enable desktop notification for mentions. Activar notificaciones de escritorio para las menciones. - + + (Color is hexadecimal) (El color es hexadecimal) - + Add message Añadir mensaje - + Message: Mensaje: @@ -3212,336 +3713,337 @@ La versión local es %1, la versión remota es %2. Player - + &View library &Ver biblioteca - + Move top cards to &graveyard... Mover cartas de la parte s&uperior de la biblioteca al cementerio... - + View &top cards of library... Ver cartas de la parte &superior de la biblioteca... - + &View graveyard Ver &Cementerio - + &View sideboard Ver &sideboard - + Player "%1" Jugador "%1" - - - + + + + &Hand &Mano - + &Reveal hand to... &Enseñar mano a... - + Reveal r&andom card to... Revelar una carta al &azar a... - + &Library &Biblioteca - - - + + + &Graveyard &Cementerio - + &Sideboard &Reserva - + Red - + Rojo - + Yellow - + Amarillo - + Green - + Verde - + View top cards of library Ver cartas de la parte superior de la biblioteca - + Number of cards: Número de cartas: - + &Draw card &Robar carta - + Reveal top cards of library - + Muestra las primeras cartas de la biblioteca - + Number of cards: (max. %1) - + Número de cartas: (máx. %1) - + &View exile Ver &exilio - - - + + + &Exile &Exilio - + Reveal t&op cards to... - + Mostrar las &primeras cartas a... - + D&raw cards... &Robar cartas... - + Take &mulligan Hacer &mulligan - + &Shuffle &Barajar - + &Counters &Contadores - + &Untap all permanents &Enderezar todos los permanentes - + R&oll die... &Lanzar dado... - + &Create token... Crear &Ficha... - + C&reate another token C&rea otra ficha - + S&ay D&ecir - + &Move hand to... &Mover a - - - - + + + + &Top of library &parte superior de la biblioteca - - - - + + + + &Bottom of library &fondo de la biblioteca - + &Move graveyard to... - + &Mover el cementerio a... - + &Move exile to... - + Mover las cartas exiliadas a... - + Reveal &library to... Enseñar &biblioteca a... - + &Always reveal top card &Siempre revelar la carta superior - + O&pen deck in deck editor &Abrir mazo en el editor de mazos - + &Undo last draw &Deshacer último robo - + Play top card &face down - + Jugar la primera carta &boca abajo - + Move top cards to &exile... Mover cartas superiores al &exilio... - + Put top card on &bottom Poner carta superior en la parte &inferior - + Put bottom card &in graveyard - + Poner la última carta &en el cementerio - + Cr&eate predefined token Cr&ear ficha predefinida - + C&ard C&arta - + &All players &Todos los jugadores - + &Play &Jugar - + &Hide &Ocultar - + Play &Face Down Jugar boca abajo - + &Tap &Girar - + &Untap &Enderezar - + Toggle &normal untapping Alternar enderezamiento &normal - + &Flip &Voltear - + &Peek at card face &Mirar el dorso de la carta - + &Clone &Clonar - + Attac&h to card... Ane&xar a una carta... - + Unattac&h Desane&xar - + &Draw arrow... &Dibujar flecha... - + &Increase power &Incrementar fuerza - + &Decrease power &Decrementar fuerza - + I&ncrease toughness I&ncrementar resistencia - + D&ecrease toughness D&ecrementar resistencia @@ -3551,22 +4053,22 @@ La versión local es %1, la versión remota es %2. In&crementar fuerza y resistencia - + Dec&rease power and toughness Dec&rementar fuerza y resistencia - + Set &power and toughness... Establecer &fuerza y resistencia... - + &Set annotation... E&scribir anotación... - + &Add counter (%1) &Añadir contador (%1) @@ -3576,118 +4078,123 @@ La versión local es %1, la versión remota es %2. &Quitar contador (%1) - + &Set counters (%1)... E&stablecer contadores (%1)... - + Draw cards Robar cartas - - - - + + + + Number: Número: - + Move top cards to grave Mover cartas superiores al cementerio - + Move top cards to exile Mover cartas superiores al exilio - + Roll die Lanzar dado - + Number of sides: Número de caras: - + Set power/toughness Establecer fuerza/resistencia - + Please enter the new PT: Por favor, introduzca la nueva F/R: - + Set annotation Escribir anotación - + Please enter the new annotation: Por favor, introduza la nueva anotación: - + Set counters Establecer contadores + + + Cr&eate related card + Cr&ear carta relacionada + QMenuBar - + Services Servicios - + Hide %1 - + Ocultar %1 - + Hide Others Ocultar otros - + Show All Mostrar todo - + Preferences... Preferencias... - + Quit %1 - + Salir %1 - + About %1 - + Sobre %1 QObject - + Cockatrice replays (*.cor) Replays de Cockatrice (*.cor) Common deck formats (*.cod *.dec *.mwDeck) - + Formatos de mazo comunes (*.cod *.dec *.mwDeck) @@ -3770,31 +4277,60 @@ La versión local es %1, la versión remota es %2. + Permissions + + + + Players Jugadores - + Games Partidas + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel Enabled - + Activado Set type - + Configurar tipo Set code - + Configurar código @@ -3820,7 +4356,7 @@ La versión local es %1, la versión remota es %2. &Tiempo hasta la desconexión (minutos): - + Shut down server Apagar servidor @@ -3828,80 +4364,85 @@ La versión local es %1, la versión remota es %2. SoundSettingsPage - + Choose path - + Elegir ubicación - + Enable &sounds - + Activar &sonidos - + Path to sounds directory: - + Ubicación de la carpeta de sonidos: - + Test system sound engine - + Probar el audio del sistema - + Sound settings - + Herramientas de sonido - + Master volume requires QT5 - + El volumen maestro necesita QT5 - + Master volume - + Volumen maestro TabAdmin - + Update server &message Actualizar &mensaje del servidor - + &Shut down server &Apagar servidor - + + &Reload configuration + Volver a carga&r la configuración + + + Server administration functions Funciones de administración del servidor - + &Unlock functions &Desbloquear funciones - + &Lock functions &Bloquear funciones - + Unlock administration functions Desbloquear funciones de administración - + Do you really want to unlock the administration functions? ¿Realmente quieres desbloquear las funciones de administración? - + Administration Administración @@ -3909,181 +4450,221 @@ La versión local es %1, la versión remota es %2. TabDeckEditor - + &Print deck... Im&primir mazo... - + &Close &Cerrar - + &Edit sets... &Editar ediciones... - - &Clear search - &Limpiar busqueda + + Filters + Filtros - + + &Clear all filters + &Limpiar filtros + + + + Delete selected + Borrar seleccionado + + + Deck &name: &Nombre del mazo: - + &Comments: &Comentarios: - + Hash: Hash: - + &New deck &Nuevo mazo - + &Load deck... &Cargar mazo... - + &Save deck &Guardar mazo - + Save deck &as... Guardar mazo &como... - + Load deck from cl&ipboard... Cargar mazo del &portapapeles... - + Save deck to clip&board Guardar mazo al p&ortapales - + &Analyze deck on deckstats.net A&nalizar mazo en deckstats.net - + Open custom image folder + Abrir carpeta de imágenes personalizadas + + + + Open custom sets folder - + Add card to &maindeck Añadir carta al &mazo principal - + Add card to &sideboard Añadir carta a la &reserva - + &Deck Editor - + &Editor de mazos - + C&ard Database + Base de datos de c&artas + + + + Show/Hide card information - + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - + Bienvenido/a - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. - +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + ¡Hola! Parece que es la primera vez que ejecutas esta versión de Cockatrice. Todos los sets de la base de datos de cartas han sido habilitados. Puedes leer más sobre cambiar el orden de los sets o deshabilitar sets específicos en la ventana "Editar sets". - - Show card text only - - - - + &Remove row &Eliminar columna - + &Increment number &Incrementar número - + &Decrement number &Decrementar número - + Edit &tokens... Editar &fichas... - + Deck: %1 Mazo: %1 - + Are you sure? ¿Estás seguro? - + The decklist has been modified. Do you want to save the changes? La lista del mazo ha sido modificada ¿Deseas guardar los cambios? - + Load deck Cargar mazo - - - + + + Error Error - + The deck could not be saved. El mazo no puede ser guardado. - - + + The deck could not be saved. Please check that the directory is writable and try again. El mazo no puede ser guardado Por favor, compruebe que tiene permisos de escritura en el directorio e intentelo de nuevo. - + Save deck Guardar mazo @@ -4175,100 +4756,105 @@ Por favor, introduzca un nombre: Deck storage - + Almacenamiento de mazos TabGame - + &Phases &Fases - + &Game &Partida - + Next &phase Próxima &fase - + Next &turn Próximo &turno - + &Remove all local arrows &Retirar todas las flechas locales - + + Rotate View Cl&ockwise + Girar en sentido horario + + + + Rotate View Co&unterclockwise + Girar en sentido antih&orario &U + + + Game &information &Información de la partida - + &Concede &Conceder - + &Leave game &Abandonar la partida - + C&lose replay &Cerrar replay - + &Say: &Decir: - + Concede Conceder - + Are you sure you want to concede this game? ¿Estás seguro de que quieres conceder esta partida? - + Leave game Abandonar la partida - + Are you sure you want to leave this game? ¿Estás seguro de que quieres abandonar la partida? - + You are flooding the game. Please wait a couple of seconds. - + Estás saturando el juego. Por favor, espera unos minutos. - + You have been kicked out of the game. - + Has sido expulsado de la partida. - - Replay %1: %2 - Replay %1: %2 - - - - Game %1: %2 - Partida %1: %2 + + REPLAY + RESPONDER @@ -4286,7 +4872,7 @@ Por favor, introduzca un nombre: %1 - Private chat - + %1 - Chat privado @@ -4312,107 +4898,107 @@ Por favor, introduzca un nombre: TabReplays - + Local file system Sistema de archivos local - + Server replay storage Almacen de replays del servidor - + Watch replay Ver replay - - + + Delete Borrar - + Download replay Descargar replay - + Toggle expiration lock Establecer expiración del bloqueo - + Delete local file Borrar fichero local - + Are you sure you want to delete "%1"? ¿Estás seguro de que quieres borrar "%1"? - + Delete remote replay Borrar replay remoto - + Are you sure you want to delete the replay of game %1? ¿Estás seguro de que quieres borar el replay de la partida %1? Game replays - + Repetición de partidas TabRoom - + &Say: &Decir: - + Chat Chat - + &Room &Sala - + &Leave room &Dejar sala - + &Clear chat - + &Limpiar chat - + Chat Settings... - + Herramientas del Chat... - + mentioned you. te ha mencionado. - + Click to view Click para ver - + You are flooding the chat. Please wait a couple of seconds. Estás floodeando el chat. Por favor, espera unos segundos. @@ -4428,13 +5014,23 @@ Por favor, introduzca un nombre: TabSupervisor - + Are you sure? ¿Estás seguro? - + There are still open games. Are you sure you want to quit? + Todavía hay juegos abiertos. ¿Estás seguro/a de que quieres salir? + + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. @@ -4459,169 +5055,301 @@ Por favor, introduzca un nombre: UserContextMenu - + User &details &Detalles del usuario - + Private &chat &Chat privado - + Show this user's &games Mostrar &partidas de este usuario - + Add to &buddy list Añadir a la lista de &amigos - + Remove from &buddy list Quitar de la lista de &amigos - + Add to &ignore list Añadir a la lista de &ignorados - + Remove from &ignore list Quitar de la lista de &ignorados - + Kick from &game Expulsar de la &partida - + Ban from &server Banear del &servidor - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games Partidas de %1 + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Información del usuario - + Real name: Nombre real: - - Gender: + + Pronouns: Género: - + Location: Localización: - + User level: Nivel de usuario: - + Account Age: - + Tiempo de la cuenta: - + + Edit + Editar + + + + Change password + Cambiar contraseña + + + + Change avatar + Cambiar avatar + + + Administrator Administrador - + Moderator Moderador - + Registered user Usuario registrado - - + + Unregistered user Usuario no registrado - + Unknown Desconocido - + Year Año - + Years Años - + Day Día - + Days Días + + + + + Information + Información + + + + User information updated. + Información del usuario actualizada. + + + + + + + + + + + Error + Error + + + + This server does not permit you to update your user informations. + Este servidor no permite actualizar su información de usuario. + + + + + An error occured while trying to update your user informations. + Ocurrió un error mientras se actualizaba su información de usuario. + + + + Password changed. + Su contraseña ha sido cambiada. + + + + This server does not permit you to change your password. + Este servidor no le permite cambiar su contraseña. + + + + The new password is too short. + La nueva contraseña es muy corta + + + + The old password is incorrect. + La contraseña anterior no es correcta. + + + + Avatar updated. + Avatar actualizado + + + + This server does not permit you to update your avatar. + Este servidor no permite actualizar avatar. + + + + An error occured while trying to updater your avatar. + Ocurrió un error mientras se actualizaba su avatar. + UserInterfaceSettingsPage - + General interface settings Preferencias generales de la interfaz - + Enable notifications in taskbar - + Habilitar notificaciones en la barra de tareas - + Notify in the taskbar for game events while you are spectating - + Notificar en la barra de tareas los eventos de la partida mientras estás como espectador - + &Double-click cards to play them (instead of single-click) &Doble click en las cartas para jugarlas (en lugar de un solo click) - + &Play all nonlands onto the stack (not the battlefield) by default - + &Jugar todas las cartas que no sean tierras en la pila (en lugar de en el campo de batalla) por defecto. - + + Annotate card text on tokens + Anotar en las fichas. + + + Animation settings Opciones de animación - + &Tap/untap animation Animación de &girar/enderezar @@ -4629,22 +5357,22 @@ Por favor, introduzca un nombre: UserList - + Users connected to server: %1 - + Usuarios conectados al servidor: %1 - + Users in this room: %1 Usuarios en esta sala: %1 - + Buddies online: %1 / %2 Amigos online: %1 / %2 - + Ignored users online: %1 / %2 Usuarios ignorados online: %1 / %2 @@ -4654,39 +5382,56 @@ Por favor, introduzca un nombre: Enable all sets - + Seleccionar todos los sets Disable all sets - + Deseleccionar todos los sets Move selected set up - + Mover arriba el set seleccionado + + + + Move selected set to the top + Mover el set seleccionado al principio Move selected set down - - - - - Move selected set to top - + Mover abajo el set seleccionado - Move selected set to bottom - + Move selected set to the bottom + Mover el set seleccionado al final - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + consejos: + + + + Enable the sets that you want to have available in the deck editor + Selecciona los sets que quieres tener activos en el editor de mazos + + + + Move sets around to change their order, or click on a column header to sort sets on that field + Mueve los sets para cambiar el orden o haz click en el encabezado de una columna para ordenar los sets por ese campo + + + + Sets order decides the source that will be used when loading images for a specific card + El orden de los sets determina el origen que será usado al cargar las imágenes de una carta específica + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4695,14 +5440,14 @@ Disabled sets will still be used for loading images. Editar ediciones - + Success Éxito - + The sets database has been saved successfully. - + La base de datos de los sets se ha guardado correctamente. @@ -4725,6 +5470,563 @@ Disabled sets will still be used for loading images. pile view + vista de la pila + + + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play diff --git a/cockatrice/translations/cockatrice_et.ts b/cockatrice/translations/cockatrice_et.ts index 7b27e1ae..cde1c3c4 100644 --- a/cockatrice/translations/cockatrice_et.ts +++ b/cockatrice/translations/cockatrice_et.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... - + Set counter - + New value for counter '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures - + Hand background: Käe taustapilt: - + Stack background: - + Table background: Laua taustapilt: - + Player info background: Mängija taustainfo: - + Card back: Kaart tagasi: - + Card rendering - + Display card names on cards having a picture Kuva piltidega kaartidel kaardi nimi - + Scale cards on mouse over - + Hand layout Käe välimus - + Display hand horizontally (wastes space) Kuva käsi horisontaalselt (raiskab ruumi) - + Enable left justification - + Table grid layout - + Invert vertical coordinate - + Minimum player count for multi-column layout: - - - - - + + + + + Choose path Vali sihtkoht @@ -107,97 +107,102 @@ BanDialog - + ban &user name blokeeri &kasutajanimi - + ban &IP address blokeeri &IP-aadress - + + ban client I&D + + + + Ban type Blokeerimise tüüp - + &permanent ban &igavene blokeering - + &temporary ban &ajutine blokeering - + &Days: &Päeva: - + &Hours: &Tundi: - + &Minutes: &Minutit - + Duration of the ban Blokeerimise kestvus - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Palun sisestage blokeerimise põhjus. See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. - + Please enter the reason for the ban that will be visible to the banned person. Palun sisestage blokeerimise põhjus, mis on nähtav blokeeritavale. - + &OK &OK - + &Cancel &Tühista - + Ban user from server Blokeeri kasutaja serverist - + Error Viga - - You have to select a name-based or IP-based ban, or both. - Peate valima, kas nimepõhise või IP-põhise blokeeringu või mõlemad. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe.P/T + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: Nimi: - + Mana cost: - + + Color(s): + + + + Card type: Kaardi tüüp: - + P / T: P / T: - + Loyalty: Hinnang: @@ -276,27 +304,32 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe.Kuva kogu info - + Name: Nimi: - + Mana cost: - + + Color(s): + + + + Card type: Kaardi tüüp: - + P / T: P / T: - + Loyalty: Hinnang: @@ -560,12 +593,12 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. DeckEditorSettingsPage - + Nothing is here... yet - + General Üldine @@ -573,17 +606,17 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. DeckListModel - + Number Number - + Card Kaart - + Price Hind @@ -605,42 +638,42 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. DeckViewContainer - - Load &local deck + + Load local deck - - Load d&eck from server + + Load deck from server - + Ready to s&tart Valmis a&lustama - + S&ideboard unlocked - + S&ideboard locked - + Load deck - + Error Viga - + The selected file could not be loaded. Valitud faili ei suudetud laadida. @@ -686,37 +719,52 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: - + + Enter host name + + + + &Port: &Port: - + Player &name: Mängija &nimi: - + P&assword: P&arool: - + &Save password &Salvesta parool - + A&uto connect at start Ü&hendu käivitumisel automaatselt - + Connect to server Ühenda serveriga @@ -724,82 +772,92 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. DlgCreateGame - + &Description: &Kirjeldus: - + &Password: &Parool: - + P&layers: M&ängijad: - + + Re&member settings + + + + Game type Mängu tüüp - + Only &buddies can join Ainult &sõbrad saavad liituda - + Only &registered users can join Ainult &registreeritud kasutajad saavad liituda - + Joining restrictions - + &Spectators can watch - + Spectators &need a password to watch - + Spectators can see &hands - + Spectators can &chat - + Spectators - + + &Clear + + + + Create game Loo mäng - + Game information Mängu info - + Error Viga - + Server error. Serveri viga. @@ -807,96 +865,169 @@ See on mõeldud ainult moderaatoritele ja blokeeritav isik seda ei näe. DlgCreateToken - + &Name: &Nimi: - + Token - + C&olor: V&ärv: - + white valge - + blue sinine - + black must - + red punane - + green roheline - + multicolor segu - + colorless värvitu - + &P/T: - + &Annotation: - + &Destroy token when it leaves the table - + Token data - + Show &all tokens - + Show tokens from this &deck - + Choose token from list - + Create token + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Värskenda @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di - + + Error Viga - + + Invalid deck list. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1097,7 +1372,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1108,21 +1383,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1131,7 +1406,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1140,63 +1415,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User Interface - + Deck Editor - + Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - - - - - &Join - - @@ -1206,7 +1476,7 @@ Would you like to change your database location setting? - + Error @@ -1251,37 +1521,47 @@ Would you like to change your database location setting? - + Join game - + Password: - + Please join the respective room first. - + Games - + &Filter games - + C&lear filter - + + C&reate + + + + + &Join + + + + J&oin as spectator @@ -1398,97 +1678,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path - + Success - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Personal settings - + Language: - + Download card pictures on the fly - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths - + Decks directory: - + Replays directory: - + Pictures directory: - + Card database: - + Token database: - + Picture cache size: - - + + English Eesti Keel (Estonian) @@ -1496,357 +1786,529 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. - + Scheduled server shutdown. - + Banned by moderator - + Expected end time: %1 - + This ban lasts indefinitely. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed - + The server has terminated your connection. Reason: %1 - + Scheduled server shutdown - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 - + Number of players - + Please enter the number of players. - - + + Player %1 - + Load replay - + About Cockatrice - + Version %1 - + Translators: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error - + Server timeout - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. - + + You are banned until %1. - + + You are banned indefinitely. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. - + Connecting to %1... - + + Registering to %1 as %2... + + + + Disconnected - + Connected, logging in at %1 - - Logged in as %1 at %2 - - - - + &Connect... - + &Disconnect - + Start &local game... - + &Watch replay... - + &Deck editor - + &Full screen - + + &Register to server... + + + + &Settings... - - + + &Exit - + A&ctions - + &Cockatrice - + &About Cockatrice - + &Help - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2085,62 +2547,62 @@ Local version is %1, remote version is %2. - + the bottom card of %1's library - + the bottom card of his library - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library - + from the bottom of her library - + the top card of %1's library - + the top card of his library - + the top card of her library - + from the top of %1's library - + from the top of his library - + from the top of her library @@ -2543,304 +3005,304 @@ Local version is %1, remote version is %2. - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - + %1 sets %2 to not untap normally. male - + %1 sets %2 to untap normally. female - + %1 sets %2 to untap normally. male - + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male + + + %1 is looking at %2. + female + + %1 is looking at %2. - female + male + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + - - %1 is looking at %2. - male - - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - - - %1 is looking at the top %n card(s) %2. - male - + %1 stops looking at %2. - female - - - - - %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male + + + %1 reveals %2. + female + + %1 reveals %2. - female - - - - - %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - + %1 randomly reveals %2%3 to %4. p1 male, p2 female - + %1 randomly reveals %2%3 to %4. p1 male, p2 male - + %1 randomly reveals %2%3. female - + %1 randomly reveals %2%3. male - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - + a card @@ -2903,12 +3365,12 @@ Local version is %1, remote version is %2. - + ending phase - + untap step @@ -3018,64 +3480,64 @@ Local version is %1, remote version is %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step - + draw step - + first main phase - + beginning of combat step - + declare attackers step - + declare blockers step - + combat damage step - + end of combat step - + second main phase - + It is now the %1. @@ -3083,62 +3545,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message - + Message: @@ -3204,336 +3678,337 @@ Local version is %1, remote version is %2. Player - + &View library - + Move top cards to &graveyard... - + View &top cards of library... - + &View graveyard - + &View sideboard - + Player "%1" + + + + + + &Hand + + + + + &Reveal hand to... + + + + + Reveal r&andom card to... + + + + + &Library + + + + + + + + &Graveyard + + + + + &Sideboard + + + + + Red + + + + + Yellow + + + + + Green + + + + + View top cards of library + + + + + Number of cards: + + + + + &Draw card + + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + - - &Hand - - - - - &Reveal hand to... - - - - - Reveal r&andom card to... - - - - - &Library - - - - - - - - &Graveyard - - - - - &Sideboard - - - - - Red - - - - - Yellow - - - - - Green - - - - - View top cards of library - - - - - Number of cards: - - - - - &Draw card - - - - - Reveal top cards of library - - - - - Number of cards: (max. %1) - - - - - &View exile - - - - - - - - &Exile - - - - Reveal t&op cards to... - - - - - D&raw cards... - - - - - Take &mulligan - - - - - &Shuffle - - - - - &Counters - - - - - &Untap all permanents - - - - - R&oll die... - - - - - &Create token... - - - - - C&reate another token - - - - - S&ay - - - - - &Move hand to... - - - - - - - - &Top of library - - - - - - - + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card - + O&pen deck in deck editor - + &Undo last draw - + Play top card &face down - + Move top cards to &exile... - + Put top card on &bottom - + Put bottom card &in graveyard - + Cr&eate predefined token - + C&ard - + &All players - + &Play - + &Hide - + Play &Face Down - + &Tap - + &Untap - + Toggle &normal untapping - + &Flip - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Increase power - + &Decrease power - + I&ncrease toughness - + D&ecrease toughness @@ -3543,22 +4018,22 @@ Local version is %1, remote version is %2. - + Dec&rease power and toughness - + Set &power and toughness... - + &Set annotation... - + &Add counter (%1) @@ -3568,103 +4043,108 @@ Local version is %1, remote version is %2. - + &Set counters (%1)... - + Draw cards - - - - + + + + Number: - + Move top cards to grave - + Move top cards to exile - + Roll die - + Number of sides: - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3672,7 +4152,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) @@ -3762,14 +4242,43 @@ Local version is %1, remote version is %2. - Players + Permissions + Players + + + + Games + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3812,7 +4321,7 @@ Local version is %1, remote version is %2. - + Shut down server @@ -3820,37 +4329,37 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3858,42 +4367,47 @@ Local version is %1, remote version is %2. TabAdmin - + Update server &message - + &Shut down server - + + &Reload configuration + + + + Server administration functions - + &Unlock functions - + &Lock functions - + Unlock administration functions - + Do you really want to unlock the administration functions? - + Administration @@ -3901,179 +4415,219 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... - + &Close - + &Edit sets... - - &Clear search + + Filters - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: - + &Comments: - + Hash: - + &New deck - + &Load deck... - + &Save deck - + Save deck &as... - + Load deck from cl&ipboard... - + Save deck to clip&board - + &Analyze deck on deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck - + Add card to &sideboard - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row - + &Increment number - + &Decrement number - + Edit &tokens... - + Deck: %1 - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4170,93 +4724,98 @@ Please enter a name: TabGame - + &Phases - + &Game - + Next &phase - + Next &turn - + &Remove all local arrows - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information - + &Concede - + &Leave game - + C&lose replay - + &Say: - + Concede - + Are you sure you want to concede this game? - + Leave game - + Are you sure you want to leave this game? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 - - - - - Game %1: %2 + + REPLAY @@ -4301,54 +4860,54 @@ Please enter a name: TabReplays - + Local file system - + Server replay storage - + Watch replay - - + + Delete - + Download replay - + Toggle expiration lock - + Delete local file - + Are you sure you want to delete "%1"? - + Delete remote replay - + Are you sure you want to delete the replay of game %1? @@ -4361,47 +4920,47 @@ Please enter a name: TabRoom - + &Say: - + Chat - + &Room - + &Leave room - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. @@ -4417,15 +4976,25 @@ Please enter a name: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4448,169 +5017,301 @@ Please enter a name: UserContextMenu - + User &details - + Private &chat - + Show this user's &games - + Add to &buddy list - + Remove from &buddy list - + Add to &ignore list - + Remove from &ignore list - + Kick from &game - + Ban from &server - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information - + Real name: - - Gender: + + Pronouns: - + Location: - + User level: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator - + Moderator - + Registered user - - + + Unregistered user - + Unknown - + Year - + Years - + Day - + Days + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings - + &Tap/untap animation @@ -4618,22 +5319,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 - + Buddies online: %1 / %2 - + Ignored users online: %1 / %2 @@ -4655,27 +5356,44 @@ Please enter a name: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4684,12 +5402,12 @@ Disabled sets will still be used for loading images. - + Success - + The sets database has been saved successfully. @@ -4717,4 +5435,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_fr.ts b/cockatrice/translations/cockatrice_fr.ts index 2dcec4c2..82d269d8 100644 --- a/cockatrice/translations/cockatrice_fr.ts +++ b/cockatrice/translations/cockatrice_fr.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... &Nombre de marqueurs... - + Set counter Nombre de marqueurs - + New value for counter '%1': Nouvelle valeur du compteur '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Zone images de fond - + Hand background: Image de fond de la zone de main: - + Stack background: Image de fond de la pile: - + Table background: Image de fond de la zone de jeu: - + Player info background: Image de fond de la zone d'informations joueur: - + Card back: Dos de carte: - + Card rendering Rendu des cartes - + Display card names on cards having a picture Afficher le nom des cartes ayant une image - + Scale cards on mouse over - + Agrandir les cartes sous le curseur - + Hand layout Disposition de la main - + Display hand horizontally (wastes space) Afficher la main horizontalement (perte d'espace) - + Enable left justification Activer justification à gauche - + Table grid layout Disposition en forme de grille - + Invert vertical coordinate Inverser la disposition du champ de bataille - + Minimum player count for multi-column layout: Nombre minimum de joueurs pour la disposition multi-colonnes : - - - - - + + + + + Choose path Choisir le chemin @@ -107,99 +107,104 @@ BanDialog - + ban &user name bloquer &nom d'utilisateur - + ban &IP address bloquer &adresse IP - + + ban client I&D + + + + Ban type Type du blocage - + &permanent ban &blocage permanent - + &temporary ban &blocage temporaire - + &Days: &Jours: - + &Hours: &Heures: - + &Minutes: &Minutes: - + Duration of the ban Durée du blocage - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Veuillez expliquer la raison du blocage. Cette information sera consultable uniquement par les modérateurs. - + Please enter the reason for the ban that will be visible to the banned person. Veuillez expliquer la raison du bannissement. Cette information sera visible par la personne bannie. - + &OK &OK - + &Cancel &Annuler - + Ban user from server Bannir le joueur du serveur - + Error Érreur - - You have to select a name-based or IP-based ban, or both. - Vous devez choisir un blocage à partir du nom ou de l'IP, ou des deux. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + Nouvelles éditions trouvées - + %1 new set(s) have been found in the card database. Do you want to enable them? - + %1 nouvelle(s) édition(s) détectée(s) dans la base de données. Voulez-vous les activer ? @@ -230,30 +235,53 @@ Cette information sera consultable uniquement par les modérateurs.F/E + + CardFrame + + + Image + Image + + + + Description + Description + + + + Both + Les deux + + CardInfoText - + Name: Nom: - + Mana cost: Coût de mana: - + + Color(s): + + + + Card type: Type: - + P / T: F / E: - + Loyalty: Loyauté: @@ -276,27 +304,32 @@ Cette information sera consultable uniquement par les modérateurs.Montrer toutes les informations - + Name: Nom: - + Mana cost: Cout de mana: - + + Color(s): + + + + Card type: Type de carte: - + P / T: F / E: - + Loyalty: Loyauté: @@ -560,12 +593,12 @@ Cette information sera consultable uniquement par les modérateurs. DeckEditorSettingsPage - + Nothing is here... yet Il n'y a rien ici pour l'instant - + General Géneral @@ -573,17 +606,17 @@ Cette information sera consultable uniquement par les modérateurs. DeckListModel - + Number Nombre - + Card Carte - + Price Prix @@ -605,42 +638,42 @@ Cette information sera consultable uniquement par les modérateurs. DeckViewContainer - - Load &local deck - Charger un deck &local + + Load local deck + Charger un deck local - - Load d&eck from server - Charger un d&eck depuis le serveur + + Load deck from server + Charger un deck depuis le serveur - + Ready to s&tart P&rêt à démarrer - + S&ideboard unlocked Réserve débloquée - + S&ideboard locked Réserve bloquée - + Load deck Charger deck - + Error Erreur - + The selected file could not be loaded. Le fichier sélectionné n'a pas pu être chargé. @@ -686,37 +719,52 @@ Cette information sera consultable uniquement par les modérateurs. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Hôte: - + + Enter host name + + + + &Port: &Port: - + Player &name: &Nom du joueur: - + P&assword: Mot de p&asse: - + &Save password &Se souvenir du mot de passe - + A&uto connect at start A&uto connexion au démarrage - + Connect to server Connexion au serveur @@ -724,82 +772,92 @@ Cette information sera consultable uniquement par les modérateurs. DlgCreateGame - + &Description: &Description: - + &Password: Mot de &Passe: - + P&layers: &Joueurs: - + + Re&member settings + + + + Game type Type de partie - + Only &buddies can join Seuls les &amis peuvent rejoindre - + Only &registered users can join Seules les personnes en&registrées peuvent rejoindre - + Joining restrictions Conditions pour rejoindre - + &Spectators can watch Les &spectateurs peuvent observer. - + Spectators &need a password to watch Les spectateurs ont besoin d'un &mot-de-passe pour observer. - + Spectators can see &hands Les spectateurs peuvent voir les &mains des joueurs. - + Spectators can &chat Les spectateurs peuvent dis&cuter - + Spectators Spectateurs - + + &Clear + + + + Create game Créer partie - + Game information Informations sur la partie - + Error Erreur - + Server error. Erreur serveur. @@ -807,96 +865,169 @@ Cette information sera consultable uniquement par les modérateurs. DlgCreateToken - + &Name: &Nom: - + Token Jeton - + C&olor: C&ouleur: - + white blanc - + blue bleu - + black noir - + red rouge - + green vert - + multicolor multicolore - + colorless incolore - + &P/T: &F/E: - + &Annotation: &Note: - + &Destroy token when it leaves the table &Detruire le jeton lorsqu'il quitte le champ de bataille - + Token data Données du jeton - + Show &all tokens Afficher &tous les tokens - + Show tokens from this &deck Afficher les tokens présents dans ce &deck - + Choose token from list Choisir un jeton dans la liste - + Create token Créer jeton + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + Erreur + + + + The new passwords don't match. + + + DlgEditTokens @@ -989,6 +1120,55 @@ Cette information sera consultable uniquement par les modérateurs. The chosen name conflicts with an existing card or token. Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. + Le nom choisi est en conflit avec une carte ou un jeton existant. +Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. + + + + DlgEditUser + + + Email: + + + + + Pronouns: + Pronoms: + + + + Neutral + + + + + Masculine + Masculin + + + + Feminine + Féminin + + + + Country: + Pays: + + + + Undefined + Indéfini + + + + Real name: + + + + + Edit user profile @@ -1002,7 +1182,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Show &password protected games - + Afficher les parties &protégées par un mot de passe @@ -1043,7 +1223,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Rafraîchir @@ -1053,12 +1233,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Charger deck depuis le presse-papier - + + Error Erreur - + + Invalid deck list. Liste de deck invalide. @@ -1071,22 +1253,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Charger deck + + DlgRegister + + + &Host: + Hôte + + + + &Port: + &Port: + + + + Player &name: + Joueur &nom: + + + + P&assword: + Mot de passe: + + + + Password (again): + + + + + Email: + Email + + + + Email (again): + + + + + Pronouns: + Pronoms: + + + + Neutral + + + + + Masculine + Masculin + + + + Feminine + Féminin + + + + Undefined + Indéfini + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + Pays: + + + + Real name: + Nom: + + + + Register to server + S'enregistrer au serveur + + DlgSettings - - - + + + Error Erreur - + Unknown Error loading card database Erreur de chargement de la base de données des cartes inconnue. - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1379,7 @@ Vous aurez peut-être à redémarrer oracle pour mettre à jour votre base de do Voulez vous changer les paramètres d'emplacement de base de données ? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1396,7 @@ Généralement il suffit de redémarrer oracle pour mettre à jour votre base de Voulez vous changer les paramètres d'emplacement de base de données ? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1405,7 @@ Would you like to change your database location setting? Voulez vous changer les paramètres d'emplacement de base de données ? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,81 +1414,80 @@ Would you like to change your database location setting? Voulez vous changer les paramètres d'emplacement de base de données ? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached Would you like to change your database location setting? - + Votre - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues Would you like to change your database location setting? - + Chargement d'une base de données de cartes inconnue + +Veuillez envoyer un ticket à http://github.com/Cockatrice/Cockatrice/issues s' + +Voulez-vous changer votre paramètre de localisation la base de données? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Le chemin d'accès pour le répertoire de votre deck est invalide. Souhaitez-vous redéfinir le chemin d'accès? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Le chemin d'accès pour le répertoire de vos images est invalide. Souhaitez-vous redéfinir le chemin d'accès? - + Settings Paramètres - + General Géneral - + Appearance Apparence - + User Interface Interface utilisateur - + Deck Editor Éditeur de Deck - + Chat Chat - + Sound + Son + + + + Shortcuts GameSelector - - - C&reate - C&réer - - - - &Join - Re&joindre - @@ -1222,7 +1497,7 @@ Would you like to change your database location setting? - + Error Erreur @@ -1267,37 +1542,47 @@ Would you like to change your database location setting? Vous avez été ignoré par le créateur de la partie. - + Join game Rejoindre partie - + Password: Mot de passe: - + Please join the respective room first. Veuillez d'abord rejoindre le bon salon. - + Games Parties - + &Filter games - Filtrer les parties + - + C&lear filter - Effacer le filtre + - + + C&reate + + + + + &Join + + + + J&oin as spectator Rej&oindre en tant que spectateur @@ -1414,97 +1699,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Effacer les images téléchargées - - - - - + + + + + Choose path Choisir chemin d'accès - + Success Réussite - + Downloaded card pictures have been reset. Les images téléchargés ont bien été effacées - + Error Erreur - + One or more downloaded card pictures could not be cleared. Une ou plusieurs images téléchargées ne peuvent pas être effacées. - + Personal settings Paramètres personnels - + Language: Langue: - + Download card pictures on the fly Charger les images de cartes à la volée - - Download high-quality card pictures - Télécharger des images en haute qualité + + Download card pictures from a custom URL + Télécharger les images depuis une URL - + + Custom Card Download URL: + URL des images à télécharger + + + + Linking FAQ + lié à la FAQ + + + Paths Chemins - + Decks directory: Répertoire des decks: - + Replays directory: Dossier de rediffusion - + Pictures directory: Répertoire des images: - + Card database: Base de données de cartes : - + Token database: Bases de données des jetons : - + Picture cache size: Taille du cache des images : - - + + English Français (French) @@ -1512,56 +1807,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Il y a trop de connections simultanées depuis votre ordinateur. - + Scheduled server shutdown. Fermeture prévue du serveur. - + Banned by moderator Banni par un modérateur - + Expected end time: %1 Fin théorique : %1 - + This ban lasts indefinitely. Banni définitivement. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Connection fermée - + The server has terminated your connection. Reason: %1 Le serveur a coupé votre connexion. Raison: %1 - + Scheduled server shutdown Fermeture prévue du serveur - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1572,304 +1860,485 @@ Aucune partie en cours ne sera sauvegardée. Raison de la fermeture : %1 - + Number of players Nombre de joueurs - + Please enter the number of players. Entrez s'il vous plait le nombre de joueurs. - - + + Player %1 Joueur %1 - + Load replay Charger replay - + About Cockatrice à propos de Cockatrice - + Version %1 Version %1 - + Translators: Traducteurs: - + Project Manager: + Chef de projet: + + + + The server has reached its maximum user capacity, please check back later. - + + + Invalid username. + Nom d'utilisateur non valide. + + + + You have been logged out due to logging in at another location. + + + + + + Success + Réussite + + + + Registration accepted. +Will now login. + Enregistrement accepté. +Connexion. + + + + Account activation accepted. +Will now login. + Activation du compte acceptée. +Connexion. + + + Past Project Managers: - + Anciens chefs de projet: - - Developers: - - - - - Our Developers - - - - - Help Develop! - - - - - Recognition Page - - - - - Help Translate! - - - - - Support: - - - - - Report an Issue - - - - - Suggest an Improvement - - - - - - - - - + Developers: + Développeurs: + + + + Our Developers + Nos développeurs + + + + Help Develop! + Aidez à développer! + + + + Recognition Page + Page des remerciements + + + + Help Translate! + Aidez à traduire! + + + + Support: + Assistance: + + - - - - + Report an Issue + Signaler un problème + + + + + + + + + + + + + + + + + + + + + Error Erreur - + Server timeout Délai de la demande dépassé - + Incorrect username or password. Please check your authentication information and try again. Nom d'utilisateur ou mot de passe incorrect. Veuillez vérifier vos identifiants et réessayez. - + There is already an active session using this user name. Please close that session first and re-login. Il y a déjà une session ouvert avec le même pseudo. Fermez cette session puis re-connectez-vous. - + + You are banned until %1. Vous êtes banni jusqu'au : %1 - + + You are banned indefinitely. Vous êtes banni indéfiniment - - This server requires user registration. - Vous devez être enregistré pour accéder à ce serveur. + + This server requires user registration. Do you want to register now? + Vous devez être enregistré pour accéder à ce serveur. Voulez-vous vous enregistrer? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + Activation du compte + + + Unknown login error: %1 Erreur de connexion inconnue : %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + Enregistrement refusé + + + + Registration is currently disabled on this server + L'enregistrement est désactivée sur ce serveur + + + + There is already an existing account with the same user name. + Il existe déjà un compte avec le même nom d'utilisateur. + + + + It's mandatory to specify a valid email address when registering. + Il est obligatoire de spécifier une adresse e-mail valide lors de l'enregistrement. + + + + Too many registration attempts from your IP address. + Excès de tentatives d'enregistrement depuis votre adresse IP. + + + + Password too short. + Mot de passe trop court. + + + + Registration failed for a technical problem on the server. + Enregistrement échoué lié à un problème technique du serveur. + + + + Unknown registration error: %1 + + + + + Account activation failed + Activation du compte échouée + + + Socket error: %1 Erreur de socket: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Vous tentez de vous connecter à un serveur obsolète. Chargez la nouvelle version de Cockatrice ou connectez-vous à un serveur approprié. La version la plus récente est %1, l'ancienne version est %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Votre client Cockatrice est obsolète. Veuillez charger la nouvelle version. La version la plus récente est %1, l'ancienne version est %2. - + Connecting to %1... Connexion à %1... - + + Registering to %1 as %2... + Enregistrement de %1 en tant que %2... + + + Disconnected Déconnecté - + Connected, logging in at %1 Connecté, connexion à %1 - - Logged in as %1 at %2 - Connecté en tant que %1 à %2 - - - + &Connect... &Connecter... - + &Disconnect &Déconnecter - + Start &local game... Démarrer une partie &locale... - + &Watch replay... - &Regarder un replay ... + &Regarder un replay... - + &Deck editor Éditeur de &deck - + &Full screen &Plein écran - + + &Register to server... + S'inscrire sur le serveur... + + + &Settings... &Paramètres... - - + + &Exit &Quitter - + A&ctions Actions - + &Cockatrice &Cockatrice - + &About Cockatrice À propos de Cock&atrice - + &Help A&ide - - Check card updates... - + + Check for card updates... + Recherche de mises à jour de la base de données... - - + + A card database update is already running. + Une mise à jour de la base de données est déjà en cours. + + + + Unable to run the card database updater: + Impossible de lancer la mise à jour de la base de données: + + + + The card database updater exited with an error: %1 + L'outil de mise à jour de la base de données s'est arrêté avec l'erreur: %1 + + + + Update completed successfully. Cockatrice will now reload the card database. + La mise à jour s'est effectuée avec succès. Cockatrice va maintenant charger de nouveau la base de données des cartes. + + + + Information + Information + + + + Troubleshooting - - A card update is already ongoing. + + F.A.Q. - - Unable to run the card updater: + + Your account has not been activated yet. +You need to provide the activation token received in the activation email - + failed to start. - + Échec du démarrage. - + crashed. - + planté. - + timed out. - + déconnecté. - + write error. - + Erreur d'écriture. - + read error. - + Erreur de lecture. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + Erreur inconnue. @@ -2109,69 +2578,69 @@ La version la plus récente est %1, l'ancienne version est %2. depuis la zone exil - + the bottom card of %1's library - + La carte du dessous de la bibliothèque de %1 - + the bottom card of his library la carte du dessous de sa bibliothèque - + the bottom card of her library la carte du dessous de sa bibliothèque - + from the bottom of %1's library - + À partir du dessous de la bibliothèque de %1 - + from the bottom of his library du dessous de sa bibliothèque - + from the bottom of her library du dessous de sa bibliothèque - + the top card of %1's library - + La carte du dessus de la bibliothèque de %1 - + the top card of his library le carte du dessus de sa bibliothèque - + the top card of her library le carte du dessus de sa bibliothèque - + from the top of %1's library - + À partir du dessus de la bibliothèque de %1 - + from the top of his library du dessus de sa bibliothèque - + from the top of her library du dessus de sa bibliothèque from %1's library - + de la bibliothèque de %1 @@ -2567,304 +3036,304 @@ La version la plus récente est %1, l'ancienne version est %2.%1 retire %n %2 marqueur de %3 (maintenant %4).%1 retire %n %2 marqueurs de %3 (maintenant %4). - + %1 taps her permanents. female %1 engage ses permanents. - + %1 untaps her permanents. female %1 dégage ses permanents. - + %1 taps his permanents. male %1 engage ses permanents. - + %1 untaps his permanents. male %1 dégage ses permanents. - + %1 taps %2. female %1 engage %2. - + %1 untaps %2. female %1 dégage %2. - + %1 taps %2. male %1 engage %2. - + %1 untaps %2. male %1 dégage %2. - + %1 sets counter %2 to %3 (%4%5). female %1 met les marqueurs %2 à %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 met les marqueurs %2 à %3 (%4%5). - + %1 sets %2 to not untap normally. female %2 de %1 ne se dégagera pas lors de l'étape de dégagement. - + %1 sets %2 to not untap normally. male %2 de %1 ne se dégagera pas lors de l'étape de dégagement. - + %1 sets %2 to untap normally. female %2 de %1 se dégagera lors de l'étape de dégagement. - + %1 sets %2 to untap normally. male %2 de %1 se dégagera lors de l'étape de dégagement. - + %1 sets PT of %2 to %3. female %1 change la F/E de %2 à %3. - + %1 sets PT of %2 to %3. male %1 change la F/E de %2 à %3. - + %1 sets annotation of %2 to %3. female %1 met l'annotation %3 à %2. - + %1 sets annotation of %2 to %3. male %1 met l'annotation %3 à %2. + + + %1 is looking at %2. + female + %1 regarde %2. + %1 is looking at %2. - female + male %1 regarde %2. + + %1 is looking at the top %n card(s) %2. + female + %1 regarde les %n cartes du dessus %2.%1 regarde les %n cartes du dessus %2. + + + %1 is looking at the top %n card(s) %2. + male + %1 regarde la carte du dessus %2.%1 regarde les %n cartes du dessus %2. + - - %1 is looking at %2. - male - %1 regarde %2. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - - - %1 is looking at the top %n card(s) %2. - male - + %1 arrête de regarder %2. %1 stops looking at %2. - female - %1 arrête de regarder %2. - - - - %1 stops looking at %2. male %1 arrête de regarder %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 révèle %2 à %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 révèle %2 à %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 révèle %2 à %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 révèle %2 à %3. + + + %1 reveals %2. + female + %1 révèle %2. + %1 reveals %2. - female - %1 révèle %2. - - - - %1 reveals %2. male %1 révèle %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 révèle au hasard %2%3 à %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 révèle au hasard %2%3 à %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 révèle au hasard %2%3 à %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 révèle au hasard %2%3 à %4. - + %1 randomly reveals %2%3. female %1 révèle au hasard %2%3. - + %1 randomly reveals %2%3. male %1 révèle au hasard %2%3. - + %1 peeks at face down card #%2. female - + %1 regarde furtivement la carte face cachée %2. - + %1 peeks at face down card #%2. male - + %1 regarde furtivement la carte face cachée %2. - + %1 peeks at face down card #%2: %3. female - + %1 regarde furtivement la carte face cachée %2: %3. - + %1 peeks at face down card #%2: %3. male - + %1 regarde furtivement la carte face cachée %2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 révèle %2%3 à %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 révèle %2%3 à %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 révèle %2%3 à %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 révèle %2%3 à %4. - + %1 reveals %2%3. female %1 révèle %2%3. - + %1 reveals %2%3. male %1 révèle %2%3. + + + %1 is now keeping the top card %2 revealed. + %1 garde maintenant sa carte du dessus %2 révélée. + - %1 is now keeping the top card %2 revealed. - - - - %1 is not revealing the top card %2 any longer. - + %1 ne révèle plus la carte du dessus %2. - + It is now %1's turn. female C'est maintenant le tour de %1. - + It is now %1's turn. male C'est maintenant le tour de %1. - + a card une carte @@ -2927,12 +3396,12 @@ La version la plus récente est %1, l'ancienne version est %2. - + ending phase phase de fin de tour - + untap step étape de dégagement @@ -2940,13 +3409,13 @@ La version la plus récente est %1, l'ancienne version est %2. %1 draws %2 card(s). female - + %1 pioche %2 carte(s). %1 draws %2 card(s). male - + %1 pioche %2 carte(s). @@ -2976,22 +3445,22 @@ La version la plus récente est %1, l'ancienne version est %2. %1 puts %2%3 into her graveyard. - + %1 met %2%3 dans son cimetière. %1 puts %2%3 into his graveyard. - + %1 met %2%3 dans son cimetière. %1 moves %2%3 to her hand. - + %1 met %2%3 dans sa main. %1 moves %2%3 to his hand. - + %1 met %2%3 dans sa main. @@ -3021,85 +3490,85 @@ La version la plus récente est %1, l'ancienne version est %2. %1 places %2 %3 counter(s) on %4 (now %5). female - + %1 met %2 %3 marqueur(s) sur %4 (désormais %5). %1 places %2 %3 counter(s) on %4 (now %5). male - + %1 met %2 %3 marqueur(s) sur %4 (désormais %5). %1 removes %2 %3 counter(s) from %4 (now %5). female - + %1 retire %2 %3 marqueur(s) sur %4 (désormais %5). %1 removes %2 %3 counter(s) from %4 (now %5). male - + %1 retire %2 %3 marqueur(s) sur %4 (désormais %5). + + + + %1 is looking at the top %2 card(s) %3. + female + %1 regarde le(s) %2 carte(s) %3. %1 is looking at the top %2 card(s) %3. - female - - - - - %1 is looking at the top %2 card(s) %3. male - + %1 regarde le(s) %2 carte(s) %3. - + upkeep step étape d'entretien - + draw step étape de pioche - + first main phase première phase principale - + beginning of combat step étape de début du combat - + declare attackers step étape de déclaration des attaquants - + declare blockers step étape de déclaration des bloqueurs - + combat damage step étape de répartition et de résolution des blessures - + end of combat step étape de fin de combat - + second main phase seconde phase principale - + It is now the %1. C'est maintenant %1. @@ -3107,62 +3576,74 @@ La version la plus récente est %1, l'ancienne version est %2. MessagesSettingsPage - + Chat settings Paramètres du chat - - Enable chat mentions + + Custom alert words - + + Enable chat mentions + Activer les mentions dans le chat + + + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - + Inverser la couleur du texte - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + Activer les notifications de bureau pour les mentions. - + + (Color is hexadecimal) - + (La couleur est hexadécimale) - + Add message Ajouter message - + Message: Message: @@ -3228,338 +3709,339 @@ La version la plus récente est %1, l'ancienne version est %2. Player - + &View library &Voir la bibliothèque - + Move top cards to &graveyard... Déplacer les cartes du dessus vers le &cimetière... - + View &top cards of library... Voir les cartes du &dessus de la bibliothèque... - + &View graveyard &Voir le cimetière - + &View sideboard &Voir la réserve - + Player "%1" Joueur "%1" - - - + + + + &Hand &Main - + &Reveal hand to... &Révéler la main à ... - + Reveal r&andom card to... Révéler une c&arte au hasard à ... - + &Library &Bibliothèque - - - + + + &Graveyard &Cimetière - + &Sideboard Ré&serve - + Red Rouge - + Yellow Jaune - + Green Vert - + View top cards of library Voir les cartes du dessus de la bibliothèque - + Number of cards: Nombre de cartes: - + &Draw card &Piocher une carte - + Reveal top cards of library - + Révéler les cartes du dessus de la bibliothèque - + Number of cards: (max. %1) - + Nombre de cartes: (max. %1) - + &View exile &Voir la zone exil - - - + + + &Exile &Exil - + Reveal t&op cards to... - + Révéler la carte de dessus à... - + D&raw cards... P&iocher plusieurs cartes... - + Take &mulligan &Mulliganer - + &Shuffle Mél&anger - + &Counters Mar&queurs - + &Untap all permanents Dé&gager tous les permanents - + R&oll die... Lancer un &dé... - + &Create token... &Créer un jeton... - + C&reate another token C&réer un autre jeton - + S&ay D&ire - + &Move hand to... Déplacer la &main dans ... - - - - + + + + &Top of library &Dessus de la bibliothèque - - - - + + + + &Bottom of library Dessous de la &bibliothèque - + &Move graveyard to... Déplacer le cimetière vers... - + &Move exile to... Déplacer l'exil vers... - + Reveal &library to... Révéler &la bibliothèque à... - + &Always reveal top card Toujours révéler l&a carte du dessus - + O&pen deck in deck editor - + Ouvrir le deck dans l'éditeur de deck - + &Undo last draw Annu&ler la dernière pioche - + Play top card &face down - + Jouer la carte du dessus face cachée - + Move top cards to &exile... Déplacer les cartes du dessus vers la zone &exil... - + Put top card on &bottom Mettre la carte du dessus en &dessous - + Put bottom card &in graveyard - - - - - Cr&eate predefined token - + Mettre la carte du dessous &dans le cimetière + Cr&eate predefined token + C&réer un jeton prédéfini + + + C&ard C&arte - + &All players &Tous les joueurs - - - &Play - - - - - &Hide - - - - - Play &Face Down - - - - - &Tap - - - - - &Untap - - - - - Toggle &normal untapping - - - &Flip - + &Play + &Jouer - &Peek at card face - + &Hide + &Cacher + Play &Face Down + Jouer &Face Cachée + + + + &Tap + &Engager + + + + &Untap + &Dégager + + + + Toggle &normal untapping + Basculer en Dégagement &normal + + + + &Flip + &Retourner la carte + + + + &Peek at card face + &Regarder furtivement la carte face cachée + + + &Clone &Copier la carte - + Attac&h to card... Attac&her à une autre carte ... - + Unattac&h Détac&her - + &Draw arrow... - + &Tracer une flèche... - + &Increase power &Augmenter la force - + &Decrease power &Diminuer la force - + I&ncrease toughness - + A&ugmenter l'endurance - + D&ecrease toughness - + D&iminuer l'endurance @@ -3567,148 +4049,153 @@ La version la plus récente est %1, l'ancienne version est %2.Au&gmenter la force et l'endurance - + Dec&rease power and toughness Di&minuer la force et l'endurance - + Set &power and toughness... Fi&xer la force et l'endurance... - + &Set annotation... - + &Ajouter une annotation... - + &Add counter (%1) - + &Ajouter un marqueur (%1) &Remove counter (%1) - + &Retirer un marquer (%1) - + &Set counters (%1)... - + $Nombre de marqueurs (%1)... - + Draw cards Piocher plusieurs cartes - - - - + + + + Number: Nombre: - + Move top cards to grave Mettre les cartes du dessus dans le cimetière - + Move top cards to exile Mettre les cartes du dessus dans la zone exil - + Roll die Lancer un dé - + Number of sides: Nombre de faces: - + Set power/toughness Fixer force/endurance - + Please enter the new PT: Entrer la nouvelle F/E: - + Set annotation Mettre une note - + Please enter the new annotation: Entrez la nouvelle note: - + Set counters Mettre des marqueurs + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Cacher %1 - + Hide Others - + Cacher les autres - + Show All - + Montrer tout - + Preferences... Préférences... - + Quit %1 - + Quitter %1 - + About %1 - + À Propos %1 QObject - + Cockatrice replays (*.cor) - + Replays Cockatrice (*.cor) Common deck formats (*.cod *.dec *.mwDeck) - + Formats de decks courants (*.cod *.dec *.mwDeck) All files (*.*) - + Afficher tous les fichiers (*.*) @@ -3734,12 +4221,12 @@ La version la plus récente est %1, l'ancienne version est %2. ID - + ID Name - + Nom @@ -3749,17 +4236,17 @@ La version la plus récente est %1, l'ancienne version est %2. Keep - + Garder Time started - + Le temps a commencé Duration (sec) - + Durée (sec) @@ -3786,31 +4273,60 @@ La version la plus récente est %1, l'ancienne version est %2. + Permissions + + + + Players Joueurs - + Games Parties + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel Enabled - + Activé Set type - + Définir le type Set code - + Définir le code @@ -3820,7 +4336,7 @@ La version la plus récente est %1, l'ancienne version est %2. Release date - + Date de sortie @@ -3836,7 +4352,7 @@ La version la plus récente est %1, l'ancienne version est %2.&Temps avant la fermeture (minutes): - + Shut down server Fermer le serveur @@ -3844,262 +4360,309 @@ La version la plus récente est %1, l'ancienne version est %2. SoundSettingsPage - + Choose path - + Choisir le chemin d'accès - + Enable &sounds - + Activer le &son - + Path to sounds directory: - + Chemin d'accès au répertoire sons - + Test system sound engine - + Tester les effets sonores - + Sound settings - + Réglages du son - + Master volume requires QT5 - + Master volume - + Volume principal TabAdmin - + Update server &message Mettre à jour le &message du serveur - + &Shut down server &Fermer le serveur - + + &Reload configuration + + + + Server administration functions Fonctions d'administration du serveur - + &Unlock functions &Débloquer fonctions - + &Lock functions &Bloquer fonctions - + Unlock administration functions Débloquer fonctions d'administration - + Do you really want to unlock the administration functions? Êtes-vous sûr de vouloir débloquer les fonctions d'administration? - + Administration - + Administration TabDeckEditor - + &Print deck... Im&primer le deck... - + &Close - + &Fermer - + &Edit sets... &Editer les éditions... - - &Clear search - Effa&cer la recherche + + Filters + Filtres - + + &Clear all filters + &Enlever tous les filtres + + + + Delete selected + Enlever la sélection + + + Deck &name: &Nom du deck: - + &Comments: &Commentaires: - + Hash: - + &New deck &Nouveau deck - + &Load deck... Charger un deck ... - + &Save deck &Sauvegarder le deck - + Save deck &as... S&auvegarder le deck sous... - + Load deck from cl&ipboard... Charger un deck depuis le presse-pap&ier ... - + Save deck to clip&board - + Sauvegarder le deck dans le &presse-papier - + &Analyze deck on deckstats.net &Analyser le deck sur deckstats.net - + Open custom image folder Ouvrir le dossier image Custom - + + Open custom sets folder + + + + Add card to &maindeck Ajouter carte au &deck - + Add card to &sideboard Ajouter carte à la ré&serve - + &Deck Editor &Éditeur de Deck - + C&ard Database B&ase de données de cartes : - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - + Bienvenue - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. - +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + Salut! Il semble que vous lanciez cette version de Cockatrice pour la première fois. +Tous les sets de cartes dans la base de données ont été activés. +Lire plus à propos du changement d'ordre des sets ou de la désactivation des sets et de ce que cela implique dans la fenêtre "Editer les sets". - - Show card text only - Montrer le texte uniquement - - - + &Remove row &Retirer la flèche - + &Increment number Augmenter quant&ité - + &Decrement number &Diminuer quantité - + Edit &tokens... Modifier les je&tons... - + Deck: %1 - + Deck: %1 - + Are you sure? Êtes-vous sûr? - + The decklist has been modified. Do you want to save the changes? Le deck a été modifié. Voulez vous enregistrer les modifications? - + Load deck Charger deck - - - + + + Error Erreur - + The deck could not be saved. Le deck n'a pas pu être enregistré. - - + + The deck could not be saved. Please check that the directory is writable and try again. Le deck n'a pas pu être enregistré. Vérifiez que le répertoire ne soit pas en lecture seule et réessayez. - + Save deck Sauvegarder le deck @@ -4171,7 +4734,7 @@ Entrez un nom s'il vous plaît: Are you sure you want to delete "%1"? - + Êtes-vous sûr de vouloir effacer "%1"? @@ -4181,12 +4744,12 @@ Entrez un nom s'il vous plaît: Delete remote folder - + Effacer le dossier local Delete remote deck - + Effacer le deck local @@ -4197,94 +4760,99 @@ Entrez un nom s'il vous plaît: TabGame - + &Phases &Phases - + &Game &Partie - + Next &phase &Prochaine phase - + Next &turn Prochain &Tour - + &Remove all local arrows &Retirer toutes les flèches locales - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information Information sur la partie - + &Concede &Concéder - + &Leave game &Quitter la partie - + C&lose replay Fermer &le replay - + &Say: &Dire: - + Concede Concéder - + Are you sure you want to concede this game? Êtes-vous sûr de vouloir concéder la partie? - + Leave game Quitter la partie - + Are you sure you want to leave this game? Êtes-vous sûr de vouloir quitter la partie? - + You are flooding the game. Please wait a couple of seconds. - + Vous floodez la partie. Veuillez patienter quelques secondes. - + You have been kicked out of the game. Vous avez été expulsé de la partie. - - Replay %1: %2 - - - - - Game %1: %2 - Partie %1:%2 + + REPLAY + REPLAY @@ -4292,7 +4860,7 @@ Entrez un nom s'il vous plaît: Private &chat - + &chat privé @@ -4302,7 +4870,7 @@ Entrez un nom s'il vous plaît: %1 - Private chat - + %1-Chat privé @@ -4312,7 +4880,7 @@ Entrez un nom s'il vous plaît: Private message from - + Message privé de @@ -4328,56 +4896,56 @@ Entrez un nom s'il vous plaît: TabReplays - + Local file system - + Server replay storage - + Watch replay Voir replay - - + + Delete Supprimer - + Download replay Charger replay - + Toggle expiration lock - + Delete local file Effacer fichier local - + Are you sure you want to delete "%1"? - + Êtes-vous sûr de vouloir supprimer "%1"? - + Delete remote replay - + Are you sure you want to delete the replay of game %1? - + Êtes-vous sûr de vouloir supprimer le replay de la partie %1? @@ -4388,47 +4956,47 @@ Entrez un nom s'il vous plaît: TabRoom - + &Say: &Dire: - + Chat Chat - + &Room &Salon - + &Leave room &Quitter le salon - + &Clear chat Effacer le &Chat - + Chat Settings... Paramètres du Chat... - + mentioned you. - + vous a mentionné. - + Click to view Cliquer pour voir - + You are flooding the chat. Please wait a couple of seconds. Vous floodez le chat. Veuillez patienter quelques secondes. @@ -4444,27 +5012,37 @@ Entrez un nom s'il vous plaît: TabSupervisor - + Are you sure? Êtes vous sur? - + There are still open games. Are you sure you want to quit? Il y a encore des parties en cours. Êtes-vous sûr de vouloir quitter? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists Add to Buddy List - + Ajouter à la liste d'amis Add to Ignore List - + Ajouter à la liste noire @@ -4475,169 +5053,301 @@ Entrez un nom s'il vous plaît: UserContextMenu - + User &details &Détails utilisateur - + Private &chat &Chat privé - + Show this user's &games Voir les parties du joueur - - - Add to &buddy list - - - - - Remove from &buddy list - - - - - Add to &ignore list - - - Remove from &ignore list - + Add to &buddy list + Ajouter à la liste d'&amis - Kick from &game - + Remove from &buddy list + Retirer de la liste d'&amis + Add to &ignore list + Ajouter à la &liste noire + + + + Remove from &ignore list + Retirer de la &liste noire + + + + Kick from &game + Exclure de la &partie + + + Ban from &server + Bannir du &serveur + + + + &Promote user to moderator - + + Dem&ote user from moderator + + + + %1's games + Parties de %1 + + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. UserInfoBox - + User information Informations utilisateur - + Real name: Vrai nom: - - Gender: - Sexe: + + Pronouns: + Pronoms: - + Location: Localisation: - + User level: Rang utilisateur: - + Account Age: + Ancienneté du compte: + + + + Edit + Editer + + + + Change password - + + Change avatar + + + + Administrator Administrateur - + Moderator Modérateur - + Registered user Utilisateur enregistré - - + + Unregistered user Utilisateur non enregistré - + Unknown Inconnu - + Year Année - + Years Années - + Day Jour - + Days Jours + + + + + Information + Information + + + + User information updated. + + + + + + + + + + + + Error + Érreur + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Réglages généraux de l'interface - + Enable notifications in taskbar Activer les notifications dans la barre de tâches - + Notify in the taskbar for game events while you are spectating - + Notifier dans la barre des tâches pour les évènements des parties quand vous y êtes spectateur - + &Double-click cards to play them (instead of single-click) &Double cliquer sur la carte pour la jouer (au lieu d'un simple clic) - + &Play all nonlands onto the stack (not the battlefield) by default &Jouer toutes les cartes non terrain dans la pile (pas sur le champ de bataille) par defaut - + + Annotate card text on tokens + + + + Animation settings Réglage des animations - + &Tap/untap animation &Animation d'engagement et de dégagement @@ -4645,22 +5355,22 @@ Entrez un nom s'il vous plaît: UserList - + Users connected to server: %1 - + Utilisateurs connectés au serveur: %1 - + Users in this room: %1 Utilisateurs dans ce salon: %1 - + Buddies online: %1 / %2 Amis connectés: %1 / %2 - + Ignored users online: %1 / %2 Personnes sur liste noire connectés: %1 / %2 @@ -4680,29 +5390,46 @@ Entrez un nom s'il vous plaît: Move selected set up - + Déplacer l'édition sélectionnée vers le haut + + + + Move selected set to the top + Déplacer l'édition sélectionnée tout en haut Move selected set down - - - - - Move selected set to top - + Déplacer l'édition sélectionnée vers le bas - Move selected set to bottom - + Move selected set to the bottom + Déplacer l'édition sélectionnée tout en bas - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + astuces: + + + + Enable the sets that you want to have available in the deck editor + Activer les sets que vous souhaitez disponibles dans l'éditeur de deck + + + + Move sets around to change their order, or click on a column header to sort sets on that field + Déplacez les éditions pour changer leur ordre, ou cliquez sur le titre d'une colonne pour trier les éditions selon ce champ + + + + Sets order decides the source that will be used when loading images for a specific card + L'ordre des éditions détermine la source qui sera utilisée au chargement des images pour une carte spécifique + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4711,14 +5438,14 @@ Disabled sets will still be used for loading images. Editer les sets - + Success - + Réussite - + The sets database has been saved successfully. - + La base de données des éditions a été sauvergardée avec succès. @@ -4741,6 +5468,563 @@ Disabled sets will still be used for loading images. pile view + vue de la pile + + + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play diff --git a/cockatrice/translations/cockatrice_it.ts b/cockatrice/translations/cockatrice_it.ts index d767d123..fc8b0067 100644 --- a/cockatrice/translations/cockatrice_it.ts +++ b/cockatrice/translations/cockatrice_it.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... Imposta &segnalini... - + Set counter Imposta segnalini - + New value for counter '%1': Nuovo valore per il segnalino '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Immagini di sfondo delle zone - + Hand background: Sfondo mano: - + Stack background: Sfondo pila: - + Table background: Sfondo tavolo: - + Player info background: Sfondo info giocatore: - + Card back: Sfondo carta: - + Card rendering Visualizzazione delle carte - + Display card names on cards having a picture Visualizza nome delle carte sopra le immagini - + Scale cards on mouse over Ingrandisci la carta sotto il mouse - + Hand layout Layout della mano - + Display hand horizontally (wastes space) Disponi la mano orizzontalmente (spreca spazio) - + Enable left justification Allinea a sinistra - + Table grid layout Layout della griglia tabellare - + Invert vertical coordinate Inverti le coordinate verticali - + Minimum player count for multi-column layout: Numero di giocatori minimo per layout multicolonna: - - - - - + + + + + Choose path Seleziona percorso @@ -107,97 +107,102 @@ BanDialog - + ban &user name Banna &user name - + ban &IP address Banna indirizzo &IP - + + ban client I&D + banna client I&D + + + Ban type Tipo di ban - + &permanent ban Ban &permanente - + &temporary ban Ban &temporaneo - + &Days: &Giorni: - + &Hours: &Ore: - + &Minutes: &Minuti: - + Duration of the ban Durata del ban - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Per favore inserisci la ragione del ban. Questo è solo visibile ai moderatori e non alla persona bannata. - + Please enter the reason for the ban that will be visible to the banned person. Per favore inserisci la ragione del ban che sarà visibile alla persona bannata. - + &OK &OK - + &Cancel &Annulla - + Ban user from server Banna utente dal server - + Error Errore - - You have to select a name-based or IP-based ban, or both. - Seleziona un ban su nome, IP, o entrambi. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + Seleziona un nome, un IP, un client ID o una combinazione di questi per impostare un ban. CardDatabase - + New sets found Nuovi set trovati - + %1 new set(s) have been found in the card database. Do you want to enable them? %1 nuovi set sono stati trovati nel database delle carte. Vuoi abilitare i nuovi set? @@ -230,30 +235,53 @@ Questo è solo visibile ai moderatori e non alla persona bannata. F/C + + CardFrame + + + Image + Immagine + + + + Description + Testo + + + + Both + Entrambi + + CardInfoText - + Name: Nome: - + Mana cost: Costo mana: - + + Color(s): + Colori: + + + Card type: Tipo carta: - + P / T: F / C: - + Loyalty: Lealtà: @@ -276,27 +304,32 @@ Questo è solo visibile ai moderatori e non alla persona bannata. Visualizza tutte le info - + Name: Nome: - + Mana cost: Costo: - + + Color(s): + Colori: + + + Card type: Tipo: - + P / T: F / C: - + Loyalty: Fedeltà: @@ -560,12 +593,12 @@ Questo è solo visibile ai moderatori e non alla persona bannata. DeckEditorSettingsPage - + Nothing is here... yet Nulla da vedere qui... per ora - + General Generale @@ -573,17 +606,17 @@ Questo è solo visibile ai moderatori e non alla persona bannata. DeckListModel - + Number Numero - + Card Carta - + Price Prezzo @@ -605,42 +638,42 @@ Questo è solo visibile ai moderatori e non alla persona bannata. DeckViewContainer - - Load &local deck - Carica &mazzo dal pc + + Load local deck + Carica mazzo dal pc - - Load d&eck from server - Carica &mazzo dal server + + Load deck from server + Carica mazzo dal server - + Ready to s&tart Pronto a i&niziare - + S&ideboard unlocked S&ideboard sbloccato - + S&ideboard locked S&ideboard bloccato - + Load deck Carica mazzo - + Error Errore - + The selected file could not be loaded. I file selezionati non posso essere caricati. @@ -686,37 +719,52 @@ Questo è solo visibile ai moderatori e non alla persona bannata. DlgConnect - + + Previous Host + Ultimo indirizzo + + + + New Host + Nuovo indirizzo + + + &Host: &Host: - + + Enter host name + Inserisci indirizzo + + + &Port: &Porta: - + Player &name: &Nome giocatore: - + P&assword: P&assword: - + &Save password &Salva password - + A&uto connect at start Connetti a&utomaticamente all'avvio - + Connect to server Connetti al server @@ -724,82 +772,92 @@ Questo è solo visibile ai moderatori e non alla persona bannata. DlgCreateGame - + &Description: &Descrizione: - + &Password: &Password: - + P&layers: G&iocatori: - + + Re&member settings + Ricorda impostazioni + + + Game type Formato di gioco - + Only &buddies can join Solo gli &amici possono entrare - + Only &registered users can join Solo &utenti registrati possono entrare - + Joining restrictions Restrizioni d'ingresso - + &Spectators can watch &Gli spettatori possono vedere - + Spectators &need a password to watch Gli spettatori &necessitano di password - + Spectators can see &hands Gli spettatori vedono le mani - + Spectators can &chat &Spettatori possono &chattare - + Spectators Spettatori - + + &Clear + Pulisci + + + Create game Crea partita - + Game information Informazioni partita - + Error Errore - + Server error. Errore del server. @@ -807,96 +865,170 @@ Questo è solo visibile ai moderatori e non alla persona bannata. DlgCreateToken - + &Name: &Nome: - + Token Pedina - + C&olor: C&olore: - + white bianco - + blue blu - + black nero - + red rosso - + green verde - + multicolor multicolore - + colorless incolore - + &P/T: &F/C: - + &Annotation: &Appunti: - + &Destroy token when it leaves the table &Distruggi la pedina quando lascia il tavolo - + Token data Dati pedina - + Show &all tokens Mostra &tutte le pedine - + Show tokens from this &deck Mostra pedine di questo &mazzo - + Choose token from list Scegli pedina dalla lista - + Create token Crea pedina + + DlgEditAvatar + + + + No image chosen. + Nessuna immagine selezionata. + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + Per cambiare il tuo avatar, scegli una nuova immagine. +Per rimuovere il tuo avatar attuale, conferma senza scegliere una nuova immagine. + + + + Browse... + Cerca... + + + + Change avatar + Cambia avatar + + + + Open Image + Apri immagine + + + + Image Files (*.png *.jpg *.bmp) + File immagine (*.png *.jpg *.bmp) + + + + Invalid image chosen. + immagine selezionata non valida. + + + + DlgEditPassword + + + Old password: + Vecchia password: + + + + New password: + Nuova password: + + + + Confirm new password: + Conferma nuova password: + + + + Change password + Cambia password + + + + Error + Errore + + + + The new passwords don't match. + Le password non coincidono. + + DlgEditTokens @@ -993,6 +1125,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Assicurati di aver abilitato il set "Tokens" nella finestra "Modifica set" per visualizzarli correttamente. + + DlgEditUser + + + Email: + Email: + + + + Pronouns: + Pronomi: + + + + Neutral + Neutrali + + + + Masculine + Maschili + + + + Feminine + Femminili + + + + Country: + Stato: + + + + Undefined + Non definito + + + + Real name: + Nome reale: + + + + Edit user profile + Modifica profilo utente + + DlgFilterGames @@ -1044,7 +1224,7 @@ Assicurati di aver abilitato il set "Tokens" nella finestra "Modi DlgLoadDeckFromClipboard - + &Refresh &Aggiorna @@ -1054,12 +1234,14 @@ Assicurati di aver abilitato il set "Tokens" nella finestra "Modi Carica mazzo dagli appunti - + + Error Errore - + + Invalid deck list. Lista del mazzo non valida. @@ -1072,22 +1254,116 @@ Assicurati di aver abilitato il set "Tokens" nella finestra "Modi Carica mazzo + + DlgRegister + + + &Host: + &Host: + + + + &Port: + &Porta: + + + + Player &name: + &Nome giocatore: + + + + P&assword: + P&assword: + + + + Password (again): + Password (conferma): + + + + Email: + Email: + + + + Email (again): + Email (conferma): + + + + Pronouns: + Pronomi: + + + + Neutral + Neutrali + + + + Masculine + Maschili + + + + Feminine + Femminili + + + + Undefined + Non definito + + + + + Registration Warning + Avviso Registrazione + + + + Your passwords do not match, please try again. + Le password non combaciano, riprova. + + + + Your email addresses do not match, please try again. + Gli indirizzi email non combaciano, riprova. + + + + Country: + Stato: + + + + Real name: + Nome reale: + + + + Register to server + Registrati sul server + + DlgSettings - - - + + + Error Errore - + Unknown Error loading card database Errore sconosciuto durante il caricamento del database delle carte - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1104,7 +1380,7 @@ Ti consigliamo di avviare oracle per aggiornare il tuo database delle carte. Vuoi modificare le impostazioni della posizione del database della carte? - + Your card database version is too old. This can cause problems loading card information or images @@ -1121,7 +1397,7 @@ Ti consigliamo di avviare oracle per aggiornare il tuo database delle carte. Vuoi modificare le impostazioni della posizione del database della carte? - + File Error loading your card database. Would you like to change your database location setting? @@ -1130,7 +1406,7 @@ Would you like to change your database location setting? Vuoi modificare le impostazioni della posizione del database della carte? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1139,7 +1415,7 @@ Would you like to change your database location setting? Vuoi modificare le impostazioni della posizione del database della carte? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1152,7 +1428,7 @@ Ti preghiamo di aprire un ticket su http://github.com/Cockatrice/Cockatrice/issu Vuoi modificare le impostazioni della posizione del database della carte? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1165,63 +1441,58 @@ Ti preghiamo di aprire un ticket su http://github.com/Cockatrice/Cockatrice/issu Vuoi modificare le impostazioni della posizione del database della carte? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Il percorso della cartella del mazzo non è valido. Vuoi tornare in dietro e impostare il percorso corretto? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Il percorso della cartella delle immagini delle carte è invilido. Vuoi tornare indietro e impostare il percorso corretto? - + Settings Impostazioni - + General Generale - + Appearance Aspetto - + User Interface Interfaccia - + Deck Editor Editor di mazzi - + Chat Chat - + Sound Suoni + + + Shortcuts + Combinazioni + GameSelector - - - C&reate - Cr&ea - - - - &Join - &Entra - @@ -1231,7 +1502,7 @@ Vuoi modificare le impostazioni della posizione del database della carte? - + Error Errore @@ -1276,37 +1547,47 @@ Vuoi modificare le impostazioni della posizione del database della carte?Sei stato ingnorato dal creatore di questa partita. - + Join game Entra nella partita - + Password: Password: - + Please join the respective room first. Si prega di entrare nella rispettiva stanza prima. - + Games Partite - + &Filter games &Filtri partite - + C&lear filter E&limina filtri - + + C&reate + Cr&ea + + + + &Join + &Entra + + + J&oin as spectator Entra c&ome spettatore @@ -1423,97 +1704,107 @@ Vuoi modificare le impostazioni della posizione del database della carte? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Resetta/Pulisci le Immagini Scaricate - - - - - + + + + + Choose path Seleziona il percorso - + Success Successo - + Downloaded card pictures have been reset. Le immagini scaricate delle carte sono state resettate. - + Error Errore - + One or more downloaded card pictures could not be cleared. Una o più immagini scaricate delle carte non possono essere pulite. - + Personal settings Impostazioni personali - + Language: Lingua: - + Download card pictures on the fly Download immagini delle carte - - Download high-quality card pictures - Scarica immagini delle carte ad alta qualità + + Download card pictures from a custom URL + Download immagini delle carte da un indirizzo personalizzato - + + Custom Card Download URL: + Indirizzo immagini personalizzato: + + + + Linking FAQ + Info su indirizzo immagini personalizzato + + + Paths Percorso - + Decks directory: Cartella mazzi: - + Replays directory: Cartella replay: - + Pictures directory: Cartella immagini: - + Card database: Database carte: - + Token database: Database pedine: - + Picture cache size: Dimensione cache immagini: - - + + English Italiano (Italian) @@ -1521,57 +1812,49 @@ Vuoi modificare le impostazioni della posizione del database della carte? MainWindow - + There are too many concurrent connections from your address. Ci sono troppe connessioni contemporanee dal tuo indirizzo. - + Scheduled server shutdown. Spegnimento del server in programma. - + Banned by moderator Bannato dal moderatore - + Expected end time: %1 Fine prevista: %1 - + This ban lasts indefinitely. Questo ban dura a tempo indeterminato. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - Nome utente non valido. -Puoi usare solo i caratteri A-Z, a-z, 0-9, _, ., e - nel tuo nome utente. - - - + Connection closed Connessione chiusa - + The server has terminated your connection. Reason: %1 Hai perso la connessione con il server. Ragione: %1 - + Scheduled server shutdown Spegnimento del server in programma - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1582,305 +1865,489 @@ Tutti le partite saranno perse. Ragione dello spegnimento: %1 - + Number of players Numero di giocatori - + Please enter the number of players. Inserisci il numero di giocatori. - - + + Player %1 Giocatore %1 - + Load replay Carica replay - + About Cockatrice Info su Cockatrice - + Version %1 Versione %1 - + Translators: Traduttori: - + Project Manager: Manager del progetto: - + + The server has reached its maximum user capacity, please check back later. + Il server ha raggiunto la sua massima capacità di utenti, riprova più tardi. + + + + + Invalid username. + Nome utente non valido. + + + + You have been logged out due to logging in at another location. + Sei stato disconnesso: ti sei connesso da un'altra postazione. + + + + + Success + Successo + + + + Registration accepted. +Will now login. + Registrazione accettata. +Login in corso. + + + + Account activation accepted. +Will now login. + Attivazione account accettata. +Login in corso. + + + Past Project Managers: Manager precedenti: - + Developers: Sviluppatori: - + Our Developers I nostri sviluppatori - + Help Develop! Aiutaci nello sviluppo! - + Recognition Page Informazioni generali - + Help Translate! Aiutaci nella traduzione! - + Support: Supporto: - + Report an Issue Segnala un problema - - Suggest an Improvement - Suggerisci una miglioria - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error Errore - + Server timeout Timeout del server - + Incorrect username or password. Please check your authentication information and try again. Nome utente o password non validi. Ricontrolla i tuoi dati di accesso e riprova. - + There is already an active session using this user name. Please close that session first and re-login. Sei già loggato in un'altra sessione con questo username. Chiudi prima quella sessione e riprova a loggare. - + + You are banned until %1. Sei bannato fino a %1. - + + You are banned indefinitely. Sei stato bannato per tempo indeterminato. - - This server requires user registration. - Questo server richiede la registrazione degli utenti. + + This server requires user registration. Do you want to register now? + Questo server richiede la registrazione degli utenti. Vuoi registrarti adesso? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + Il server richiede un client ID. Il tuo programma non ha generato un client ID. +Riprova dopo ver chiuso e riaperto il tuo programma. + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + Si è verificato un errore, riprova chiudendo e riaprendo il programma. Se l'errore persiste, aggiorna il programma all'ultima versione o chiedi assistenza. + + + + Account activation + Attivazione account + + + Unknown login error: %1 Errore di login sconosciuto: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + +Solitamente questo significa che stai usando una vecchia versione del programma, e il server ha inviato un messaggio che il tuo programma non è in grado di comprendere. + + + + Your username must respect these rules: + Il nome utente deve seguire queste regole: + + + + is %1 - %2 characters long + essere lungo %1 - %2 caratteri + + + + can %1 contain lowercase characters + %1 può contenere caratteri minuscoli + + + + + + + NOT + NON + + + + can %1 contain uppercase characters + %1 può contenere caratteri maiuscoli + + + + can %1 contain numeric characters + %1 può contenere numeri + + + + can contain the following punctuation: %1 + può contenere la punteggiatura: %1 + + + + first character can %1 be a punctuation mark + il primo carattere %1 può essere punteggiatura + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + Puoi usare solo i caratteri A-Z, a-z, 0-9, _, ., e - nel tuo nome utente. + + + + + + + + Registration denied + Registrazione negata + + + + Registration is currently disabled on this server + La registrazione è disabilitata su questo server + + + + There is already an existing account with the same user name. + Esiste già un account con lo stesso nome utente. + + + + It's mandatory to specify a valid email address when registering. + E' obbligatorio specificare un indirizzo email valido per la registrazione. + + + + Too many registration attempts from your IP address. + Ci sono troppi tentativi di registrazione dal tuo indirizzo. + + + + Password too short. + Password troppo corta. + + + + Registration failed for a technical problem on the server. + La registrazione è fallita a causa di un problema tecnico sul server. + + + + Unknown registration error: %1 + Errore di registrazione sconosciuto: %1 + + + + Account activation failed + Attivazione account fallita + + + Socket error: %1 Errore nella connessione: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Stai cercando di connetterti a un server obsoleto. Declassa la versione di Cockatrice per farlo funzionare o connetti ad un altro server. La tua versione è la %1, la versione remota è la %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. La tua versione di Cockatrice è obsoleta. Aggiorna la tua versione di Cockatrice. La tua versione è la %1, la versione online è la %2. - + Connecting to %1... Connessione a %1... - + + Registering to %1 as %2... + Registrazione su %1 come %2... + + + Disconnected Disconnesso - + Connected, logging in at %1 Connesso, login in corso su %1 - - Logged in as %1 at %2 - Loggato come %1 su %2 - - - + &Connect... &Connetti... - + &Disconnect &Disconnetti - + Start &local game... Inizia &partita in locale... - + &Watch replay... &Visualizza replay... - + &Deck editor &Editore di mazzi - + &Full screen &Schermo intero - + + &Register to server... + &Registrati sul server... + + + &Settings... &Impostazioni... - - + + &Exit &Esci - + A&ctions A&zioni - + &Cockatrice &Cockatrice - + &About Cockatrice &Info su Cockatrice - + &Help &Aiuto - - Check card updates... + + Check for card updates... Aggiorna carte... - - + + A card database update is already running. + L'aggiornamento delle carte è già in corso. + + + + Unable to run the card database updater: + Impossibile avviare l'aggiornamento delle carte: + + + + The card database updater exited with an error: %1 + L'aggiornamento delle carte ha causato un errore: %1 + + + + Update completed successfully. Cockatrice will now reload the card database. + Aggiornamento delle carte completato. Il database delle carte verrà ricaricato. + + + + Information Informazione - - A card update is already ongoing. - L'aggiornamento delle carte è già in corso. + + Troubleshooting + Risoluzione dei problemi - - Unable to run the card updater: - Impossibile avviare l'aggiornamento delle carte: + + F.A.Q. + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + Il tuo account non è ancora attivo. +Per attivarlo inserisci il codice di attivazione ricevuto tramite email + + + failed to start. avvio fallito. - + crashed. crashato. - + timed out. time out. - + write error. errore di scrittura. - + read error. errore di lettura. - + unknown error. errore sconosciuto. - - - The card updater exited with an error: %1 - L'aggiornamento delle carte ha causato un errore: %1 - - - - Card update completed successfully. Will now reload card database. - Aggiornamento delle carte completato. Il database delle carte verrà ricaricato. - MessageLogWidget @@ -2119,62 +2586,62 @@ La tua versione è la %1, la versione online è la %2. dall'esilio - + the bottom card of %1's library l'ultima carta del grimorio di %1 - + the bottom card of his library l'ultima carta del suo grimorio - + the bottom card of her library l'ultima carta del suo grimorio - + from the bottom of %1's library dalla fine del grimorio di %1 - + from the bottom of his library dal fondo del suo grimorio - + from the bottom of her library dal fondo del suo grimorio - + the top card of %1's library la prima carta del grimorio di %1 - + the top card of his library la prima carta del suo grimorio - + the top card of her library la prima carta del suo grimorio - + from the top of %1's library dalla cima del grimorio di %1 - + from the top of his library dalla cima del suo grimorio - + from the top of her library dalla cima del suo grimorio @@ -2577,121 +3044,121 @@ La tua versione è la %1, la versione online è la %2. %1 rimuove uno segnalino %2 su %3 (adesso ne ha %4).%1 rimuove %n segnalini %2 su %3 (adesso ne ha %4). - + %1 taps her permanents. female %1 tappa i suoi permanenti. - + %1 untaps her permanents. female %1 stappa i suoi permanenti. - + %1 taps his permanents. male %1 tappa i suoi permanenti. - + %1 untaps his permanents. male %1 stappa i suoi permanenti. - + %1 taps %2. female %1 tappa %2. - + %1 untaps %2. female %1 stappa %2. - + %1 taps %2. male %1 tappa %2. - + %1 untaps %2. male %1 stappa %2. - + %1 sets counter %2 to %3 (%4%5). female %1 imposta i segnalini %2 a %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 imposta i segnalini %2 a %3 (%4%5). - + %1 sets %2 to not untap normally. female %1 imposta che %2 non stappa normalmente. - + %1 sets %2 to not untap normally. male %1 imposta che %2 non stappa normalmente. - + %1 sets %2 to untap normally. female %1 imposta che %2 stappa normalmente. - + %1 sets %2 to untap normally. male %1 imposta che %2 stappa normalmente. - + %1 sets PT of %2 to %3. female %1 imposta la FC di %2 a %3. - + %1 sets PT of %2 to %3. male %1 imposta la FC di %2 a %3. - + %1 sets annotation of %2 to %3. female %1 imposta le annotazioni di %2 con %3. - + %1 sets annotation of %2 to %3. male %1 imposta le annotazioni di %2 con %3. - + %1 is looking at %2. female %1 sta guardando il %2. - + %1 is looking at %2. male %1 sta guardando il %2. @@ -2707,174 +3174,174 @@ La tua versione è la %1, la versione online è la %2. %1 sta guardando le prime %2 carte di %3.%1 sta guardando le prime %n carte di %2. - + %1 stops looking at %2. female %1 non sta più guardando il %2. - + %1 stops looking at %2. male %1 non sta più guardando il %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 rivela %2 a %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 rivela %2 a %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 rivela %2 a %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 rivela %2 a %3. + + + %1 reveals %2. + female + %1 rivela %2. + %1 reveals %2. - female - %1 rivela %2. - - - - %1 reveals %2. male %1 rivela %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 rivela a caso %2%3 a %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 rivela a caso %2%3 a %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 rivela a caso %2%3 a %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 rivela a caso %2%3 a %4. - + %1 randomly reveals %2%3. female %1 rivela a caso %2%3. - + %1 randomly reveals %2%3. male %1 rivela a caso %2%3. - + %1 peeks at face down card #%2. female %1 ha sbirciato la faccia in giù della carta #%2. - + %1 peeks at face down card #%2. male %1 ha sbirciato la faccia in giù della carta #%2. - + %1 peeks at face down card #%2: %3. female %1 ha sbirciato la faccia in giù della carta #%2: %3. - + %1 peeks at face down card #%2: %3. male %1 ha sbirciato la faccia in giù della carta #%2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 rivela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 rivela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 rivela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 rivela %2%3 a %4. - + %1 reveals %2%3. female %1 rivela %2%3. - + %1 reveals %2%3. male %1 rivela %2%3. - + %1 is now keeping the top card %2 revealed. %1 sta tenendo la prima carta %2 rivelata. - + %1 is not revealing the top card %2 any longer. %1 non sta più rivelando la prima carta %2. - + It is now %1's turn. female È il turno di %1. - + It is now %1's turn. male È il turno di %1. - + a card una carta @@ -2937,12 +3404,12 @@ La tua versione è la %1, la versione online è la %2. - + ending phase Fase finale - + untap step Interfase di stappo @@ -3052,64 +3519,64 @@ La tua versione è la %1, la versione online è la %2. %1 rimuove %2 %3 segnalino(i) da %4 (adesso ne ha %5). - + %1 is looking at the top %2 card(s) %3. female %1 sta guardando le prime %2 carta(e) di %3. - + %1 is looking at the top %2 card(s) %3. male %1 sta guardando le prime %2 carta(e) di %3. - + upkeep step - Interfase di matenimento + Interfase di mantenimento - + draw step Interfase di pescaggio - + first main phase Prima fase principale - + beginning of combat step Interfase di inizio combattimento - + declare attackers step Interfase di dichiarazione degli attaccanti - + declare blockers step Interfase di dichiarazione dei bloccanti - + combat damage step Interfase di risoluzione del danno da combattimento - + end of combat step Interfase di fine combattimento - + second main phase Seconda fase principale - + It is now the %1. È adesso la %1. @@ -3117,62 +3584,74 @@ La tua versione è la %1, la versione online è la %2. MessagesSettingsPage - + Chat settings Impostazioni Chat - + + Custom alert words + Lista parole evidenziate + + + Enable chat mentions Abilita citazioni in chat - + + Enable mention completer + Abilita completamento citazioni + + + In-game message macros Messaggi rapidi in partita - - Ignore unregistered users in main chat - Ignora gli utenti non registrati nella chat principale - - - - Ignore chat room messages sent by unregistered users. + + Ignore chat room messages sent by unregistered users Ignora i messaggi in chat inviati dagli utenti non registrati - - Ignore private messages sent by unregistered users. + + Ignore private messages sent by unregistered users Ignora i messaggi privati inviati dagli utenti non registrati - + + Enable desktop notifications for private messages + Abilita notifiche sul desktop per i messaggi privati + + + + Separate words with a space, alphanumeric characters only + Separare le parole con uno spazio; solo caratteri alfanumerici + + + + Invert text color Inverti colore testo - - Enable desktop notifications for private messages. - Abilita notifiche sul desktop per i messaggi privati - - - + Enable desktop notification for mentions. Abilita le notifiche sul desktop per le menzioni. - + + (Color is hexadecimal) (Colore in esadecimale) - + Add message Aggiungi messaggio - + Message: Messaggio: @@ -3187,7 +3666,7 @@ La tua versione è la %1, la versione online è la %2. Upkeep step - Interfase di matenimento + Interfase di mantenimento @@ -3238,336 +3717,337 @@ La tua versione è la %1, la versione online è la %2. Player - + &View library &Guarda mazzo - + Move top cards to &graveyard... Metti le prime carte nel &cimitero... - + View &top cards of library... Guarda &le prime carte del mazzo... - + &View graveyard &Guarda cimitero - + &View sideboard &Guarda la sideboard - + Player "%1" Giocatore "%1" - - - + + + + &Hand &Mano - + &Reveal hand to... &Rivela mano a... - + Reveal r&andom card to... Rivela carta c&asuale a... - + &Library &Mazzo - - - + + + &Graveyard &Cimitero - + &Sideboard &Sideboard - + Red Rosso - + Yellow Giallo - + Green Verde - + View top cards of library Guarda le prima carte del grimorio - + Number of cards: Numero di carte: - + &Draw card &Pesca una carta - + Reveal top cards of library Rivela le prime carte del grimorio - + Number of cards: (max. %1) Numero di carte: (max %1) - + &View exile &Guarda zona di esilio - - - + + + &Exile &Esilio - + Reveal t&op cards to... Rivela le prime carte a... - + D&raw cards... P&esca carte... - + Take &mulligan Mu&lliga - + &Shuffle &Mischia - + &Counters &Segnalini - + &Untap all permanents &Stappa tutti i permanenti - + R&oll die... L&ancia un dado... - + &Create token... &Crea una pedina... - + C&reate another token C&rea un'altra pedina - + S&ay P&arla - + &Move hand to... &Sposta mano in... - - - - + + + + &Top of library Cima al grimorio - - - - + + + + &Bottom of library Fondo al grimorio - + &Move graveyard to... &Muovi cimitero in... - + &Move exile to... &Muovi esilio in... - + Reveal &library to... Rive&la grimorio a... - + &Always reveal top card Rivela &sempre la prima carta - + O&pen deck in deck editor A&pri mazzo nell'editore di mazzi - + &Undo last draw &Annulla l'ultima pescata - + Play top card &face down Gioca la prima carta faccia in giù - + Move top cards to &exile... Metti le prime carte in &esilio... - + Put top card on &bottom Metti la prima carta in &fondo - + Put bottom card &in graveyard Metti l'ultima carta nel c&imitero - + Cr&eate predefined token Cr&a pedina predefinita - + C&ard C&arta - + &All players &Tutti i giocatori - + &Play &Gioca - + &Hide &Nascondi - + Play &Face Down Gioca a &Faccia in giù - + &Tap &Tappa - + &Untap &Stappa - + Toggle &normal untapping Non &stappa normalmente - + &Flip &Gira - + &Peek at card face &Sbircia la faccia della carta - + &Clone &Copia - + Attac&h to card... Attacc&a alla carta... - + Unattac&h Stacc&a - + &Draw arrow... &Disegna una freccia... - + &Increase power &Aumenta forza - + &Decrease power &Diminuisci forza - + I&ncrease toughness A&umenta costituzione - + D&ecrease toughness D&iminuisci costituzione @@ -3577,22 +4057,22 @@ La tua versione è la %1, la versione online è la %2. Au&menta forza e costituzione - + Dec&rease power and toughness Dim&inuisci forza e costituzione - + Set &power and toughness... Imposta &forza e costituzione... - + &Set annotation... &Imposta annotazioni... - + &Add counter (%1) &Aggiungi contatore (%1) @@ -3602,103 +4082,108 @@ La tua versione è la %1, la versione online è la %2. &Rimuovi contatore (%1) - + &Set counters (%1)... &Imposta contatori (%1)... - + Draw cards Pesca carte - - - - + + + + Number: Numero: - + Move top cards to grave Metti le prima carte nel cimitero - + Move top cards to exile Metti le prime carte in esilio - + Roll die Lancia un dado - + Number of sides: Numero di facce: - + Set power/toughness Imposta forza/costituzione - + Please enter the new PT: Inserisci la nuova FC: - + Set annotation Imposta annotazione - + Please enter the new annotation: Inserisci le nuove annotazioni: - + Set counters Imposta i segnalini + + + Cr&eate related card + Cr&ea carta collegata + QMenuBar - + Services Servizi - + Hide %1 Nascondi %1 - + Hide Others Nascondi altre - + Show All Mostra tutte - + Preferences... Preferenze... - + Quit %1 Esci da %1 - + About %1 Informazioni su %1 @@ -3706,7 +4191,7 @@ La tua versione è la %1, la versione online è la %2. QObject - + Cockatrice replays (*.cor) Replay di Cockatrice (*.cor) @@ -3796,14 +4281,43 @@ La tua versione è la %1, la versione online è la %2. + Permissions + Permessi + + + Players Giocatori - + Games Partite + + + + Error + Errore + + + + You do not have the proper permission to join this room. + Non hai i permessi necessari ad entrare in questa partita. + + + + Failed to join the room due to an unknown error. + Errore cercando di entrare nella partita. + + + + SequenceEdit + + + Shortcut already in use + Combinazione già usata + SetsModel @@ -3846,7 +4360,7 @@ La tua versione è la %1, la versione online è la %2. &Tempo allo spegnimento (minuti): - + Shut down server Spegni il server @@ -3854,37 +4368,37 @@ La tua versione è la %1, la versione online è la %2. SoundSettingsPage - + Choose path Scegli percorso - + Enable &sounds Abilita &suoni - + Path to sounds directory: Percorso alla cartella dei suoni: - + Test system sound engine Prova il funzionamento dei suoni - + Sound settings Impostazioni suoni - + Master volume requires QT5 Il controllo volume richiede QT5 - + Master volume Volume @@ -3892,42 +4406,47 @@ La tua versione è la %1, la versione online è la %2. TabAdmin - + Update server &message Aggiorna &messaggio del server - + &Shut down server &Spegni il server - + + &Reload configuration + &Ricarica configurazione + + + Server administration functions Funzioni da amministratore del server - + &Unlock functions &Sblocca funzioni - + &Lock functions &Blocca funzioni - + Unlock administration functions Sblocca le funzioni da amministratore - + Do you really want to unlock the administration functions? Vuoi veramente sbloccare le funzioni da amministratore? - + Administration Amministrazione @@ -3935,183 +4454,223 @@ La tua versione è la %1, la versione online è la %2. TabDeckEditor - + &Print deck... &Stampa mazzo... - + &Close &Chiudi - + &Edit sets... &Modifica set... - - &Clear search - &Annulla ricerca + + Filters + Filtri - + + &Clear all filters + Rimuovi tutti + + + + Delete selected + Rimuovi selez. + + + Deck &name: &Nome mazzo: - + &Comments: &Commenti: - + Hash: Hash: - + &New deck &Nuovo mazzo - + &Load deck... &Carica mazzo... - + &Save deck &Salva mazzo - + Save deck &as... Salva mazzo &con nome... - + Load deck from cl&ipboard... Carica mazzo dagli app&unti... - + Save deck to clip&board Salva mazzo nei app&unti - + &Analyze deck on deckstats.net &Analizza il mazzo con deckstats.net - + Open custom image folder Apri cartella immagini personalizzate - + + Open custom sets folder + Apri cartella set personalizzati + + + Add card to &maindeck Aggiungi carta al &grimorio - + Add card to &sideboard Aggiungi carta al &sideboard - + &Deck Editor Editor di mazzi - + C&ard Database Database delle C&arte - + + Show/Hide card information + Mostra/nascondi info carta + + + + Show/Hide deck + Mostra/nascondi mazzo + + + + Show/Hide filters + Mostra/nascondi filtri + + + + Reset layout + Resetta disposizione + + + + Card Info + Info carta + + + + Deck + Mazzo + + + Welcome Benvenuto - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. Ciao! E' la prima volta che usi questa versione di Cockatrice. Tutti i set nel database delle carte sono stati attivati. -Se vuoi cambiare l'ordine dei set o disattivarne alcuni, ai un'occhiata alla finestra "Modifica set". +Se vuoi cambiare l'ordine dei set o disattivarne alcuni, dai un'occhiata alla finestra "Modifica set". - - Show card text only - Mostra solo testo carta - - - + &Remove row &Rimuovi carta - + &Increment number &Aumenta il numero - + &Decrement number &Diminuisci il numero - + Edit &tokens... Modifica &pedine... - + Deck: %1 Mazzo: %1 - + Are you sure? Sei sicuro? - + The decklist has been modified. Do you want to save the changes? La lista del mazzo è stata modificata. Vuoi salvare i cambiamenti? - + Load deck Carica mazzo - - - + + + Error Errore - + The deck could not be saved. Il mazzo non può essere salvato. - - + + The deck could not be saved. Please check that the directory is writable and try again. Il mazzo non può essere salvato. Controlla se la cartella è valida e prova ancora. - + Save deck Salva mazzo @@ -4208,94 +4767,99 @@ Please enter a name: TabGame - + &Phases &Fasi - + &Game &Partita - + Next &phase Prossima &fase - + Next &turn Prossimo &turno - + &Remove all local arrows &Rimuovi tutte le frecce - + + Rotate View Cl&ockwise + Ruota vista in senso &orario + + + + Rotate View Co&unterclockwise + R&uota vista in senso antiorario + + + Game &information &Informazioni partita - + &Concede &Concedi - + &Leave game &Lascia partita - + C&lose replay C&hiudi replay - + &Say: &Parla: - + Concede Concedi - + Are you sure you want to concede this game? Vuoi veramente concedere la partita? - + Leave game Lascia la partita - + Are you sure you want to leave this game? Sei sicuro di voler lasciare la partita? - + You are flooding the game. Please wait a couple of seconds. Stai spammando la partita. Attendi un paio di secondi. - + You have been kicked out of the game. Sei stato kickato fuori dalla partita. - - Replay %1: %2 - Replay %1: %2 - - - - Game %1: %2 - Partita %1: %2 + + REPLAY + REPLAY @@ -4339,54 +4903,54 @@ Please enter a name: TabReplays - + Local file system File locali - + Server replay storage Replay sul server - + Watch replay Visualizza replay - - + + Delete Elimina - + Download replay Scarica replay - + Toggle expiration lock Metti/togli blocco scadenza - + Delete local file Elimina il file locale - + Are you sure you want to delete "%1"? Sei sicuro di voler eliminare "%1"? - + Delete remote replay Elimina replay remoto - + Are you sure you want to delete the replay of game %1? Sei sicuro di voler eliminare il replay della partita %1? @@ -4399,47 +4963,47 @@ Please enter a name: TabRoom - + &Say: &Parla: - + Chat Chat - + &Room &Stanza - + &Leave room &Lascia stanza - + &Clear chat Pulisci &chat - + Chat Settings... Impostazioni Chat... - + mentioned you. ti ha menzionato. - + Click to view Clicca per visualizzare - + You are flooding the chat. Please wait a couple of seconds. Stai spammando la chat. Attendi un paio di secondi. @@ -4455,15 +5019,25 @@ Please enter a name: TabSupervisor - + Are you sure? Sei sicuro? - + There are still open games. Are you sure you want to quit? Ci sono ancora delle partite aperte. Sei sicuro di voler uscire? + + + Promotion + Promozione + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + Sei stato promosso a moderatore. Esci e rientra per rendere effettiva la promozione. + TabUserLists @@ -4486,169 +5060,301 @@ Please enter a name: UserContextMenu - + User &details &Dettagli utente - + Private &chat &Chat privata - + Show this user's &games Visualizza le &partite dell'utente - + Add to &buddy list Aggiungi alla lista &amici - + Remove from &buddy list Rimuovi dalla lista &amici - + Add to &ignore list Aggiungi alla lista &ignorati - + Remove from &ignore list Rimuovi dalla lista &ignorati - + Kick from &game Caccia dalla &partita - + Ban from &server Banna dal &server - + + &Promote user to moderator + &Promuovi utente a moderatore + + + + Dem&ote user from moderator + Degrada il m&oderatore ad utente + + + %1's games Partite di %1 + + + + Success + Successo + + + + Successfully promoted user. + Utente promosso. + + + + Successfully demoted user. + Utente degradato. + + + + + Failed + Fallito + + + + Failed to promote user. + Promozione fallita. + + + + Failed to demote user. + Degradazione fallita. + UserInfoBox - + User information Informazioni utente - + Real name: Nome reale: - - Gender: - Sesso: + + Pronouns: + Pronomi: - + Location: Posizione: - + User level: Livello utente: - + Account Age: Età Account: - + + Edit + Modifica + + + + Change password + Cambia password + + + + Change avatar + Cambia avatar + + + Administrator Amministratore - + Moderator Moderatore - + Registered user Utente registrato - - + + Unregistered user Utente non registrato - + Unknown Sconosciuta - + Year Anno - + Years Anni - + Day Giorno - + Days Giorni + + + + + Information + Informazione + + + + User information updated. + Informazioni utente aggiornate. + + + + + + + + + + + Error + Errore + + + + This server does not permit you to update your user informations. + Questo server non ti permette di aggiornare le tue informazioni utente. + + + + + An error occured while trying to update your user informations. + Si è verificato un errore durante l'aggiornamento delle informazioni utente. + + + + Password changed. + Password modificata. + + + + This server does not permit you to change your password. + Questo server non ti permette di modificare la password. + + + + The new password is too short. + La nuova password è troppo corta. + + + + The old password is incorrect. + La vecchia password è sbagliata. + + + + Avatar updated. + Avatar modificato. + + + + This server does not permit you to update your avatar. + Questo server non ti permette di aggiornare il tuo avatar. + + + + An error occured while trying to updater your avatar. + Si è verificato un errore durante l'aggiornamento del tuo avatar. + UserInterfaceSettingsPage - + General interface settings Impostazioni di interfaccia generale - + Enable notifications in taskbar Abilita notifiche sulla barra delle applicazioni - + Notify in the taskbar for game events while you are spectating Abilita notifiche anche per i giochi in cui si è solo uno spettatore - + &Double-click cards to play them (instead of single-click) &Doppio click sulle carte per giocarle (disabilitato un solo click) - + &Play all nonlands onto the stack (not the battlefield) by default Gioca tutte le carte non terra sulla &pila (invece che sul campo di battaglia) - + + Annotate card text on tokens + Annota il testo della carta sulle pedine + + + Animation settings Impostazioni di animazione - + &Tap/untap animation Animazioni &Tappa/Stappa @@ -4656,22 +5362,22 @@ Please enter a name: UserList - + Users connected to server: %1 Utenti connessi al server: %1 - + Users in this room: %1 Utenti in questa stanza: %1 - + Buddies online: %1 / %2 Amici online: %1 / %2 - + Ignored users online: %1 / %2 Utenti ignorati online: %1 / %2 @@ -4693,31 +5399,45 @@ Please enter a name: Move selected set up Muovi set selezionato su + + + Move selected set to the top + Muovi set selezionato in cima + Move selected set down Muovi set selezionato giù - - - Move selected set to top - Muovi set selezionato in cima - - Move selected set to bottom + Move selected set to the bottom Muovi set selezionato in fondo - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. - Abilita i set che buoi avere a disposizione nell'editor di mazzi. -Sposta i set usando il mouse o i pulsanti a sinistra per modificarne l'ordine, o clicca sull'intestazione di una colonna per ordinare i set su quel campo. -L'ordine dei set decide quali set verranno utilizzati per cariare le immagini delle carte. -I set disabilitati saranno comunque usati per caricare le immagini. + hints: + suggerimenti: + + + + Enable the sets that you want to have available in the deck editor + Abilita i set che vuoi avere a disposizione nell'editor di mazzi + + + + Move sets around to change their order, or click on a column header to sort sets on that field + Muovi i set per cambiare il loro ordine, o clicca l'intestazione di una colonna per ordinare i set su quel campo + + + + Sets order decides the source that will be used when loading images for a specific card + L'ordine dei set decide l'origine che verrà usata per caricare le immagini per le specifiche carte + + + + Disabled sets will be used for loading images only if all the enabled sets failed + I set disabilitati verranno usati per caricare immagini solo dopo tutti i set abilitati @@ -4725,12 +5445,12 @@ I set disabilitati saranno comunque usati per caricare le immagini.Modifica impostazioni - + Success Successo - + The sets database has been saved successfully. Il database dei set è stato salvato correttamente. @@ -4758,4 +5478,561 @@ I set disabilitati saranno comunque usati per caricare le immagini.vista raggruppata + + shortcutsTab + + + Form + Schermate + + + + Main Window + Finestra principale + + + + Deck editor + Editor di mazzi + + + + Local gameplay + Partita locale + + + + Watch replay + Vista replay + + + + Connect + Connetti + + + + Register + Registra + + + + Full screen + Schermo intero + + + + Settings + Impostazioni + + + + Check for card updates + Aggiorna carte + + + + Exit + Esci + + + + Deck Editor + Editor di mazzi + + + + Analyze deck + Analizza mazzo + + + + Load deck (clipboard) + Carica mazzo (appunti) + + + + Clerar all filters + Pulisci filtri + + + + New deck + Nuovo mazzo + + + + Clear one filter + Rimuovi filtro + + + + Open custom folder + Apri cartella immagini + + + + Close + Chiudi + + + + Print deck + Stampa mazzo + + + + Edit sets + Modifica set + + + + Delete card + Cancella carta + + + + Edit tokens + Modifica pedine + + + + Reset layout + Resetta disposizione + + + + Add card + Aggiungi carta + + + + Save deck + Salva mazzo + + + + Remove card + Rimuovi carta + + + + Save deck as + Salva mazzo con nome + + + + Load deck + Carica mazzo + + + + Save deck (clipboard) + Salva mazzo (appunti) + + + + + Counters + Segnalini + + + + Life + Punti vita + + + + + + + + Set + Imposta + + + + + + + Add + Aggiungi + + + + + + + Remove + Togli + + + + Red + Rosso + + + + Green + Verde + + + + Yellow + Giallo + + + + Mainwindow / Deck editor + Finestra / Editor mazzi + + + + Power / toughness + Forza / costituzione + + + + Power and toughness + Forza e costituzione + + + + Add (+1/+1) + Aggiungi (+1/+1) + + + + Remove (-1/-1) + Togli (-1/-1) + + + + Toughness + Costituzione + + + + Remove (-0/-1) + Rimuovi (-0/-1) + + + + Add (+0/+1) + Aggiungi (+0/+1) + + + + Power + Forza + + + + Remove (-1/-0) + Togli (-1/-0) + + + + Add (+1/+0) + Aggiungi (+1/+0) + + + + Game Phases + Fasi di gioco + + + + Untap + Stap + + + + Disconnect + Disconnetti + + + + Upkeep + Mantenimento + + + + + Draw + Acquisizione + + + + Main 1 + Principale 1 + + + + Start combat + Inizio combattimento + + + + Attack + Attacco + + + + Block + Blocco + + + + Damage + Danni + + + + End combat + Fine combattimento + + + + Main 2 + Principale 2 + + + + End + Fine + + + + Next phase + Prossima fase + + + + Next turn + Prossimo turno + + + + Player + Giocatore + + + + Tap Card + Tappa carta + + + + Untap Card + Stappa carta + + + + Untap all + Stappa tutte + + + + Toogle untap + Non stappa mai + + + + Flip card + Gira carta + + + + Peek card + Sbircia carta + + + + Play card + Gioca carta + + + + Attach card + Attacca carta + + + + Unattach card + Stacca carta + + + + Clone card + Clona carta + + + + Create token + Crea pedina + + + + Create another token + Crea un'altra pedina + + + + Set annotation + Imposta annotazione + + + + Phases / P/T / Player + Fasi / F/C / Giocatore + + + + Move card to + Metti carta su + + + + Bottom library + Fondo grimorio + + + + Top library + Cima grimorio + + + + + Graveyard + Cimitero + + + + + Exile + Esilio + + + + Hand + Mano + + + + View + Viste + + + + Library + Mazzo + + + + Tops card of library + Prime carte del mazzo + + + + Sideboard + Sideboard + + + + Close recent view + Chiudi ultima vista + + + + Pre-play + Prima del gioco + + + + Load remote deck + Carica mazzo remoto + + + + Load local deck + Carica mazzo dal pc + + + + Game play + Gioco + + + + Draw arrow + Disegna freccia + + + + Leave game + Lascia partita + + + + Remove local arrows + Rimuovi tutte le frecce + + + + Concede + Concedi + + + + Roll dice + Lancia dado + + + + Rotate view CW + Ruota vista orario + + + + Shuffle library + Mescola mazzo + + + + Rotate view CCW + Ruota vista antiorario + + + + Mulligan + Mulliga + + + + Draw card + Pesca carta + + + + Draw cards + Pesca carte + + + + Undo draw + Annulla pescata + + + + Always reveal top card + Rivela sempre la prima carta + + + + Draw / Move / View / Game play + Pesca / Sposta / Vedi / Gioco + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_ja.ts b/cockatrice/translations/cockatrice_ja.ts index f782515b..7caf1518 100644 --- a/cockatrice/translations/cockatrice_ja.ts +++ b/cockatrice/translations/cockatrice_ja.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... カウンターの数を決める... - + Set counter カウンターの設定 - + New value for counter '%1': カウンター '%1'の新しい値: @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures 背景画像の設定 - + Hand background: 手札: - + Stack background: スタック: - + Table background: 戦場: - + Player info background: プレイヤー情報: - + Card back: カード背面: - + Card rendering カードの描画 - + Display card names on cards having a picture 画像持ちカードのカード名を表示する - + Scale cards on mouse over マウスオーバーでカードを拡大する - + Hand layout 手札のレイアウト - + Display hand horizontally (wastes space) 手札を横に並べる - + Enable left justification 左揃えを有効 - + Table grid layout テーブルのレイアウト - + Invert vertical coordinate カード配置の垂直反転 - + Minimum player count for multi-column layout: プレイヤーを複数列レイアウトにする最少人数: - - - - - + + + + + Choose path 画像の指定 @@ -107,99 +107,104 @@ BanDialog - + ban &user name ユーザー名でBAN - + ban &IP address IPアドレスでBAN - + + ban client I&D + クライアントIDでBAN + + + Ban type BANタイプ - + &permanent ban 永久BAN - + &temporary ban 一時BAN - + &Days: - + &Hours: 時間 - + &Minutes: - + Duration of the ban BAN期間 - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. BANの理由を入力してください。 これはモデレーターによって保存されBANされた人には見えません。 - + Please enter the reason for the ban that will be visible to the banned person. BANの理由を入力してください。これはBANされる人に通知されます。 - + &OK OK - + &Cancel キャンセル - + Ban user from server サーバーからBANされたユーザーです - + Error エラー - - You have to select a name-based or IP-based ban, or both. - 名前によるBANかIPによるBAN、もしくは両方を選択してください。 + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + BANの理由を選んで下さい:不適切な名前、不適切なIPアドレス、クライアントID CardDatabase - + New sets found - + 新しいセットが見つかりました - + %1 new set(s) have been found in the card database. Do you want to enable them? - + %1個の新しいセットが見つかりました。有効にしますか? @@ -230,30 +235,53 @@ This is only saved for moderators and cannot be seen by the banned person.P/T + + CardFrame + + + Image + カード画像 + + + + Description + カード文章 + + + + Both + 画像+文章 + + CardInfoText - + Name: カード名: - + Mana cost: マナ・コスト: - + + Color(s): + 色: + + + Card type: カード・タイプ: - + P / T: P / T: - + Loyalty: 忠誠度: @@ -276,27 +304,32 @@ This is only saved for moderators and cannot be seen by the banned person.全て表示 - + Name: カード名: - + Mana cost: マナ・コスト: - + + Color(s): + 色: + + + Card type: カード・タイプ: - + P / T: P / T: - + Loyalty: 忠誠度: @@ -560,12 +593,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - + Nothing is here... yet まだ何もありませーん - + General 全般 @@ -573,17 +606,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Number カード枚数 - + Card カード名 - + Price 価格 @@ -605,42 +638,42 @@ This is only saved for moderators and cannot be seen by the banned person. DeckViewContainer - - Load &local deck - ローカルからデッキを開く + + Load local deck + デッキ読み込み - - Load d&eck from server - サーバーからデッキを開く + + Load deck from server + サーバーに保存したデッキ - + Ready to s&tart 準備完了! - + S&ideboard unlocked サイドボードをロックする - + S&ideboard locked サイドボードを使用する - + Load deck デッキを開く - + Error エラー - + The selected file could not be loaded. 選択したファイルが開けませんでした。 @@ -686,37 +719,52 @@ This is only saved for moderators and cannot be seen by the banned person. DlgConnect - + + Previous Host + 以前のホスト + + + + New Host + 新しいホスト + + + &Host: ホストIP: - + + Enter host name + ホストの名前を入力 + + + &Port: ポート: - + Player &name: プレイヤーネーム: - + P&assword: パスワード: - + &Save password パスワードを保存する - + A&uto connect at start 起動時に自動的に接続 - + Connect to server サーバーに接続 @@ -724,82 +772,92 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateGame - + &Description: 説明: - + &Password: パスワード: - + P&layers: プレイヤー人数: - + + Re&member settings + + + + Game type ゲームタイプ - + Only &buddies can join フレンドのみ参加可能 - + Only &registered users can join 登録済みプレイヤーのみ参加可能 - + Joining restrictions 参加制限 - + &Spectators can watch 観戦を許可 - + Spectators &need a password to watch 観戦にパスワードが必要 - + Spectators can see &hands 観戦者に手札を見せる - + Spectators can &chat 観戦者はチャットに参加できる - + Spectators 観戦者 - + + &Clear + クリア + + + Create game ゲームを作成 - + Game information ゲーム情報 - + Error エラー - + Server error. サーバーエラー。 @@ -807,96 +865,170 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateToken - + &Name: トークン名: - + Token トークン - + C&olor: 色: - + white - + blue - + black - + red - + green - + multicolor 多色 - + colorless 無色 - + &P/T: P/T: - + &Annotation: 注釈: - + &Destroy token when it leaves the table トークンが戦場を離れる場合破壊する - + Token data トークン設定 - + Show &all tokens すべてのトークンを見る - + Show tokens from this &deck このデッキのトークンを見る - + Choose token from list 一覧からトークンを作成する - + Create token トークンを作成 + + DlgEditAvatar + + + + No image chosen. + 画像がありません。 + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + アバターを変更するには、新しい画像を選択してください。 +現在のアバターを削除するには、新しい画像を選ばずにOKを押してください。 + + + + Browse... + 参照... + + + + Change avatar + アバター変更 + + + + Open Image + アバター画像を開く + + + + Image Files (*.png *.jpg *.bmp) + 画像ファイル (*.png *.jpg *.bmp) + + + + Invalid image chosen. + この画像は使用できません。 + + + + DlgEditPassword + + + Old password: + 前のパスワード: + + + + New password: + 新しいパスワード: + + + + Confirm new password: + 新しいパスワード(再入力): + + + + Change password + パスワード変更 + + + + Error + エラー + + + + The new passwords don't match. + 新しいパスワードが一致しませんでした。 + + DlgEditTokens @@ -989,7 +1121,56 @@ This is only saved for moderators and cannot be seen by the banned person. The chosen name conflicts with an existing card or token. Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. - + 選択された名前は、既存のカードやトークンと競合しています。 +正しく表示するためにセットの設定ダイアログでトークンの設定を有効にしていることを確認してください。 + + + + DlgEditUser + + + Email: + メールアドレス: + + + + Pronouns: + 三人称代名詞の扱い: + + + + Neutral + 中性 + + + + Masculine + 男性 + + + + Feminine + 女性 + + + + Country: + 国籍: + + + + Undefined + 指定なし + + + + Real name: + 本名: + + + + Edit user profile + ユーザー情報編集 @@ -1043,7 +1224,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh 更新 @@ -1053,12 +1234,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di クリップボードからデッキを開く - + + Error エラー - + + Invalid deck list. 無効なデッキリストです。 @@ -1071,22 +1254,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di デッキを開く + + DlgRegister + + + &Host: + ホストアドレス: + + + + &Port: + ポート: + + + + Player &name: + プレイヤー名: + + + + P&assword: + パスワード: + + + + Password (again): + パスワード (再入力) : + + + + Email: + メールアドレス: + + + + Email (again): + メールアドレス (再入力) : + + + + Pronouns: + 三人称代名詞の扱い: + + + + Neutral + 中性 + + + + Masculine + 男性 + + + + Feminine + 女性 + + + + Undefined + 設定なし + + + + + Registration Warning + 登録に関する警告 + + + + Your passwords do not match, please try again. + パスワードが一致しませんでした。もう一度入力して下さい。 + + + + Your email addresses do not match, please try again. + メールアドレスが一致しませんでした。もう一度入力して下さい。 + + + + Country: + 国籍: + + + + Real name: + 本名: + + + + Register to server + サーバーに登録する + + DlgSettings - - - + + + Error エラー - + Unknown Error loading card database 不明なエラー。 - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1380,7 @@ Oracle Importerでデータベースを更新する必要があります。 データベースの場所の設定を変更してください。 - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1397,7 @@ Oracle Importerでデータベースを更新する必要があります。 データベースの場所の設定を変更してください。 - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1406,7 @@ Would you like to change your database location setting? データベースの場所の設定を変更してください。 - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1415,7 @@ Would you like to change your database location setting? データベースの場所の設定を変更してください。 - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1151,7 +1428,7 @@ Would you like to change your database location setting? データベースの場所の設定を変更してください。 - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1164,63 +1441,58 @@ http://github.com/Cockatrice/Cockatrice/issues でチケットを提出してく データベースの場所の設定を変更してください。 - + The path to your deck directory is invalid. Would you like to go back and set the correct path? あなたのデッキディレクトリへのパスは無効です。前に戻って正しいパスを設定してください。 - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? あなたのカード画像ディレクトリへのパスは無効です。前に戻って正しいパスを設定してください。 - + Settings 設定 - + General 全般 - + Appearance 外観 - + User Interface UI - + Deck Editor デッキエディター - + Chat チャット - + Sound - + サウンド + + + + Shortcuts + ショートカット GameSelector - - - C&reate - ゲームを作成 - - - - &Join - 参加する - @@ -1230,7 +1502,7 @@ http://github.com/Cockatrice/Cockatrice/issues でチケットを提出してく - + Error エラー @@ -1275,37 +1547,47 @@ http://github.com/Cockatrice/Cockatrice/issues でチケットを提出してく あなたはゲームの作成者によって参加拒否されています。 - + Join game 参加 - + Password: パスワード: - + Please join the respective room first. 最初にルームに参加してください。 - + Games ゲーム - + &Filter games ゲームフィルタ - + C&lear filter フィルタ解除 - + + C&reate + ゲームを作成 + + + + &Join + 参加する + + + J&oin as spectator 観戦する @@ -1422,97 +1704,107 @@ http://github.com/Cockatrice/Cockatrice/issues でチケットを提出してく GeneralSettingsPage - + Reset/Clear Downloaded Pictures ダウンロードした画像を削除する - - - - - + + + + + Choose path パスを選択 - + Success 完了 - + Downloaded card pictures have been reset. ダウンロードしたカード画像を削除しました。 - + Error エラー - + One or more downloaded card pictures could not be cleared. 一部のカード画像が削除できませんでした。 - + Personal settings 個人設定 - + Language: 言語: - + Download card pictures on the fly カード画像を自動的にダウンロードする(英語) - - Download high-quality card pictures - 高解像度カード画像をダウンロードする + + Download card pictures from a custom URL + カスタムURLからカード画像をダウンロード - + + Custom Card Download URL: + カード画像をダウンロードするURL + + + + Linking FAQ + URLの設定の仕方について(英語) + + + Paths パス - + Decks directory: デッキフォルダ: - + Replays directory: リプレイフォルダ: - + Pictures directory: カード画像フォルダ: - + Card database: カードデータベース: - + Token database: トークンデータベース: - + Picture cache size: 画像キャッシュサイズ: - - + + English 日本語 (Japanese) @@ -1520,57 +1812,49 @@ http://github.com/Cockatrice/Cockatrice/issues でチケットを提出してく MainWindow - + There are too many concurrent connections from your address. あなたのアドレスからの同時接続が多すぎます。 - + Scheduled server shutdown. サーバーシャットダウン予定時間。 - + Banned by moderator モデレーターによるBAN - + Expected end time: %1 予想終了時間: %1 - + This ban lasts indefinitely. 永久BANされています。 - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - 無効なユーザー名。 -ユーザー名には半角英数字、および半角の「_」「.」「-」のみが使えます。 - - - + Connection closed 通信切断 - + The server has terminated your connection. Reason: %1 サーバーはあなたの接続を切断しました。 理由: %1 - + Scheduled server shutdown サーバーシャットダウン予定時刻 - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1579,304 +1863,489 @@ Reason for shutdown: %1 シャットダウンの理由: %1 - + Number of players プレイヤー人数 - + Please enter the number of players. プレイヤーの人数を入れてください. - - + + Player %1 プレイヤー %1 - + Load replay リプレイを開く - + About Cockatrice Cockatriceについて - + Version %1 Version %1 - + Translators: 翻訳者: - + Project Manager: プロジェクトマネージャ: - + + The server has reached its maximum user capacity, please check back later. + サーバーが満員です。 + + + + + Invalid username. + ユーザー名が無効です。 + + + + You have been logged out due to logging in at another location. + + + + + + Success + 成功 + + + + Registration accepted. +Will now login. + 登録が完了しました。 +ログインします。 + + + + Account activation accepted. +Will now login. + アカウント有効化が完了しました。 +ログインします。 + + + Past Project Managers: 元プロジェクトマネージャ: - + Developers: デベロッパ: - + Our Developers リンク先参照 - + Help Develop! 開発者募集中! - + Recognition Page リンク先参照 - + Help Translate! 翻訳者募集中! - + Support: サポート: - + Report an Issue 問題を報告 - - Suggest an Improvement - 改善を提案 - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error エラー - + Server timeout サーバータイムアウト - + Incorrect username or password. Please check your authentication information and try again. ユーザー名かパスワードが正しくありません。 - + There is already an active session using this user name. Please close that session first and re-login. 既にこのユーザー名を使用しているアクティブなセッションが存在します。 アクティブなセッションを閉じて再ログインしてください。 - + + You are banned until %1. あなたは%1までBANされています。 - + + You are banned indefinitely. あなたは無期限BANされています。 - - This server requires user registration. - このサーバーはユーザー登録が必要です。 + + This server requires user registration. Do you want to register now? + このサーバーはユーザー登録が必要です。 +今すぐ登録しますか? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + このサーバは、クライアントのIDを必要とします。あなたのクライアントは、IDを生成が失敗しているか、変更されたクライアントを実行しています。 +クライアントを再起動してもう一度お試し下さい。 + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + アカウント有効化 + + + Unknown login error: %1 不明なログインエラー: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + +これは通常、あなたのクライアントのバージョンが古くなっていて、サーバーがあなたのクライアントを認識できないと返答してきていることを意味しています。 + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + ユーザー名には『半角英数字』、および半角の「_」「.」「-」のみが使えます。 + + + + + + + + Registration denied + 登録が拒否されました。 + + + + Registration is currently disabled on this server + 現在このサーバーでは登録が無効になっています。 + + + + There is already an existing account with the same user name. + 既に同じユーザー名のアカウントが登録されています。 + + + + It's mandatory to specify a valid email address when registering. + 登録時に有効な電子メールアドレスを指定する必要があります。 + + + + Too many registration attempts from your IP address. + あなたのアドレスからのユーザー登録試行が多すぎます。 + + + + Password too short. + パスワードが短すぎます。 + + + + Registration failed for a technical problem on the server. + ユーザー登録に失敗しました(サーバーの技術的問題)。 + + + + Unknown registration error: %1 + + + + + Account activation failed + アカウントの有効化に失敗しました。 + + + Socket error: %1 ソケットエラー: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. あなたは古いバージョンのサーバーに接続しようとしています。Cockatriceのバージョンをダウングレードするか適切なサーバーに接続してください。 ローカルVer %1,サーバーVer %2 - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. あなたのCockatriceのバージョンがサーバーのバージョンより古いです。Cockatriceをアップデートしてください. ローカルVer %1,サーバーVer %2 - + Connecting to %1... %1へ接続しています... - + + Registering to %1 as %2... + %1に%2として登録中... + + + Disconnected 切断されました - + Connected, logging in at %1 接続完了、%1にログイン中 - - Logged in as %1 at %2 - %1として%2にログイン - - - + &Connect... 接続... - + &Disconnect 切断 - + Start &local game... ローカルゲームを開始... - + &Watch replay... リプレイを見る... - + &Deck editor デッキエディター - + &Full screen フルスクリーン - + + &Register to server... + サーバーに登録する + + + &Settings... 設定... - - + + &Exit 終了 - + A&ctions アクション - + &Cockatrice Cockatrice - + &About Cockatrice Cockatriceについて - + &Help ヘルプ - - Check card updates... - + + Check for card updates... + カードデータベース更新 - - + + A card database update is already running. + データベース更新は既に起動中です。 + + + + Unable to run the card database updater: + データベース更新が実行できませんでした: + + + + The card database updater exited with an error: %1 + データベース更新に失敗しました: %1 + + + + Update completed successfully. Cockatrice will now reload the card database. + 更新が正常に完了しました。新しいデータベースを読み込み直します。 + + + + Information - + 情報 - - A card update is already ongoing. - + + Troubleshooting + トラブルシューティング - - Unable to run the card updater: - + + F.A.Q. + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + このアカウントはまだ有効化されていません。 +登録されたアドレスに送信されたアクティベーショントークンを入力して下さい。 + + + failed to start. - + 開始失敗。 - + crashed. - + クラッシュしました。 - + timed out. - + タイムアウトしました。 - + write error. - + 書き込みに失敗しました。 - + read error. - + 読み込みに失敗しました。 - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + 不明なエラー。 @@ -2116,62 +2585,62 @@ Local version is %1, remote version is %2. 追放領域から - + the bottom card of %1's library %1のライブラリーの一番下のカード - + the bottom card of his library ライブラリーの一番下のカード - + the bottom card of her library ライブラリーの一番下のカード - + from the bottom of %1's library %1のライブラリーの一番下から - + from the bottom of his library ライブラリーの一番下から - + from the bottom of her library ライブラリーの一番下から - + the top card of %1's library %1のライブラリーの一番上のカード - + the top card of his library ライブラリーの一番上のカード - + the top card of her library ライブラリーの一番上のカード - + from the top of %1's library %1のライブラリーの一番上から - + from the top of his library ライブラリーの一番上から - + from the top of her library ライブラリーの一番上から @@ -2263,7 +2732,7 @@ Local version is %1, remote version is %2. %1 plays %2%3. - %1は%2を%3サイドボードに置いた。 + %1は%2を%3プレイした。 @@ -2574,304 +3043,304 @@ Local version is %1, remote version is %2. %1は、%3の上から%2カウンターを%n個取り除いた (現在%4個) 。 - + %1 taps her permanents. female %1は自分のコントロールするパーマネントをタップした。 - + %1 untaps her permanents. female %1は自分のコントロールするパーマネントをアンタップした。 - + %1 taps his permanents. male %1は自分のコントロールするパーマネントをタップした。 - + %1 untaps his permanents. male %1は自分のコントロールするパーマネントをアンタップした。 - + %1 taps %2. female %1は%2をタップした。 - + %1 untaps %2. female %1は%2をアンタップした。 - + %1 taps %2. male %1は%2をタップした。 - + %1 untaps %2. male %1は%2をアンタップした。 - + %1 sets counter %2 to %3 (%4%5). female %1は%2カウンターを%3に設定した (%4%5)。 - + %1 sets counter %2 to %3 (%4%5). male %1は%2カウンターを%3に設定した (%4%5)。 - + %1 sets %2 to not untap normally. female %1は%2をアンタップ・ステップの間にアンタップしないように設定した。 - + %1 sets %2 to not untap normally. male %1は%2をアンタップ・ステップの間にアンタップしないように設定した。 - + %1 sets %2 to untap normally. female %1は%2を通常にアンタップするように設定した。 - + %1 sets %2 to untap normally. male %1は%2を通常にアンタップするように設定した。 - + %1 sets PT of %2 to %3. female %1は%2のP/Tを%3にした。 - + %1 sets PT of %2 to %3. male %1は%2のP/Tを%3にした。 - + %1 sets annotation of %2 to %3. female %1は%2に注釈をつけた (%3)。 - + %1 sets annotation of %2 to %3. male %1は%2に注釈をつけた (%3)。 + + + %1 is looking at %2. + female + %1は%2を見ている。 + %1 is looking at %2. - female + male %1は%2を見ている。 + + %1 is looking at the top %n card(s) %2. + female + %1は%2上から%n枚のカードを見ている。 + + + %1 is looking at the top %n card(s) %2. + male + %1は%2上から%n枚のカードを見ている。 + - - %1 is looking at %2. - male - %1は%2を見ている。 - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1は%2上から%n枚のカードを見ている。 - - - %1 is looking at the top %n card(s) %2. - male - %1は%2上から%n枚のカードを見ている。 + %1は%2を見るのをやめた。 %1 stops looking at %2. - female - %1は%2を見るのをやめた。 - - - - %1 stops looking at %2. male %1は%2を見るのをやめた。 - + %1 reveals %2 to %3. p1 female, p2 female %1は%3に%2を見せた。 - + %1 reveals %2 to %3. p1 female, p2 male %1は%3に%2を見せた。 - + %1 reveals %2 to %3. p1 male, p2 female %1は%3に%2を見せた。 - + %1 reveals %2 to %3. p1 male, p2 male %1は%3に%2を見せた。 + + + %1 reveals %2. + female + %1は%2を公開した。 + %1 reveals %2. - female - %1は%2を公開した。 - - - - %1 reveals %2. male %1は%2を公開した。 - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1は%3%2を無作為に選び、%4に見せた。 - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1は%3%2を無作為に選び、%4に見せた。 - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1は%3%2を無作為に選び、%4に見せた。 - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1は%3%2を無作為に選び、%4に見せた。 - + %1 randomly reveals %2%3. female %1は%3%2を無作為に選び、公開した。 - + %1 randomly reveals %2%3. male %1は%3%2を無作為に選び、公開した。 - + %1 peeks at face down card #%2. female %1は裏向きのカード#%2の表面を確認した。 - + %1 peeks at face down card #%2. male %1は裏向きのカード#%2の表面を確認した。 - + %1 peeks at face down card #%2: %3. female %1は裏向きのカード#%2の表面を確認した (%3) 。 - + %1 peeks at face down card #%2: %3. male %1は裏向きのカード#%2の表面を確認した (%3) 。 - + %1 reveals %2%3 to %4. p1 female, p2 female %1は%3%2を%4に見せた。 - + %1 reveals %2%3 to %4. p1 female, p2 male %1は%3%2を%4に見せた。 - + %1 reveals %2%3 to %4. p1 male, p2 female %1は%3%2を%4に見せた。 - + %1 reveals %2%3 to %4. p1 male, p2 male %1は%3%2を%4に見せた。 - + %1 reveals %2%3. female %1は%3%2を公開した - + %1 reveals %2%3. male %1は%3%2を公開した - + %1 is now keeping the top card %2 revealed. %1は、%2の一番上のカードを公開した状態でゲームをプレイしている。 - + %1 is not revealing the top card %2 any longer. %1は、%2の一番上のカードの公開を終えた。 - + It is now %1's turn. female ■%1のターン■ - + It is now %1's turn. male ■%1のターン■ - + a card カード @@ -2934,12 +3403,12 @@ Local version is %1, remote version is %2. - + ending phase 最終フェイズ - + untap step アンタップ・ステップ @@ -3049,64 +3518,64 @@ Local version is %1, remote version is %2. %1は、%4の上に%3カウンターを%2個取り除いた (計%5個) 。 - + %1 is looking at the top %2 card(s) %3. female %1は%3上から%2枚のカードを見ている。 - + %1 is looking at the top %2 card(s) %3. male %1は%3上から%2枚のカードを見ている。 - + upkeep step アップキープ・ステップ - + draw step ドロー・ステップ - + first main phase 戦闘前メイン・フェイズ - + beginning of combat step 戦闘開始ステップ - + declare attackers step 攻撃クリーチャー指定ステップ - + declare blockers step ブロック・クリーチャー指定ステップ - + combat damage step 戦闘ダメージ・ステップ - + end of combat step 戦闘終了ステップ - + second main phase 戦闘後メイン・フェイズ - + It is now the %1. %1 @@ -3114,62 +3583,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings チャット設定 - + + Custom alert words + 反応するキーワード + + + Enable chat mentions メンション機能を有効にする - + + Enable mention completer + + + + In-game message macros 定型文 - - Ignore unregistered users in main chat - 未登録ユーザーのメインチャットを無視 - - - - Ignore chat room messages sent by unregistered users. + + Ignore chat room messages sent by unregistered users 未登録ユーザーのルームメッセージを無視 - - Ignore private messages sent by unregistered users. + + Ignore private messages sent by unregistered users 未登録ユーザーの個人チャットを無視 - + + Enable desktop notifications for private messages + 個人チャットのデスクトップ通知を有効にする + + + + Separate words with a space, alphanumeric characters only + 英数字のみ使用可能。単語を半角スペースで区切って下さい。 + + + + Invert text color 反転テキストの色 - - Enable desktop notifications for private messages. - 個人チャットのデスクトップ通知を有効にする - - - + Enable desktop notification for mentions. メンションのデスクトップ通知を有効にする - + + (Color is hexadecimal) (色は16進数) - + Add message メッセージを追加する - + Message: メッセージ: @@ -3235,336 +3716,337 @@ Local version is %1, remote version is %2. Player - + &View library ライブラリーを見る - + Move top cards to &graveyard... 一番上からX枚墓地へ置く - + View &top cards of library... 上からX枚見る - + &View graveyard 墓地を見る - + &View sideboard サイドボードを見る - + Player "%1" プレイヤー "%1" - - - + + + + &Hand 手札 - + &Reveal hand to... 手札を見せる... - + Reveal r&andom card to... 無作為に1枚選んで公開する - + &Library ライブラリー - - - + + + &Graveyard 墓地 - + &Sideboard サイドボード - + Red - + Yellow - + Green - + View top cards of library 上からX枚見る - + Number of cards: カードの枚数: - + &Draw card カードを引く - + Reveal top cards of library - + ライブラリーの一番上のカードを公開する - + Number of cards: (max. %1) - + カード枚数:(最大%1) - + &View exile 追放領域を見る - - - + + + &Exile 追放領域 - + Reveal t&op cards to... - + ライブラリーの一番上のカードを見せる... - + D&raw cards... カードをX枚引く - + Take &mulligan マリガンを行う - + &Shuffle ライブラリーを切り直す - + &Counters カウンター - + &Untap all permanents 全てのパーマネントをアンタップする - + R&oll die... X面ダイスをふる - + &Create token... トークンを作成する... - + C&reate another token 同じトークンを作成する - + S&ay 定型文 - + &Move hand to... 手札を移動させる... - - - - + + + + &Top of library ライブラリーの一番上 - - - - + + + + &Bottom of library ライブラリーの一番下 - + &Move graveyard to... 墓地を移動させる... - + &Move exile to... 追放領域を移動させる... - + Reveal &library to... ライブラリーを公開する... - + &Always reveal top card 一番上のカードを公開したままプレイ - + O&pen deck in deck editor デッキエディターで開く - + &Undo last draw 最後に引いたカードを戻す - + Play top card &face down 一番上のカードを伏せたままプレイ - + Move top cards to &exile... ライブラリーの一番上からX枚追放する - + Put top card on &bottom 一番上のカードを一番下に置く - + Put bottom card &in graveyard 一番下のカードを墓地に置く - + Cr&eate predefined token 定義済みのトークンを作成する - + C&ard カード - + &All players 全てのプレイヤー - + &Play プレイ - + &Hide 隠す - + Play &Face Down 裏向きにプレイ - + &Tap タップ - + &Untap アンタップ - + Toggle &normal untapping 通常のアンタップの切り替え - + &Flip 反転 - + &Peek at card face 表面を見る - + &Clone 複製 - + Attac&h to card... カードにつける... - + Unattac&h はずす - + &Draw arrow... 対象を指定 - + &Increase power パワーを上げる - + &Decrease power パワーを下げる - + I&ncrease toughness タフネスを上げる - + D&ecrease toughness タフネスを下げる @@ -3574,22 +4056,22 @@ Local version is %1, remote version is %2. P/Tを上げる - + Dec&rease power and toughness P/Tを下げる - + Set &power and toughness... P/Tを設定する... - + &Set annotation... 注釈をつける... - + &Add counter (%1) %1色のカウンターを置く @@ -3599,103 +4081,108 @@ Local version is %1, remote version is %2. %1色のカウンターを取り除く - + &Set counters (%1)... %1色のカウンターの数を設定 - + Draw cards カードを引く - - - - + + + + Number: 数: - + Move top cards to grave 上からX枚墓地へ置く - + Move top cards to exile 上からX枚追放する - + Roll die ダイスをふる - + Number of sides: 面の数: - + Set power/toughness P/Tを設定する - + Please enter the new PT: 新しいパワー/タフネス: - + Set annotation 注釈をつける - + Please enter the new annotation: 新しい注釈: - + Set counters カウンターを設定する + + + Cr&eate related card + 関連カードを作成 + QMenuBar - + Services サービス - + Hide %1 %1を隠す - + Hide Others 他を隠す - + Show All すべて表示 - + Preferences... 設定... - + Quit %1 %1から出る - + About %1 %1について @@ -3703,7 +4190,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) Cockatriceリプレイファイル (*.cor) @@ -3793,14 +4280,43 @@ Local version is %1, remote version is %2. + Permissions + アクセス権限 + + + Players 人数 - + Games ゲーム + + + + Error + エラー + + + + You do not have the proper permission to join this room. + あなたはこのルームに参加するための権限がありません。 + + + + Failed to join the room due to an unknown error. + 原因不明のエラーによってルームに参加できませんでした。 + + + + SequenceEdit + + + Shortcut already in use + ショートカットは既に使われています + SetsModel @@ -3843,7 +4359,7 @@ Local version is %1, remote version is %2. シャットダウンまでの時間(分): - + Shut down server サーバーシャットダウン @@ -3851,80 +4367,85 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + パスを選択 - + Enable &sounds - + サウンドを有効 - + Path to sounds directory: - + サウンドフォルダヘのパス - + Test system sound engine - + サウンドテスト - + Sound settings - + サウンド設定 - + Master volume requires QT5 - + マスター音量はQT5が必要 - + Master volume - + 音量 TabAdmin - + Update server &message サーバー更新&メッセージ - + &Shut down server サーバーシャットダウン - + + &Reload configuration + 設定を再読み込み + + + Server administration functions サーバー管理機能 - + &Unlock functions 機能をアンロックする - + &Lock functions 機能をロックする - + Unlock administration functions 管理機能をアンロックする - + Do you really want to unlock the administration functions? 本当に管理機能をアンロックしますか? - + Administration 管理 @@ -3932,181 +4453,223 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... デッキを印刷... - + &Close 閉じる - + &Edit sets... セットの設定... - - &Clear search - 検索を解除 + + Filters + フィルタ - + + &Clear all filters + 全フィルタ解除 + + + + Delete selected + 選択を削除 + + + Deck &name: デッキ名: - + &Comments: コメント: - + Hash: ハッシュ: - + &New deck 新しいデッキ - + &Load deck... デッキを開く... - + &Save deck デッキを保存 - + Save deck &as... 名前を付けて保存... - + Load deck from cl&ipboard... クリップボードからデッキを開く - + Save deck to clip&board クリップボードにデッキをコピー - + &Analyze deck on deckstats.net deckstats.net でデッキを分析 - + Open custom image folder カスタム画像フォルダを開く - + + Open custom sets folder + カスタムセットフォルダを開く + + + Add card to &maindeck カードをメインデッキに追加 - + Add card to &sideboard カードをサイドボードに追加 - + &Deck Editor デッキエディター - + C&ard Database カードデータベース - + + Show/Hide card information + カード情報を表示/非表示 + + + + Show/Hide deck + デッキを表示/非表示 + + + + Show/Hide filters + フィルタを表示/非表示 + + + + Reset layout + レイアウトをリセット + + + + Card Info + カード情報 + + + + Deck + デッキ + + + Welcome - + ようこそ - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. - +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + こんにちは! 現行バージョンのCockatriceの最初の起動です。 +カードデータベースのすべてのセットが有効化されています。 +「セットの設定」ウィンドウ内の特定のセットを無効にしたり、順序を変更をする方法の詳細をお読みください。 - - Show card text only - カードをルール文章で表示 - - - + &Remove row カードを取り除く - + &Increment number 枚数を増やす - + &Decrement number 枚数を減らす - + Edit &tokens... トークンの設定... - + Deck: %1 デッキ: %1 - + Are you sure? 確認 - + The decklist has been modified. Do you want to save the changes? デッキリストが変更されています。 変更を保存しますか? - + Load deck デッキを開く - - - + + + Error エラー - + The deck could not be saved. デッキが保存できませんでした。 - - + + The deck could not be saved. Please check that the directory is writable and try again. デッキは保存できませんでした。 ディレクトリが書き込み可能であることを確認してから、もう一度やり直してください。 - + Save deck デッキを保存 @@ -4204,94 +4767,99 @@ Please enter a name: TabGame - + &Phases フェイズ - + &Game ゲーム - + Next &phase 次のフェイズ - + Next &turn 次のターン - + &Remove all local arrows 全ての対象指定を消す - + + Rotate View Cl&ockwise + 席順を時計回りに送る + + + + Rotate View Co&unterclockwise + 席順を反時計回りに送る + + + Game &information ゲーム情報 - + &Concede 投了する - + &Leave game ゲームから離脱する - + C&lose replay リプレイを閉じる - + &Say: 発言欄: - + Concede 投了する - + Are you sure you want to concede this game? 本当にこのゲームに投了しますか? - + Leave game ゲームから離脱する - + Are you sure you want to leave this game? 本当にこのゲームから離脱しますか? - + You are flooding the game. Please wait a couple of seconds. あなたはゲームから弾かれました。少々お待ちください. - + You have been kicked out of the game. ゲームからキックされました。 - - Replay %1: %2 - リプレイ %1: %2 - - - - Game %1: %2 - ゲーム %1: %2 + + REPLAY + リプレイ @@ -4335,54 +4903,54 @@ Please enter a name: TabReplays - + Local file system ローカルのリプレイ - + Server replay storage サーバーに保存されたリプレイ - + Watch replay リプレイを再生 - - + + Delete 削除 - + Download replay リプレイをダウンロード - + Toggle expiration lock 有効期限ロックの切り替え - + Delete local file ローカルファイルの削除 - + Are you sure you want to delete "%1"? 本当に "%1" を削除してもよろしいですか? - + Delete remote replay サーバーファイルの削除 - + Are you sure you want to delete the replay of game %1? 本当にゲーム:%1のリプレイを削除してもよろしいですか? @@ -4395,47 +4963,47 @@ Please enter a name: TabRoom - + &Say: 発言欄: - + Chat チャット - + &Room ルーム - + &Leave room ルームから出る - + &Clear chat チャットをクリア - + Chat Settings... チャット設定... - + mentioned you. あなたにメンションした。 - + Click to view クリックで見る - + You are flooding the chat. Please wait a couple of seconds. あなたはチャットルームから弾かれました。少々お待ちください。 @@ -4451,15 +5019,25 @@ Please enter a name: TabSupervisor - + Are you sure? よろしいですか? - + There are still open games. Are you sure you want to quit? ゲームがまだ開いています。本当に退出しますか? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4482,169 +5060,301 @@ Please enter a name: UserContextMenu - + User &details ユーザー詳細 - + Private &chat プライベートチャット - + Show this user's &games このユーザーのゲームを表示 - + Add to &buddy list フレンドリストに追加 - + Remove from &buddy list フレンドリストから削除 - + Add to &ignore list 無視リストに追加 - + Remove from &ignore list 無視リストから削除 - + Kick from &game ゲームからキックする - + Ban from &server サーバーからBANする - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games %1のゲーム + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information ユーザー情報 - + Real name: 本名: - - Gender: - 性別: + + Pronouns: + 三人称代名詞の扱い: - + Location: 現在地: - + User level: ユーザー種別: - + Account Age: アカウント年齢: - + + Edit + 編集 + + + + Change password + パスワード変更 + + + + Change avatar + アバター変更 + + + Administrator 管理者 - + Moderator モデレーター - + Registered user 登録ユーザー - - + + Unregistered user 未登録ユーザー - + Unknown 不明 - + Year - + Years - + Day - + Days + + + + + Information + 情報 + + + + User information updated. + ユーザー情報が更新されました。 + + + + + + + + + + + Error + エラー + + + + This server does not permit you to update your user informations. + このサーバーはユーザーの情報を更新することができません。 + + + + + An error occured while trying to update your user informations. + ユーザー情報の更新中にエラーが発生しました。 + + + + Password changed. + パスワードが変更されました。 + + + + This server does not permit you to change your password. + このサーバーはパスワードを変更することができません。 + + + + The new password is too short. + 新しいパスワードが短すぎます。 + + + + The old password is incorrect. + 前のパスワードが一致しませんでした。 + + + + Avatar updated. + アバターを更新しました。 + + + + This server does not permit you to update your avatar. + このサーバーはアバターを更新することができません。 + + + + An error occured while trying to updater your avatar. + アバターの更新中にエラーが発生しました。 + UserInterfaceSettingsPage - + General interface settings インターフェース全般設定 - + Enable notifications in taskbar タスクバーの通知を有効 - + Notify in the taskbar for game events while you are spectating タスクバーに観戦中のゲームイベントを通知する - + &Double-click cards to play them (instead of single-click) ダブルクリックでカードをプレイする (シングルクリックの代わり) - + &Play all nonlands onto the stack (not the battlefield) by default 土地でないカードをプレイする時、すぐに戦場に出さずにスタックに置く - + + Annotate card text on tokens + トークンのカードテキストを注釈にもつける + + + Animation settings アニメーション設定 - + &Tap/untap animation タップ/アンタップアニメーション @@ -4652,22 +5362,22 @@ Please enter a name: UserList - + Users connected to server: %1 ユーザーが%1サーバーに接続 - + Users in this room: %1 ルームのユーザー数: %1 - + Buddies online: %1 / %2 フレンドオンライン: %1 / %2 - + Ignored users online: %1 / %2 無視ユーザーオンライン: %1 / %2 @@ -4689,28 +5399,45 @@ Please enter a name: Move selected set up 選択中のセットを上へ + + + Move selected set to the top + 選択したセットを一番上へ + Move selected set down 選択中のセットを下へ - - - Move selected set to top - 選択中のセットを先頭へ - - Move selected set to bottom - 選択中のセットを最後尾へ + Move selected set to the bottom + 選択したセットを一番下へ - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. - + hints: + ヒント: + + + + Enable the sets that you want to have available in the deck editor + デッキエディタで使用したいセットチェックを入れて下さい。 + + + + Move sets around to change their order, or click on a column header to sort sets on that field + セットの並び順を変更するには、並べ替えたいセットを選択して緑の矢印をクリックするか、列のヘッダーをクリックして下さい。 + + + + Sets order decides the source that will be used when loading images for a specific card + カード画像は、セットの並び順上が優先されて表示されます。 + + + + Disabled sets will be used for loading images only if all the enabled sets failed + すべての有効なセットが失敗した場合にのみ、無効のセットも画像の読み込みに使用されます。 @@ -4718,12 +5445,12 @@ Disabled sets will still be used for loading images. セットの設定 - + Success 完了 - + The sets database has been saved successfully. セットデータベースを保存しました。 @@ -4751,4 +5478,561 @@ Disabled sets will still be used for loading images. タイプ別に重ねて表示 + + shortcutsTab + + + Form + フォーム + + + + Main Window + メインウィンドウ + + + + Deck editor + デッキエディター + + + + Local gameplay + ローカルゲーム + + + + Watch replay + リプレイを再生 + + + + Connect + 接続 + + + + Register + 登録 + + + + Full screen + フルスクリーン + + + + Settings + 設定 + + + + Check for card updates + カードデータベース更新 + + + + Exit + 終了 + + + + Deck Editor + デッキエディター + + + + Analyze deck + デッキ分析 + + + + Load deck (clipboard) + クリップボードからデッキを開く + + + + Clerar all filters + 全フィルタ解除 + + + + New deck + 新しいデッキ + + + + Clear one filter + フィルタをひとつ解除 + + + + Open custom folder + カスタムフォルダを開く + + + + Close + 閉じる + + + + Print deck + デッキを印刷 + + + + Edit sets + セットの設定 + + + + Delete card + カードを削除 + + + + Edit tokens + トークンを編集 + + + + Reset layout + レイアウトをリセット + + + + Add card + カードを追加 + + + + Save deck + デッキを保存 + + + + Remove card + カードを取り除く + + + + Save deck as + 名前を付けて保存... + + + + Load deck + デッキを開く + + + + Save deck (clipboard) + クリップボードにデッキをコピー + + + + + Counters + カウンター + + + + Life + ライフ + + + + + + + + Set + 設定 + + + + + + + Add + 追加 + + + + + + + Remove + 削減 + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + メインウィンドウ/デッキエディター + + + + Power / toughness + パワー / タフネス + + + + Power and toughness + パワーとタフネス + + + + Add (+1/+1) + +1/+1 + + + + Remove (-1/-1) + -1/-1 + + + + Toughness + タフネス + + + + Remove (-0/-1) + -0/-1 + + + + Add (+0/+1) + +0/+1 + + + + Power + パワー + + + + Remove (-1/-0) + -1/-0 + + + + Add (+1/+0) + +1/+0 + + + + Game Phases + フェイズ + + + + Untap + アンタップ + + + + Disconnect + + + + + Upkeep + + + + + + Draw + ドロー + + + + Main 1 + メイン1 + + + + Start combat + 戦闘開始 + + + + Attack + 攻撃 + + + + Block + ブロック + + + + Damage + ダメージ + + + + End combat + 戦闘終了 + + + + Main 2 + メイン2 + + + + End + エンド + + + + Next phase + 次のフェイズ + + + + Next turn + 次のターン + + + + Player + プレイヤー + + + + Tap Card + タップ + + + + Untap Card + アンタップ + + + + Untap all + すべてアンタップ + + + + Toogle untap + 通常のアンタップの切り替え + + + + Flip card + 裏返す + + + + Peek card + 表面を見る + + + + Play card + プレイする + + + + Attach card + つける + + + + Unattach card + はずす + + + + Clone card + カードのコピー + + + + Create token + トークンを作成 + + + + Create another token + 同じトークンを作成する + + + + Set annotation + 注釈 + + + + Phases / P/T / Player + フェイズ/ P/T /プレイヤー + + + + Move card to + カードを移動 + + + + Bottom library + ライブラリーの一番下 + + + + Top library + ライブラリーの一番上 + + + + + Graveyard + 墓地 + + + + + Exile + 追放 + + + + Hand + 手札 + + + + View + 見る + + + + Library + ライブラリー + + + + Tops card of library + ライブラリーの一番上 + + + + Sideboard + サイドボード + + + + Close recent view + 閉じる + + + + Pre-play + + + + + Load remote deck + サーバーのデッキを開く + + + + Load local deck + ローカルのデッキを開く + + + + Game play + + + + + Draw arrow + 対象を指定 + + + + Leave game + ゲームから離脱する + + + + Remove local arrows + 全ての対象指定を消す + + + + Concede + 投了 + + + + Roll dice + ダイス + + + + Rotate view CW + 席順を時計回りに送る + + + + Shuffle library + ライブラリーを切り直す + + + + Rotate view CCW + 席順を時計回りに送る + + + + Mulligan + マリガン + + + + Draw card + カードを引く + + + + Draw cards + 複数枚引く + + + + Undo draw + 最後に引いたカードを戻す + + + + Always reveal top card + 一番上のカードを公開したままプレイ + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_ko.ts b/cockatrice/translations/cockatrice_ko.ts index 766f2d10..c8a8f2c3 100644 --- a/cockatrice/translations/cockatrice_ko.ts +++ b/cockatrice/translations/cockatrice_ko.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... 카운터 설정... - + Set counter 카운터 설정 - + New value for counter '%1': 카운터 '%1'의 값을 지정해 주세요: @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures 영역별 배경 이미지 - + Hand background: 손 배경 이미지: - + Stack background: 스택 배경 이미지: - + Table background: 전장 배경 이미지: - + Player info background: 플레이어 정보 배경 이미지: - + Card back: 카드 뒷면 이미지: - + Card rendering 카드 렌더링 - + Display card names on cards having a picture 이미지가 존재하는 카드에도 카드 이름 표시 - + Scale cards on mouse over 마우스 오버 시 카드 확대 - + Hand layout 손 레이아웃 - + Display hand horizontally (wastes space) 손의 카드를 가로로 정렬 - + Enable left justification 손의 카드를 좌측으로 정렬 - + Table grid layout 테이블 격자 레이아웃 - + Invert vertical coordinate 전장의 상하배치를 전환 (대지를 앞에 배치) - + Minimum player count for multi-column layout: 다열 레이아웃를 위한 최소 플레이어 인원 (4명 이상 권장) - - - - - + + + + + Choose path 경로 선택 @@ -107,99 +107,105 @@ BanDialog - + ban &user name 사용자 계정 추방 - + ban &IP address IP 주소 추방 - + + ban client I&D + 클라이언트 ID 추방 + + + Ban type 추방 유형 - + &permanent ban 영구 추방 - + &temporary ban 기간제 추방 - + &Days: 일: - + &Hours: 시간: - + &Minutes: 분: - + Duration of the ban 추방 기간 - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. 추방 사유를 적어주세요. 해당 사유는 관리자들만 볼 수 있고 추방 당한 사용자는 볼 수 없습니다. - + Please enter the reason for the ban that will be visible to the banned person. 추방된 사용자가 보게 될 추방 사유를 적어주세요. - + &OK 확인 - + &Cancel 취소 - + Ban user from server 사용자를 서버에서 추방 - + Error 오류 - - You have to select a name-based or IP-based ban, or both. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. 추방의 유형을 선택해 주세요. CardDatabase - + New sets found - + 새로운 확장판 감지 - + %1 new set(s) have been found in the card database. Do you want to enable them? - + %1개의 새로운 확장판이 카드 데이터베이스에 추가되었습니다. +새로운 확장판을 활성화 시키시겠습니까? @@ -230,30 +236,53 @@ This is only saved for moderators and cannot be seen by the banned person.공/방 + + CardFrame + + + Image + 이미지 + + + + Description + 텍스트 + + + + Both + 모두 보기 + + CardInfoText - + Name: 카드 이름: - + Mana cost: 마나 비용: - + + Color(s): + 카드 색: + + + Card type: 카드 유형: - + P / T: 공/방: - + Loyalty: 충성도: @@ -276,27 +305,32 @@ This is only saved for moderators and cannot be seen by the banned person.정보 전체 표시 - + Name: 카드 이름: - + Mana cost: 마나 비용: - + + Color(s): + 카드 색: + + + Card type: 카드 유형: - + P / T: 공/방: - + Loyalty: 충성도: @@ -560,12 +594,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - + Nothing is here... yet 준비중입니다. - + General 일반 @@ -573,17 +607,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Number 카드 장수 - + Card 카드 이름 - + Price 가격 @@ -605,42 +639,42 @@ This is only saved for moderators and cannot be seen by the banned person. DeckViewContainer - - Load &local deck + + Load local deck 로컬 덱 불러오기 - - Load d&eck from server + + Load deck from server 서버에 저장된 덱 불러오기 - + Ready to s&tart 시작 준비 - + S&ideboard unlocked - 사이드보드 교체 활성화 - - - - S&ideboard locked - 사이드보드 교체 비활성화 + 사이드보드 카드 교체 가능 + S&ideboard locked + 사이드보드 카드 교체 불가능 + + + Load deck 덱 불러오기 - + Error 오류 - + The selected file could not be loaded. 지정하신 파일을 불러올 수 없었습니다. @@ -665,7 +699,7 @@ This is only saved for moderators and cannot be seen by the banned person. Color (OR): - 카드 색깔(OR): + 카드 색(OR): @@ -686,37 +720,52 @@ This is only saved for moderators and cannot be seen by the banned person. DlgConnect - + + Previous Host + 지난번에 접속한 서버 + + + + New Host + 새로운 서버 + + + &Host: 호스트 IP: - + + Enter host name + 서버 주소 입력 + + + &Port: 포트: - + Player &name: 사용자명: - + P&assword: 비밀번호: - + &Save password 비밀번호 저장 - + A&uto connect at start 코카트리스 시작 시 자동 연결 - + Connect to server 서버로 연결 @@ -724,82 +773,92 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateGame - + &Description: 게임 이름: - + &Password: 비밀번호: - + P&layers: 게임 참가 인원: - + + Re&member settings + + + + Game type 게임 종류 - + Only &buddies can join 친구만 입장 가능 - + Only &registered users can join 서버에 가입한 사용자만 입장 가능 - + Joining restrictions 입장 제한 - + &Spectators can watch 관전자 허용 - + Spectators &need a password to watch 관전자 입장 시 비밀번호 필요 - + Spectators can see &hands 관전자에게 플레이어의 손 공개 - + Spectators can &chat 관전자 대화 가능 - + Spectators 관전자 - + + &Clear + 설정 초기화 + + + Create game 게임 만들기 - + Game information 게임 정보 - + Error 오류 - + Server error. 서버 오류. @@ -807,96 +866,170 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateToken - + &Name: 토큰 이름: - + Token 토큰 - + C&olor: - 토큰 색깔: + 토큰 색: - + white 백색 - + blue 청색 - + black 흑색 - + red 적색 - + green 녹색 - + multicolor 다색 - + colorless 무색 - + &P/T: 공/방 - + &Annotation: 주석: - + &Destroy token when it leaves the table 토큰이 전장을 떠나면 게임에서 제거됨 - + Token data 토큰 정보 - + Show &all tokens 모든 토큰 보기 - + Show tokens from this &deck 이 덱에서 사용하는 토큰 보기 - + Choose token from list 목록에서 토큰 선택 - + Create token 토큰 생성 + + DlgEditAvatar + + + + No image chosen. + 이미지 없음 + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + 아바타를 변경하려면 새로운 이미지 파일을 선택하여 주십시오. +현재 아바타를 삭제하시려면 이미지를 선택하지 않고 확인을 하시면 됩니다. + + + + Browse... + 찾아보기 + + + + Change avatar + 아바타 변경 + + + + Open Image + 이미지 불러오기 + + + + Image Files (*.png *.jpg *.bmp) + 이미지 파일 (*.png *.jpg *.bmp) + + + + Invalid image chosen. + 잘못된 이미지를 선택하셨습니다. + + + + DlgEditPassword + + + Old password: + 예전 비밀번호: + + + + New password: + 새로운 비밀번호: + + + + Confirm new password: + 새로운 비밀번호 확인: + + + + Change password + 비밀번호 변경 + + + + Error + 오류 + + + + The new passwords don't match. + 새로운 비밀번호가 일치하지 않습니다. + + DlgEditTokens @@ -907,7 +1040,7 @@ This is only saved for moderators and cannot be seen by the banned person. C&olor: - 토큰 색깔: + 토큰 색: @@ -989,7 +1122,56 @@ This is only saved for moderators and cannot be seen by the banned person. The chosen name conflicts with an existing card or token. Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. - + 다른 토큰이나 카드와 중복되는 이름입니다. +'확장판 편집' 창에서 '토큰 전용 더미 판본'이 체크되어있는지 확인하여 주십시오. + + + + DlgEditUser + + + Email: + 이메일: + + + + Pronouns: + 성별: + + + + Neutral + 중립 + + + + Masculine + 남성 + + + + Feminine + 여성 + + + + Country: + 국가: + + + + Undefined + 미지정 + + + + Real name: + 본명: + + + + Edit user profile + 사용자 프로필 변경 @@ -1043,7 +1225,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh 새로고침 @@ -1053,12 +1235,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di 클립보드에서 덱 가져오기 - + + Error 오류 - + + Invalid deck list. 클립보드의 덱 리스트를 읽어올 수 없습니다. @@ -1071,22 +1255,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di 덱 불러오기 + + DlgRegister + + + &Host: + 호스트 IP: + + + + &Port: + 포트: + + + + Player &name: + 사용자명: + + + + P&assword: + 비밀번호: + + + + Password (again): + 비밀번호 확인: + + + + Email: + 이메일: + + + + Email (again): + 이메일 확인: + + + + Pronouns: + 성별: + + + + Neutral + 중립 + + + + Masculine + 남성 + + + + Feminine + 여성 + + + + Undefined + 미지정 + + + + + Registration Warning + 가입 실패 + + + + Your passwords do not match, please try again. + 입력된 비밀번호가 서로 다릅니다. 다시 시도하여 주십시오. + + + + Your email addresses do not match, please try again. + 입력된 이메일이 서로 다릅니다. 다시 시도하여 주십시오. + + + + Country: + 국가: + + + + Real name: + 실명: + + + + Register to server + 서버에 회원 가입 + + DlgSettings - - - + + + Error 오류 - + Unknown Error loading card database 데이터베이스를 불러올 때 미상의 오류가 발생하였습니다. - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1381,7 @@ Would you like to change your database location setting? 데이터베이스 경로를 다시 설정하시겠습니까? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1398,7 @@ Would you like to change your database location setting? 데이터베이스 경로를 다시 설정하시겠습니까? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1407,7 @@ Would you like to change your database location setting? 데이터베이스 경로를 다시 설정하시겠습니까? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1416,7 @@ Would you like to change your database location setting? 데이터베이스 경로를 다시 설정하시겠습니까? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1151,7 +1429,7 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 데이터베이스 경로를 다시 설정하시겠습니까? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1164,65 +1442,60 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 데이터베이스 경로를 다시 설정하시겠습니까? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? 덱 파일을 보관하는 디렉토리의 경로가 잘못되었습니다. 경로를 다시 설정하시겠습니까? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? 카드 이미지 파일을 보관하는 디렉토리의 경로가 잘못되었습니다. 경로를 다시 설정하시겠습니까? - + Settings 환경설정 - + General 일반 - + Appearance 외형 - + User Interface 인터페이스 - + Deck Editor 덱 편집기 - + Chat 대화 - + Sound - + 소리 + + + + Shortcuts + 단축키 GameSelector - - - C&reate - 게임 생성 - - - - &Join - 게임 참가 - @@ -1232,7 +1505,7 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 - + Error 오류 @@ -1277,37 +1550,47 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 방장이 당신을 차단하였습니다. - + Join game 게임 참가 - + Password: 비밀번호: - + Please join the respective room first. 해당되는 게임에 먼저 들어가시기 바랍니다. - + Games 게임 목록 - + &Filter games - 게임 목록 필터 + 게임 필터 - + C&lear filter - 필터 초기화 + 필터 해제 - + + C&reate + 게임 생성 + + + + &Join + 게임 참가 + + + J&oin as spectator 관전자로 참가하기 @@ -1372,7 +1655,7 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 reg. users only - 가입한 사용자만 + 가입한 사용자 전용 @@ -1424,97 +1707,107 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 GeneralSettingsPage - + Reset/Clear Downloaded Pictures 저장된 이미지 초기화 - - - - - + + + + + Choose path 경로 선택 - + Success 성공 - + Downloaded card pictures have been reset. 저장된 카드 이미지 파일들이 초기화 되었습니다. - + Error 오류 - + One or more downloaded card pictures could not be cleared. 저장된 카드 이미지 파일을 초기화 시킬 수 없었습니다. - + Personal settings 개인 설정 - + Language: 언어: - + Download card pictures on the fly 카드 이미지 파일 자동 다운로드 - - Download high-quality card pictures - 고화질 카드 이미지 사용 + + Download card pictures from a custom URL + 지정된 URL에서 카드 이미지 다운로드 - + + Custom Card Download URL: + 카드 이미지 다운로드 URL: + + + + Linking FAQ + 카드 이미지 서버 설정 FAQ + + + Paths 디렉토리 경로 - + Decks directory: 덱 파일 경로: - + Replays directory: 리플레이 파일 경로: - + Pictures directory: 카드 이미지 경로: - + Card database: 카드 데이터베이스 경로: - + Token database: 토큰 데이터베이스 경로: - + Picture cache size: 카드 이미지 캐시 최대 용량: - - + + English 한국어 (Korean) @@ -1522,57 +1815,49 @@ http://github.com/Cockatrice/Cockatrice/issues 에 cards.xml을 첨부한 버그 MainWindow - + There are too many concurrent connections from your address. 해당 IP주소에서 동시에 연결된 회선이 너무 많습니다. - + Scheduled server shutdown. 정기 점검 중입니다. - + Banned by moderator 관리자에 의해 서버에서 추방 당하였습니다. - + Expected end time: %1 예상 추방 종료 시간: %1 - + This ban lasts indefinitely. 영구적 추방입니다. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - 사용할 수 없는 사용자명입니다. -영어 대소문자, 숫자, _(밑줄), .(마침표)나 -(대쉬)만 사용하실 수 있습니다. - - - + Connection closed 서버와의 연결이 끊어졌습니다. - + The server has terminated your connection. Reason: %1 서버에서 연결을 끊었습니다. 사유: %1 - + Scheduled server shutdown 정기 점검 - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1581,152 +1866,318 @@ Reason for shutdown: %1 서버 재시작 사유: %1 - + Number of players 플레이어 인원 - + Please enter the number of players. 최대 플레이어 인원을 입력해 주세요. - - + + Player %1 플레이어 %1 - + Load replay 리플레이 불러오기 - + About Cockatrice 코카트리스에 관하여 - + Version %1 버전 %1 - + Translators: 번역진: - + Project Manager: 현 프로젝트 매니저: - + + The server has reached its maximum user capacity, please check back later. + 서버가 꽉 찼습니다. 나중에 다시 접속하여 주십시오. + + + + + Invalid username. + 잘못된 사용자명입니다. + + + + You have been logged out due to logging in at another location. + + + + + + Success + 성공 + + + + Registration accepted. +Will now login. + 가입이 승인되었습니다. +로그인합니다. + + + + Account activation accepted. +Will now login. + 계정이 활성화 되었습니다. +로그인합니다. + + + Past Project Managers: 전 프로젝트 매니저: - + Developers: 개발진: - + Our Developers 기여자 목록 - + Help Develop! 개발을 도와주세요! - + Recognition Page 번역자 목록 - + Help Translate! 번역을 도와주세요! - + Support: 기술 지원: - + Report an Issue 문제점 보고 - - Suggest an Improvement - 개선사항 건의 - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error 오류 - + Server timeout 서버 응답시간 초과 - + Incorrect username or password. Please check your authentication information and try again. 잘못된 사용자명이나 비밀번호입니다. 확인 후 다시 시도해 주세요. - + There is already an active session using this user name. Please close that session first and re-login. 해당 사용자명으로 연결된 다른 세션이 있습니다. 해당 세션을 종료 한 후에 다시 시도해 주세요. - + + You are banned until %1. %1까지 추방 당하였습니다. - + + You are banned indefinitely. 당신은 무기한 추방 당하였습니다. - - This server requires user registration. - 현재 연결된 서버는 가입이 필요합니다. + + This server requires user registration. Do you want to register now? + 본 서버는 가입이 필요합니다. +지금 가입하시겠습니까? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + 본 서버는 클라이언트 ID가 필요합니다. 현재 사용중인 코카트리스는 올바른 ID를 생성하지 못하거나 수정된 버전일 수 있습니다. + +코카트리스를 다시 실행하여 주시기 바랍니다. + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + 내부 오류가 발생하였습니다. + +코카트리스를 다시 실행하시고 다시 시도하거나 오류가 지속될 경우 코카트리스를 새로 다운받아 주시기 바랍니다. + + + + Account activation + 계정 활성화 + + + Unknown login error: %1 알 수 없는 로그인 오류: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + +대부분 클라이언트가 오래되어서 서버와 클라이언트가 말이 제대로 통하지 않을때 발생하는 문제입니다. + + + + Your username must respect these rules: + 사용자명은 다음 조건을 만족해야 합니다: + + + + is %1 - %2 characters long + %1에서 %2자 사이 + + + + can %1 contain lowercase characters + 소문자 사용 %1가능 + + + + + + + NOT + + + + + can %1 contain uppercase characters + 대문자 사용 %1가능 + + + + can %1 contain numeric characters + 숫자 사용 %1가능 + + + + can contain the following punctuation: %1 + 다음 문장 부호를 사용 가능 : + + + + first character can %1 be a punctuation mark + 첫 글자를 문장 부호로 입력 %1가능 + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + 사용자명에는 영어 대소문자, 숫자, _(밑줄), .(마침표)나 -(대쉬)만 사용하실 수 있습니다. + + + + + + + + Registration denied + 가입 실패 + + + + Registration is currently disabled on this server + 본 서버는 현재 가입을 받지 않습니다. + + + + There is already an existing account with the same user name. + 이미 존재하는 사용자명 입니다. + + + + It's mandatory to specify a valid email address when registering. + 사용 가능한 이메일 주소를 작성하셔야 합니다. + + + + Too many registration attempts from your IP address. + 현 IP주소에서 너무 많은 가입 요청이 있었습니다. + + + + Password too short. + 비밀번호가 너무 짧습니다. + + + + Registration failed for a technical problem on the server. + 서버의 기술적 문제로 가입에 실패하였습니다. + + + + Unknown registration error: %1 + 가입 중 알 수 없는 오류 발생: %1 + + + + Account activation failed + 계정 활성화 실패 + + + Socket error: %1 소켓 오류: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. 서버 버전이 클라이언트보다 오래되었습니다. @@ -1734,7 +2185,7 @@ Local version is %1, remote version is %2. 클라이언트 버전 %1, 서버 버전 %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. 코카트리스 클라이언트 버전이 오래되었습니다. @@ -1742,146 +2193,170 @@ Local version is %1, remote version is %2. 클라이언트 버전 %1, 서버 버전 %2. - + Connecting to %1... %1로 연결 시도 중... - + + Registering to %1 as %2... + 서버 %1에 %2(으)로 가입 중... + + + Disconnected 연결 안됨 - + Connected, logging in at %1 연결됨, %1로 로그인 시도 중 - - Logged in as %1 at %2 - %2에 %1(으)로 로그인됨 - - - + &Connect... 서버로 연결 - + &Disconnect 서버와의 연결 해제 - + Start &local game... 오프라인 게임 시작 - + &Watch replay... 리플레이 재생 - + &Deck editor 덱 편집기 - + &Full screen 전체 화면 - + + &Register to server... + 서버에 회원 가입 + + + &Settings... 환경설정 - - + + &Exit 끝내기 - + A&ctions 액션 - + &Cockatrice 코카트리스 - + &About Cockatrice 코카트리스에 관하여 - + &Help 도움말 - - Check card updates... - + + Check for card updates... + 카드 데이터베이스 업데이트 확인 - - + + A card database update is already running. + 이미 카드 데이터베이스 업데이트가 진행 중입니다. + + + + Unable to run the card database updater: + 카드 데이터베이스 업데이트를 진행할 수 없습니다: + + + + The card database updater exited with an error: %1 + 카드 데이터베이스 업데이트 중 다음 오류가 발생하였습니다: +%1 + + + + Update completed successfully. Cockatrice will now reload the card database. + 카드 데이터베이스가 성공적으로 업데이트 되었습니다. +데이터베이스를 다시 불러오겠습니다. + + + + Information - + 알림 - - A card update is already ongoing. - + + Troubleshooting + 문제 해결 - - Unable to run the card updater: - + + F.A.Q. + 자주 묻는 질문 - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + 계정이 아직 활성화 되지 않았습니다. +가입 시 기입한 메일 주소에서 계정 활성화 토큰을 확인하여 주십시오. + + + failed to start. - + 업데이트 시작 실패 - + crashed. - + 강제 종료됨. - + timed out. - + 시간 초과. - + write error. - + 파일 쓰기 오류. - + read error. - + 파일 읽기 오류. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + 알 수 없는 오류. @@ -1999,25 +2474,25 @@ Local version is %1, remote version is %2. %1 has locked her sideboard. female - %1이(가) 사이드보드 교체를 비활성화 하였습니다. + %1이(가) 덱에서 사이드보드 카드를 제외합니다. %1 has locked his sideboard. male - %1이(가) 사이드보드 교체를 비활성화 하였습니다. + %1이(가) 덱에서 사이드보드 카드를 제외합니다. %1 has unlocked her sideboard. female - %1이(가) 사이드보드 교체를 활성화 하였습니다. + %1이(가) 덱에 사이드보드 카드를 넣으려고 합니다. %1 has unlocked his sideboard. male - %1이(가) 사이드보드 교체를 활성화 하였습니다. + %1이(가) 덱에 사이드보드 카드를 넣으려고 합니다. @@ -2121,62 +2596,62 @@ Local version is %1, remote version is %2. 추방 영역에 - + the bottom card of %1's library %1의 서고 맨 밑의 카드 - + the bottom card of his library 그의 서고 맨 밑의 카드 - + the bottom card of her library 그녀의 서고 맨 밑의 카드 - + from the bottom of %1's library %1의 서고 맨 밑에 - + from the bottom of his library 그의 서고 맨 밑에 - + from the bottom of her library 그녀의 서고 맨 밑에 - + the top card of %1's library %1의 서고 맨 위의 카드 - + the top card of his library 그의 서고 맨 위의 카드 - + the top card of her library 그녀의 서고 맨 위의 카드 - + from the top of %1's library %1의 서고 맨 위에 - + from the top of his library 그의 서고 맨 위에 - + from the top of her library 그녀의 서고 맨 위에 @@ -2579,304 +3054,304 @@ Local version is %1, remote version is %2. %1이(가) %n개의 %2 카운터를 %3에서 제거합니다 (현재 %4개). - + %1 taps her permanents. female %1이(가) 그녀의 지속물을 전부 탭합니다. - + %1 untaps her permanents. female %1이(가) 그녀의 지속물을 전부 언탭합니다. - + %1 taps his permanents. male %1이(가) 그의 지속물을 전부 탭합니다. - + %1 untaps his permanents. male %1이(가) 그의 지속물을 전부 언탭합니다. - + %1 taps %2. female %1이(가) %2을(를) 탭합니다. - + %1 untaps %2. female %1이(가) %2을(를) 언탭합니다. - + %1 taps %2. male %1이(가) %2을(를) 탭합니다. - + %1 untaps %2. male %1이(가) %2을(를) 언탭합니다. - + %1 sets counter %2 to %3 (%4%5). female %1이(가) %2 카운터를 %3로(으로) 맞춥니다 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1이(가) %2 카운터를 %3로(으로) 맞춥니다 (%4%5). - + %1 sets %2 to not untap normally. female %1이(가) %2을(를) 언탭단에 언탭하지 않도록 지정합니다. - + %1 sets %2 to not untap normally. male %1이(가) %2을(를) 언탭단에 언탭하지 않도록 지정합니다. - + %1 sets %2 to untap normally. female %1이(가) %2을(를) 언탭단에 언탭하도록 지정합니다. - + %1 sets %2 to untap normally. male %1이(가) %2을(를) 언탭단에 언탭하도록 지정합니다. - + %1 sets PT of %2 to %3. female %1이(가) %2의 공/방을 %3로(으로) 지정합니다. - + %1 sets PT of %2 to %3. male %1이(가) %2의 공/방을 %3로(으로) 지정합니다. - + %1 sets annotation of %2 to %3. female %1이(가) %2의 주석을 %3로(으로) 달았습니다. - + %1 sets annotation of %2 to %3. male %1이(가) %2의 주석을 %3로(으로) 달았습니다. + + + %1 is looking at %2. + female + %1이(가) %2 보고 있습니다. + %1 is looking at %2. - female + male %1이(가) %2 보고 있습니다. + + %1 is looking at the top %n card(s) %2. + female + %1이(가) %2 맨 위 %n장의 카드를 보고 있습니다. + + + %1 is looking at the top %n card(s) %2. + male + %1이(가) %2 맨 위 %n장의 카드를 보고 있습니다. + - - %1 is looking at %2. - male - %1이(가) %2 보고 있습니다. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1이(가) %2 맨 위 %n장의 카드를 보고 있습니다. - - - %1 is looking at the top %n card(s) %2. - male - %1이(가) %2 맨 위 %n장의 카드를 보고 있습니다. + %1이(가) %2 더 이상 보고 있지 않습니다. %1 stops looking at %2. - female - %1이(가) %2 더 이상 보고 있지 않습니다. - - - - %1 stops looking at %2. male %1이(가) %2 더 이상 보고 있지 않습니다. - + %1 reveals %2 to %3. p1 female, p2 female %1이(가) %2을(를) %3에게 공개합니다. - + %1 reveals %2 to %3. p1 female, p2 male %1이(가) %2을(를) %3에게 공개합니다. - + %1 reveals %2 to %3. p1 male, p2 female %1이(가) %2을(를) %3에게 공개합니다. - + %1 reveals %2 to %3. p1 male, p2 male %1이(가) %2을(를) %3에게 공개합니다. + + + %1 reveals %2. + female + %1이(가) %2을(를) 공개합니다. + %1 reveals %2. - female - %1이(가) %2을(를) 공개합니다. - - - - %1 reveals %2. male %1이(가) %2을(를) 공개합니다. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1이(가) 무작위로 %3 있는 %2을(를) %4에게 공개합니다. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1이(가) 무작위로 %3 있는 %2을(를) %4에게 공개합니다. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1이(가) 무작위로 %3 있는 %2을(를) %4에게 공개합니다. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1이(가) 무작위로 %3 있는 %2을(를) %4에게 공개합니다. - + %1 randomly reveals %2%3. female %1이(가) 무작위로 %3 있는 %2을(를) 공개합니다. - + %1 randomly reveals %2%3. male %1이(가) 무작위로 %3 있는 %2을(를) 공개합니다. - + %1 peeks at face down card #%2. female %1이(가) 뒷면으로 놓여있는 카드 #%2의 앞면을 봅니다. - + %1 peeks at face down card #%2. male %1이(가) 뒷면으로 놓여있는 카드 #%2의 앞면을 봅니다. - + %1 peeks at face down card #%2: %3. female %1이(가) 뒷면으로 놓여있는 카드 #%2 (%3)의 앞면을 봅니다. - + %1 peeks at face down card #%2: %3. male %1이(가) 뒷면으로 놓여있는 카드 #%2 (%3)의 앞면을 봅니다. - + %1 reveals %2%3 to %4. p1 female, p2 female %1이(가) %3 있는 %2을(를) %4에게 공개합니다. - + %1 reveals %2%3 to %4. p1 female, p2 male %1이(가) %3 있는 %2을(를) %4에게 공개합니다. - + %1 reveals %2%3 to %4. p1 male, p2 female %1이(가) %3 있는 %2을(를) %4에게 공개합니다. - + %1 reveals %2%3 to %4. p1 male, p2 male %1이(가) %3 있는 %2을(를) %4에게 공개합니다. - + %1 reveals %2%3. female %1이(가) %3 있는 %2을(를) 공개합니다. - + %1 reveals %2%3. male %1이(가) %3 있는 %2을(를) 공개합니다. - + %1 is now keeping the top card %2 revealed. %1이(가) %2 맨 위 카드를 공개합니다. - + %1 is not revealing the top card %2 any longer. %1이(가) %2 맨 위 카드를 더 이상 공개하지 않습니다. - + It is now %1's turn. female 지금부터 %1의 턴입니다. - + It is now %1's turn. male 지금부터 %1의 턴입니다. - + a card 카드 한 장 @@ -2939,12 +3414,12 @@ Local version is %1, remote version is %2. - + ending phase 종료단 - + untap step 언탭단 @@ -3054,64 +3529,64 @@ Local version is %1, remote version is %2. %1이(가) %2개의 %3 카운터를 %4에서 제거합니다 (현재 %5개). - + %1 is looking at the top %2 card(s) %3. female %1이(가) %3 맨 위 %2장의 카드를 보고 있습니다. - + %1 is looking at the top %2 card(s) %3. male %1이(가) %3 맨 위 %2장의 카드를 보고 있습니다. - + upkeep step 유지단 - + draw step 뽑기단 - + first main phase 첫번째 본단계 - + beginning of combat step 전투단 시작 - + declare attackers step 공격자 선언단 - + declare blockers step 방어자 선언단 - + combat damage step 전투 피해단 - + end of combat step 전투 종료단 - + second main phase 두번째 본단계 - + It is now the %1. 현재 턴의 %1입니다. @@ -3119,62 +3594,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings 대화 설정 - - Enable chat mentions - 대화 중 본인의 사용자명 언급 시 해당 문장을 강조 + + Custom alert words + 키워드 알림 - + + Enable chat mentions + 대화 중 본인의 사용자명 멘션 시 해당 문장을 강조 + + + + Enable mention completer + 멘션 자동완성 기능 + + + In-game message macros 게임 내 대화 매크로 - - Ignore unregistered users in main chat - 채널 대화창에서 서버에 가입하지 않은 사용자의 대화 차단 + + Ignore chat room messages sent by unregistered users + 서버에 가입하지 않은 사용자의 채널 대화 차단 - - Ignore chat room messages sent by unregistered users. - 가입하지 않은 사용자의 채널 대화 차단 + + Ignore private messages sent by unregistered users + 서버에 가입하지 않은 사용자가 보낸 1:1 대화 차단 - - Ignore private messages sent by unregistered users. - 가입하지 않은 사용자가 보낸 1:1 대화 차단 + + Enable desktop notifications for private messages + 1:1 대화를 받을 시 데스크탑 알림 설정 - + + Separate words with a space, alphanumeric characters only + 각 단어마다 스페이스바로 띄어써주세요. 문자 및 숫자만 가능합니다. + + + + Invert text color 문장 색 반전 - - Enable desktop notifications for private messages. - 1:1 대화를 받을 시 데스크탑 알림 설정 - - - + Enable desktop notification for mentions. - 대화 중 사용자명 언급시 데스크탑 알림 설정 + 본인의 사용자명 멘션 시 데스크탑 알림 설정 - + + (Color is hexadecimal) (16진수 색상 코드) - + Add message 메세지 추가 - + Message: 메세지: @@ -3240,336 +3727,337 @@ Local version is %1, remote version is %2. Player - + &View library 서고 보기 - + Move top cards to &graveyard... 맨 위 X장의 카드를 무덤으로 이동 - + View &top cards of library... 서고 맨 위 X장 보기 - + &View graveyard 무덤 보기 - + &View sideboard 사이드보드 보기 - + Player "%1" 플레이어 "%1" - - - + + + + &Hand - + &Reveal hand to... 손의 카드 전부 공개: - + Reveal r&andom card to... 손에서 무작위 카드 공개: - + &Library 서고 - - - + + + &Graveyard 무덤 - + &Sideboard 사이드보드 - + Red 적색 - + Yellow 황색 - + Green 녹색 - + View top cards of library 서고 맨 위 X장 보기 - + Number of cards: 카드 장수: - + &Draw card 카드 뽑기 - + Reveal top cards of library - + 서고 맨 위 카드 공개 - + Number of cards: (max. %1) - + 카드 장수 (최대 %1장): - + &View exile 추방 영역 보기 - - - + + + &Exile 추방 영역 - + Reveal t&op cards to... - + 서고 맨 위 카드 공개: - + D&raw cards... 카드 X장 뽑기 - + Take &mulligan 멀리건 - + &Shuffle 서고 섞기 - + &Counters 카운터 - + &Untap all permanents 모든 지속물 언탭 - + R&oll die... 주사위 굴리기 - + &Create token... 토큰 생성 - + C&reate another token 같은 종류의 토큰 추가 생성 - + S&ay 말하기 - + &Move hand to... 손의 카드 전부 이동: - - - - + + + + &Top of library 서고 맨 위 - - - - + + + + &Bottom of library 서고 맨 아래 - + &Move graveyard to... 무덤의 카드 전부 이동: - + &Move exile to... 추방 영역의 카드 전부 이동: - + Reveal &library to... 서고 공개: - + &Always reveal top card 서고 맨 위 카드 항상 공개 - + O&pen deck in deck editor 덱 편집기로 덱 열기 - + &Undo last draw 마지막으로 뽑은 카드 서고에 돌려놓기 - + Play top card &face down 서고 맨 위 카드를 뒷면으로 발동 - + Move top cards to &exile... 서고 맨 위 X장의 카드를 추방 - + Put top card on &bottom 서고 맨 위 카드를 맨 아래로 내리기 - + Put bottom card &in graveyard 맨 아래 카드를 무덤에 넣기 - + Cr&eate predefined token 미리 지정된 토큰 생성 - + C&ard 카드 - + &All players 모든 플레이어 - + &Play 발동 - + &Hide 숨기기 - + Play &Face Down 뒷면으로 발동 - + &Tap - + &Untap 언탭 - + Toggle &normal untapping 언탭단에서의 언탭 여부 변경 - + &Flip 카드 뒤집기 - + &Peek at card face 카드 앞면 보기 - + &Clone 카드 복제 - + Attac&h to card... 다른 카드에 부착 - + Unattac&h 카드에서 떼어냄 - + &Draw arrow... 화살표 그리기 - + &Increase power 공격력 증가 - + &Decrease power 공격력 감소 - + I&ncrease toughness 방어력 증가 - + D&ecrease toughness 방어력 감소 @@ -3579,22 +4067,22 @@ Local version is %1, remote version is %2. 공격력 및 방어력 증가 - + Dec&rease power and toughness 공격력 및 방어력 감소 - + Set &power and toughness... 공격력 및 방어력 설정 - + &Set annotation... 카드 주석 설정 - + &Add counter (%1) 카운터 증가 (%1) @@ -3604,103 +4092,108 @@ Local version is %1, remote version is %2. 카운터 감소 (%1) - + &Set counters (%1)... 카운터 설정 (%1)... - + Draw cards 카드 X장 뽑기 - - - - + + + + Number: 원하는 숫자: - + Move top cards to grave 맨 위 카드 X장을 무덤으로 이동 - + Move top cards to exile 맨 위 카드 X장을 추방 - + Roll die 주사위 굴리기 - + Number of sides: 주사위 면의 수: - + Set power/toughness 공격력 및 방어력 설정 - + Please enter the new PT: 새로운 공/방 값을 입력해 주세요: - + Set annotation 주석 설정 - + Please enter the new annotation: 새로운 주석을 입력해 주세요: - + Set counters 카운터 설정 + + + Cr&eate related card + 관련된 카드 생성 + QMenuBar - + Services 서비스 - + Hide %1 %1 가리기 - + Hide Others 기타 가리기 - + Show All 모두 보기 - + Preferences... 환경설정... - + Quit %1 %1 종료 - + About %1 %1에 관하여 @@ -3708,7 +4201,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) 코카트리스 리플레이 파일 (*.cor) @@ -3798,14 +4291,43 @@ Local version is %1, remote version is %2. + Permissions + 권한 + + + Players 플레이어 - + Games 게임 + + + + Error + 오류 + + + + You do not have the proper permission to join this room. + 본 채널에 들어가는데 필요한 권한을 가지고 있지 않습니다. + + + + Failed to join the room due to an unknown error. + 알 수 없는 오류로 채널에 들어갈 수 없었습니다. + + + + SequenceEdit + + + Shortcut already in use + 이미 사용중인 단축키 입니다. + SetsModel @@ -3848,7 +4370,7 @@ Local version is %1, remote version is %2. 서버 종료까지 남은 시간(분): - + Shut down server 서버 종료 @@ -3856,80 +4378,85 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + 경로 선택 - + Enable &sounds - + 음향 효과 켜기 - + Path to sounds directory: - + 음향 효과 파일 디렉토리 경로: - + Test system sound engine - + 시스템 사운드 엔진 테스트 - + Sound settings - + 음향 설정 - + Master volume requires QT5 - + 주 음량 조절 옵션은 QT5가 필요합니다. - + Master volume - + 주 음량 TabAdmin - + Update server &message 서버 공지사항 작성 - + &Shut down server 서버 종료 - + + &Reload configuration + 설정 새로고침 + + + Server administration functions 서버 관리 기능 - + &Unlock functions 기능 잠금 해제 - + &Lock functions 기능 잠금 - + Unlock administration functions 서버 관리 기능 잠금 해제 - + Do you really want to unlock the administration functions? 정말 서버 관리 기능의 잠금을 해제하시겠습니까? - + Administration 관리자 메뉴 @@ -3937,181 +4464,223 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... 덱 인쇄 - + &Close 덱 닫기 - + &Edit sets... 확장판 목록 편집 - - &Clear search - 검색 초기화 + + Filters + 카드 필터 - + + &Clear all filters + 모든 필터 초기화 + + + + Delete selected + 선택한 필터 삭제 + + + Deck &name: 덱 이름: - + &Comments: 설명: - + Hash: 해시값: - + &New deck 새로운 덱 작성 - + &Load deck... 덱 불러오기 - + &Save deck 덱 저장 - + Save deck &as... 다른 이름으로 저장 - + Load deck from cl&ipboard... 클립보드에서 덱 가져오기 - + Save deck to clip&board 클립보드로 덱 보내기 - + &Analyze deck on deckstats.net deckstats.net에서 덱 분석 - + Open custom image folder 사용자 카드 이미지 폴더 열기 - + + Open custom sets folder + 사용자 확장판 폴더 열기 + + + Add card to &maindeck 메인덱에 카드 추가 - + Add card to &sideboard 사이드보드에 카드 추가 - + &Deck Editor 덱 편집기 - + C&ard Database 카드 데이터베이스 - + + Show/Hide card information + 카드 정보 표시/숨김 + + + + Show/Hide deck + 덱 표시/숨김 + + + + Show/Hide filters + 검색 필터 표시/숨김 + + + + Reset layout + 화면 레이아웃 초기화 + + + + Card Info + 카드 정보 + + + + Deck + + + + Welcome - + 환영합니다 - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. - +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + 본 버전부터 덱빌딩 시에 원하는 확장판만 표시가 가능합니다. +현재 모든 확장판이 선택되어있습니다. +'확장판 목록 편집' 창에서 원하지 않는 확장판을 빼거나 확장판 순서를 변경하는게 가능합니다. - - Show card text only - 카드 텍스트만 보기 - - - + &Remove row 선택한 카드 전부 제거 - + &Increment number 선택한 카드 한장 추가 - + &Decrement number 선택한 카드 한장 제거 - + Edit &tokens... 토큰 목록 편집 - + Deck: %1 덱 편집기: %1 - + Are you sure? 확실하십니까? - + The decklist has been modified. Do you want to save the changes? 덱 리스트가 변경되었습니다. 변경사항을 저장하시겠습니까? - + Load deck 덱 불러오기 - - - + + + Error 오류 - + The deck could not be saved. 덱을 저장하는데 실패하였습니다. - - + + The deck could not be saved. Please check that the directory is writable and try again. 덱을 저장하는데 실패하였습니다. 덱 파일 디렉토리에 쓰기가 가능한지 확인하시고 다시 시도해 주세요. - + Save deck 덱 저장 @@ -4209,94 +4778,99 @@ Please enter a name: TabGame - + &Phases 단계 - + &Game 게임 - + Next &phase 다음 단계로 진행 - + Next &turn 턴 넘기기 - + &Remove all local arrows - 자기가 그린 화살표 제거 + 내가 그린 화살표 제거 - + + Rotate View Cl&ockwise + 플레이어 위치 시계방향으로 조정 + + + + Rotate View Co&unterclockwise + 플레이어 위치 반시계방향으로 조정 + + + Game &information 게임 정보 - + &Concede 항복 - + &Leave game 게임 나가기 - + C&lose replay 리플레이 닫기 - + &Say: 말하기: - + Concede 항복 - + Are you sure you want to concede this game? 정말 게임에서 항복하시겠습니까? - + Leave game 게임 나가기 - + Are you sure you want to leave this game? 정말 게임에서 나가시겠습니까? - + You are flooding the game. Please wait a couple of seconds. 너무 빨리 말하고 있습니다. 조금만 기다려 주십시오. - + You have been kicked out of the game. 게임에서 강제 퇴장 당하였습니다. - - Replay %1: %2 - 리플레이 %1: %2 - - - - Game %1: %2 - 게임 %1: %2 + + REPLAY + 리플레이 @@ -4340,54 +4914,54 @@ Please enter a name: TabReplays - + Local file system 로컬 파일 시스템 - + Server replay storage 서버 리플레이 보관함 - + Watch replay 리플레이 보기 - - + + Delete 삭제 - + Download replay 리플레이 다운로드 - + Toggle expiration lock 자동 삭제 여부 토글 - + Delete local file 로컬 파일 삭제 - + Are you sure you want to delete "%1"? 정말로 "%1"을(를) 삭제하시겠습니까? - + Delete remote replay 서버에 저장된 리플레이 삭제 - + Are you sure you want to delete the replay of game %1? 정말 게임 %1의 리플레이를 삭제하시겠습니까? @@ -4400,47 +4974,47 @@ Please enter a name: TabRoom - + &Say: 말하기: - + Chat 대화 - + &Room 채널 - + &Leave room 채널에서 나가기 - + &Clear chat 대화 내용 삭제 - + Chat Settings... 대화 설정... - + mentioned you. - 이(가) 당신을 언급하였습니다. + 이(가) 당신에게 멘션을 보냈습니다. - + Click to view 여기를 눌러 확인 - + You are flooding the chat. Please wait a couple of seconds. 너무 빨리 말하고 있습니다. 조금만 기다려 주세요. @@ -4456,16 +5030,26 @@ Please enter a name: TabSupervisor - + Are you sure? 확실하십니까? - + There are still open games. Are you sure you want to quit? 참가 중인 게임이 있습니다. 정말로 코카트리스를 종료하시겠습니까? + + + Promotion + 승급 + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + 관리자로 승급되셨습니다. 다시 로그인 하시면 해당 등급이 적용됩니다. + TabUserLists @@ -4488,169 +5072,301 @@ Please enter a name: UserContextMenu - + User &details 사용자 정보 - + Private &chat 1:1 대화 - + Show this user's &games 이 사용자의 게임 보기 - + Add to &buddy list 친구 목록에 추가 - + Remove from &buddy list 친구 목록에서 삭제 - + Add to &ignore list 차단 목록에 추가 - + Remove from &ignore list 차단 목록에서 제거 - + Kick from &game 게임에서 강제 퇴장 - + Ban from &server 서버에서 추방 - + + &Promote user to moderator + 사용자를 관리자로 승급 + + + + Dem&ote user from moderator + 사용자를 관리자로부터 강등 + + + %1's games %1의 게임 + + + + Success + 성공 + + + + Successfully promoted user. + 해당 사용자 승급에 성공하였습니다. + + + + Successfully demoted user. + 해당 사용자 강등에 성공하였습니다. + + + + + Failed + 실패 + + + + Failed to promote user. + 해당 사용자 승급에 실패하였습니다. + + + + Failed to demote user. + 해당 사용자 강등에 실패하였습니다. + UserInfoBox - + User information 사용자 정보 - + Real name: 실명: - - Gender: + + Pronouns: 성별: - + Location: 거주지: - + User level: 사용자 권한: - + Account Age: 계정 나이: - + + Edit + 프로필 수정 + + + + Change password + 비밀번호 변경 + + + + Change avatar + 아바타 변경 + + + Administrator 운영자 - + Moderator 관리자 - + Registered user - 가입한 사용자 + 서버에 가입한 사용자 - - + + Unregistered user - 가입하지 않은 사용자 + 서버에 가입하지 않은 사용자 - + Unknown 미상 - + Year - + Years - + Day - + Days + + + + + Information + 사용자 정보 + + + + User information updated. + 사용자 정보가 갱신되었습니다. + + + + + + + + + + + Error + 오류 + + + + This server does not permit you to update your user informations. + 본 서버는 당신의 사용자 정보를 수정 할 수 없게 설정되어 있습니다. + + + + + An error occured while trying to update your user informations. + 사용자 정보 갱신 중 오류가 발생하였습니다. + + + + Password changed. + 비밀번호가 변경되었습니다. + + + + This server does not permit you to change your password. + 본 서버는 당신의 비밀번호를 수정 할 수 없게 설정되어 있습니다. + + + + The new password is too short. + 새로운 비밀번호가 너무 짧습니다. + + + + The old password is incorrect. + 예전 비밀번호가 잘못되었습니다. + + + + Avatar updated. + 아바타를 변경하였습니다. + + + + This server does not permit you to update your avatar. + 본 서버는 당신의 아바타를 변경 할 수 없게 설정되어 있습니다. + + + + An error occured while trying to updater your avatar. + 아바타 변경 중 오류가 발생하였습니다. + UserInterfaceSettingsPage - + General interface settings 일반 인터페이스 설정 - + Enable notifications in taskbar 상태 표시줄 알림 설정 - + Notify in the taskbar for game events while you are spectating 관전중인 게임의 상태 표시줄 알림 설정 - + &Double-click cards to play them (instead of single-click) 카드를 더블 클릭해서 발동 (해제시 한번만 클릭하면 발동 됨) - + &Play all nonlands onto the stack (not the battlefield) by default 모든 대지가 아닌 카드를 발동 시에 스택으로 이동 (해제시 전장으로 바로 이동) - + + Annotate card text on tokens + 토큰에 카드 텍스트 주석 자동 추가 + + + Animation settings 애니메이션 설정 - + &Tap/untap animation 탭/언탭 애니메이션 @@ -4658,22 +5374,22 @@ Please enter a name: UserList - + Users connected to server: %1 서버에 접속한 사용자 수: %1 - + Users in this room: %1 현재 채널에 있는 사용자 목록 : %1 - + Buddies online: %1 / %2 온라인 친구 목록: %1 / %2 - + Ignored users online: %1 / %2 차단한 사용자 목록: %1 / %2 @@ -4695,41 +5411,58 @@ Please enter a name: Move selected set up 선택한 확장판을 한 단계 올리기 + + + Move selected set to the top + 선택한 확장판을 맨 위로 올리기 + Move selected set down 선택한 확장판을 한 단계 내리기 - - - Move selected set to top - 선택한 확장판을 맨 위로 올리기 - - Move selected set to bottom + Move selected set to the bottom 선택한 확장판을 맨 아래로 내리기 - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. - + hints: + 설명: + + + + Enable the sets that you want to have available in the deck editor + 덱 편집기에 사용할 확장판을 선택하여 주십시오. + + + + Move sets around to change their order, or click on a column header to sort sets on that field + 확장판의 순서를 수동으로 변경하거나 카테고리 이름을 클릭하여 정렬할 수 있습니다. + + + + Sets order decides the source that will be used when loading images for a specific card + 확장판의 순서에 따라 카드가 어떤 확장판의 이미지를 사용할지가 결정됩니다. + + + + Disabled sets will be used for loading images only if all the enabled sets failed + 선택하지 않은 판본도 카드 이미지를 불러오기 위하여 사용될 수 있습니다. Edit sets - 확장판 목록 수정 + 확장판 목록 편집 - + Success 성공 - + The sets database has been saved successfully. 확장판 데이터베이스가 성공적으로 저장되었습니다. @@ -4757,4 +5490,561 @@ Disabled sets will still be used for loading images. 카드 유형별 행 정렬 + + shortcutsTab + + + Form + 양식 + + + + Main Window + 메인 화면 + + + + Deck editor + 덱 편집기 + + + + Local gameplay + 오프라인 게임 시작 + + + + Watch replay + 리플레이 보기 + + + + Connect + 서버로 연결 + + + + Register + 서버에 회원 가입 + + + + Full screen + 전체 화면 + + + + Settings + 환경설정 + + + + Check for card updates + 카드 DB 업데이트 확인 + + + + Exit + 끝내기 + + + + Deck Editor + 덱 편집기 + + + + Analyze deck + 덱 분석 + + + + Load deck (clipboard) + 클립보드에서 덱 가져오기 + + + + Clerar all filters + 모든 필터 초기화 + + + + New deck + 새로운 덱 작성 + + + + Clear one filter + 선택한 필터 초기화 + + + + Open custom folder + 사용자 폴더 열기 + + + + Close + 덱 닫기 + + + + Print deck + 덱 인쇄 + + + + Edit sets + 확장판 목록 편집 + + + + Delete card + 카드 삭제 + + + + Edit tokens + 토큰 수정 + + + + Reset layout + 레이아웃 초기화 + + + + Add card + 카드 추가 + + + + Save deck + 덱 저장 + + + + Remove card + 카드 제거 + + + + Save deck as + 다른 이름으로 저장 + + + + Load deck + 덱 불러오기 + + + + Save deck (clipboard) + 클립보드로 덱 보내기 + + + + + Counters + 카운터 + + + + Life + 생명점 + + + + + + + + Set + 설정 + + + + + + + Add + 증가 + + + + + + + Remove + 감소 + + + + Red + 적색 + + + + Green + 녹색 + + + + Yellow + 황색 + + + + Mainwindow / Deck editor + 메인 화면 / 덱 편집기 + + + + Power / toughness + 공격력 / 방어력 설정 + + + + Power and toughness + 공격력 및 방어력 + + + + Add (+1/+1) + +1/+1 부여 + + + + Remove (-1/-1) + -1/-1 부여 + + + + Toughness + 방어력 + + + + Remove (-0/-1) + -0/-1 부여 + + + + Add (+0/+1) + +0/+1 부여 + + + + Power + 공격력 + + + + Remove (-1/-0) + -1/-0 부여 + + + + Add (+1/+0) + +1/+0 부여 + + + + Game Phases + 턴 단계 + + + + Untap + 언탭단 + + + + Disconnect + + + + + Upkeep + + + + + + Draw + 뽑기단 + + + + Main 1 + 전투 전 본단계 + + + + Start combat + 전투 시작단 + + + + Attack + 공격자 선언단 + + + + Block + 방어자 선언단 + + + + Damage + 전투 피해단 + + + + End combat + 전투 종료단 + + + + Main 2 + 전투 후 본단계 + + + + End + 종료단 + + + + Next phase + 다음 단계로 진행 + + + + Next turn + 턴 넘기기 + + + + Player + 플레이어 + + + + Tap Card + 카드 탭 + + + + Untap Card + 카드 언탭 + + + + Untap all + 모든 지속물 언탭 + + + + Toogle untap + 언탭단에서 언탭 여부 변경 + + + + Flip card + 카드 뒤집기 + + + + Peek card + 카드 앞면 보기 + + + + Play card + 카드 플레이 + + + + Attach card + 다른 카드에 부착 + + + + Unattach card + 부착 해제 + + + + Clone card + 카드 복제 + + + + Create token + 토큰 생성 + + + + Create another token + 같은 종류의 토큰 추가 생성 + + + + Set annotation + 주석 설정 + + + + Phases / P/T / Player + 턴 단계 / 공/방 / 플레이어 + + + + Move card to + 다른 영역으로 이동: + + + + Bottom library + 서고 맨 아래 + + + + Top library + 서고 맨 위 + + + + + Graveyard + 무덤 + + + + + Exile + 추방 영역 + + + + Hand + + + + + View + 보기 + + + + Library + 서고 + + + + Tops card of library + 서고 위 카드 + + + + Sideboard + 사이드보드 + + + + Close recent view + 특정 영역 보기 닫음 + + + + Pre-play + 플레이 전 + + + + Load remote deck + 서버에 저장된 덱 불러오기 + + + + Load local deck + 로컬 덱 불러오기 + + + + Game play + 게임 플레이 + + + + Draw arrow + 화살표 그리기 + + + + Leave game + 게임 나가기 + + + + Remove local arrows + 내가 그린 화살표 제거 + + + + Concede + 항복 + + + + Roll dice + 주사위 굴리기 + + + + Rotate view CW + 플레이어 위치 시계방향으로 조정 + + + + Shuffle library + 서고 섞기 + + + + Rotate view CCW + 플레이어 위치 반시계방향으로 조정 + + + + Mulligan + 멀리건 + + + + Draw card + 카드 뽑기 + + + + Draw cards + 카드 X장 뽑기 + + + + Undo draw + 카드 뽑기 취소 + + + + Always reveal top card + 서고 맨 위 카드 항상 공개 + + + + Draw / Move / View / Game play + 카드 뽑기 / 이동 / 보기 / 게임플레이 + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_nb.ts b/cockatrice/translations/cockatrice_nb.ts index bbb60160..fa772db5 100644 --- a/cockatrice/translations/cockatrice_nb.ts +++ b/cockatrice/translations/cockatrice_nb.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... &Sett teller … - + Set counter Sett teller - + New value for counter '%1': Ny verdi for teller '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Bakgrunnsbilder for soner - + Hand background: Håndbakgrunn: - + Stack background: Stokkbakgrunn: - + Table background: Bordduk: - + Player info background: Bakgrunn på spillerinfo: - + Card back: Kortbakside: - + Card rendering Korttegning - + Display card names on cards having a picture Vis navn på kort med bilde - + Scale cards on mouse over Forstørr kort når muspekeren er over - + Hand layout Håndlayout - + Display hand horizontally (wastes space) Vis hånden horisontalt (bruker unødig mye plass) - + Enable left justification Venstrejuster tekst - + Table grid layout Ruteoppsett for bord - + Invert vertical coordinate Snu vertikale koordinater opp-ned - + Minimum player count for multi-column layout: Minste antall spillere for fler-kolonne-visning: - - - - - + + + + + Choose path Velg filsti @@ -107,97 +107,102 @@ BanDialog - + ban &user name sperr &brukernavn - + ban &IP address sperr &IP-adresse - + + ban client I&D + + + + Ban type Sperretype - + &permanent ban &permanent sperre - + &temporary ban &midlertidig sperre - + &Days: &Dager: - + &Hours: &Timer: - + &Minutes: &Minutter: - + Duration of the ban Lengde på sperre - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Fyll inn grunn for sperre. Dette vises kun til moderatorer, og er ikke synlig for brukeren. - + Please enter the reason for the ban that will be visible to the banned person. Fyll inn grunn for sperre som vises til den sperrede brukeren. - + &OK &OK - + &Cancel &Avbryt - + Ban user from server Sperr bruker fra tjener - + Error Feil - - You have to select a name-based or IP-based ban, or both. - Du må velge navnebasert og/eller IP-basert sperre. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. K/L + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: Navn: - + Mana cost: Mannakost: - + + Color(s): + + + + Card type: Korttype: - + P / T: K / L: - + Loyalty: Lojalitet: @@ -276,27 +304,32 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. Vis både kort og tekst - + Name: Navn: - + Mana cost: Mannakost: - + + Color(s): + + + + Card type: Korttype: - + P / T: K / L: - + Loyalty: Lojalitet: @@ -560,12 +593,12 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. DeckEditorSettingsPage - + Nothing is here... yet Det er ingenting her … ennå - + General Generelt @@ -573,17 +606,17 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. DeckListModel - + Number Nummer - + Card Kort - + Price Pris @@ -605,42 +638,42 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. DeckViewContainer - - Load &local deck - Last inn stokk fra %lokal disk + + Load local deck + - - Load d&eck from server - Last inn kortstokk fra &tjener + + Load deck from server + - + Ready to s&tart &Klar til å begynne - + S&ideboard unlocked &Sidebrett åpnet - + S&ideboard locked &Sidebrett låst - + Load deck Last inn kortstokk - + Error Feil - + The selected file could not be loaded. Den filen kan ikke lastes inn. @@ -686,37 +719,52 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Tjener - + + Enter host name + + + + &Port: &Port - + Player &name: Spiller&navn - + P&assword: &Passord - + &Save password &Lagre passord - + A&uto connect at start Koble til &automatisk når programmet starter - + Connect to server Koble til tjener @@ -724,82 +772,92 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. DlgCreateGame - + &Description: &Beskrivelse - + &Password: &Passord - + P&layers: Sp&illere - + + Re&member settings + + + + Game type Spilleregler - + Only &buddies can join Bare &venner slipper inn - + Only &registered users can join Bare &registrerte brukere slipper inn - + Joining restrictions Begrens deltakere - + &Spectators can watch Tillat &tilskuere - + Spectators &need a password to watch Tilskuere på ha &passord for å se på - + Spectators can see &hands Tilskuere kan se %begge spilleres hender - + Spectators can &chat Tilskuere kan p&rate - + Spectators Tilskuere - + + &Clear + + + + Create game Lag nytt spill - + Game information Spilloppsett - + Error Feil - + Server error. Tjenerfeil @@ -807,96 +865,169 @@ Dette vises kun til moderatorer, og er ikke synlig for brukeren. DlgCreateToken - + &Name: &Navn - + Token Plassholder - + C&olor: &Farge - + white hvit - + blue blå - + black sort - + red rød - + green grønn - + multicolor flerfarget - + colorless fargeløs - + &P/T: &K/L: - + &Annotation: M&erknad - + &Destroy token when it leaves the table &Slett plassholder når den forlater bordet - + Token data Plassholderinfo - + Show &all tokens Vis alle plassholdere - + Show tokens from this &deck Vis alle plassoldere fra denne &kortstokken - + Choose token from list Velg plassholder fra liste - + Create token Lag ny plassholder + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di - + + Error - + + Invalid deck list. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1097,7 +1372,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1108,21 +1383,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1131,7 +1406,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1140,63 +1415,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User Interface - + Deck Editor - + Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - - - - - &Join - - @@ -1206,7 +1476,7 @@ Would you like to change your database location setting? - + Error @@ -1251,37 +1521,47 @@ Would you like to change your database location setting? - + Join game - + Password: - + Please join the respective room first. - + Games - + &Filter games - + C&lear filter - + + C&reate + + + + + &Join + + + + J&oin as spectator @@ -1398,97 +1678,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path - + Success - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Personal settings - + Language: - + Download card pictures on the fly - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths - + Decks directory: - + Replays directory: - + Pictures directory: - + Card database: - + Token database: - + Picture cache size: - - + + English (Norwegian Bokmål) @@ -1496,357 +1786,529 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. - + Scheduled server shutdown. - + Banned by moderator - + Expected end time: %1 - + This ban lasts indefinitely. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed - + The server has terminated your connection. Reason: %1 - + Scheduled server shutdown - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 - + Number of players - + Please enter the number of players. - - + + Player %1 - + Load replay - + About Cockatrice - + Version %1 - + Translators: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error - + Server timeout - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. - + + You are banned until %1. - + + You are banned indefinitely. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. - + Connecting to %1... - + + Registering to %1 as %2... + + + + Disconnected - + Connected, logging in at %1 - - Logged in as %1 at %2 - - - - + &Connect... - + &Disconnect - + Start &local game... - + &Watch replay... - + &Deck editor - + &Full screen - + + &Register to server... + + + + &Settings... - - + + &Exit - + A&ctions - + &Cockatrice - + &About Cockatrice - + &Help - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2085,62 +2547,62 @@ Local version is %1, remote version is %2. - + the bottom card of %1's library - + the bottom card of his library - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library - + from the bottom of her library - + the top card of %1's library - + the top card of his library - + the top card of her library - + from the top of %1's library - + from the top of his library - + from the top of her library @@ -2543,304 +3005,304 @@ Local version is %1, remote version is %2. - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - + %1 sets %2 to not untap normally. male - + %1 sets %2 to untap normally. female - + %1 sets %2 to untap normally. male - + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male + + + %1 is looking at %2. + female + + %1 is looking at %2. - female + male + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + - - %1 is looking at %2. - male - - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - - - %1 is looking at the top %n card(s) %2. - male - + %1 stops looking at %2. - female - - - - - %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male + + + %1 reveals %2. + female + + %1 reveals %2. - female - - - - - %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - + %1 randomly reveals %2%3 to %4. p1 male, p2 female - + %1 randomly reveals %2%3 to %4. p1 male, p2 male - + %1 randomly reveals %2%3. female - + %1 randomly reveals %2%3. male - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - + a card @@ -2903,12 +3365,12 @@ Local version is %1, remote version is %2. - + ending phase - + untap step @@ -3018,64 +3480,64 @@ Local version is %1, remote version is %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step - + draw step - + first main phase - + beginning of combat step - + declare attackers step - + declare blockers step - + combat damage step - + end of combat step - + second main phase - + It is now the %1. @@ -3083,62 +3545,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message - + Message: @@ -3204,336 +3678,337 @@ Local version is %1, remote version is %2. Player - + &View library - + Move top cards to &graveyard... - + View &top cards of library... - + &View graveyard - + &View sideboard - + Player "%1" + + + + + + &Hand + + + + + &Reveal hand to... + + + + + Reveal r&andom card to... + + + + + &Library + + + + + + + + &Graveyard + + + + + &Sideboard + + + + + Red + + + + + Yellow + + + + + Green + + + + + View top cards of library + + + + + Number of cards: + + + + + &Draw card + + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + - - &Hand - - - - - &Reveal hand to... - - - - - Reveal r&andom card to... - - - - - &Library - - - - - - - - &Graveyard - - - - - &Sideboard - - - - - Red - - - - - Yellow - - - - - Green - - - - - View top cards of library - - - - - Number of cards: - - - - - &Draw card - - - - - Reveal top cards of library - - - - - Number of cards: (max. %1) - - - - - &View exile - - - - - - - - &Exile - - - - Reveal t&op cards to... - - - - - D&raw cards... - - - - - Take &mulligan - - - - - &Shuffle - - - - - &Counters - - - - - &Untap all permanents - - - - - R&oll die... - - - - - &Create token... - - - - - C&reate another token - - - - - S&ay - - - - - &Move hand to... - - - - - - - - &Top of library - - - - - - - + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card - + O&pen deck in deck editor - + &Undo last draw - + Play top card &face down - + Move top cards to &exile... - + Put top card on &bottom - + Put bottom card &in graveyard - + Cr&eate predefined token - + C&ard - + &All players - + &Play - + &Hide - + Play &Face Down - + &Tap - + &Untap - + Toggle &normal untapping - + &Flip - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Increase power - + &Decrease power - + I&ncrease toughness - + D&ecrease toughness @@ -3543,22 +4018,22 @@ Local version is %1, remote version is %2. - + Dec&rease power and toughness - + Set &power and toughness... - + &Set annotation... - + &Add counter (%1) @@ -3568,103 +4043,108 @@ Local version is %1, remote version is %2. - + &Set counters (%1)... - + Draw cards - - - - + + + + Number: - + Move top cards to grave - + Move top cards to exile - + Roll die - + Number of sides: - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3672,7 +4152,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) @@ -3762,14 +4242,43 @@ Local version is %1, remote version is %2. - Players + Permissions + Players + + + + Games + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3812,7 +4321,7 @@ Local version is %1, remote version is %2. - + Shut down server @@ -3820,37 +4329,37 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3858,42 +4367,47 @@ Local version is %1, remote version is %2. TabAdmin - + Update server &message - + &Shut down server - + + &Reload configuration + + + + Server administration functions - + &Unlock functions - + &Lock functions - + Unlock administration functions - + Do you really want to unlock the administration functions? - + Administration @@ -3901,179 +4415,219 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... - + &Close - + &Edit sets... - - &Clear search + + Filters - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: - + &Comments: - + Hash: - + &New deck - + &Load deck... - + &Save deck - + Save deck &as... - + Load deck from cl&ipboard... - + Save deck to clip&board - + &Analyze deck on deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck - + Add card to &sideboard - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row - + &Increment number - + &Decrement number - + Edit &tokens... - + Deck: %1 - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4170,93 +4724,98 @@ Please enter a name: TabGame - + &Phases - + &Game - + Next &phase - + Next &turn - + &Remove all local arrows - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information - + &Concede - + &Leave game - + C&lose replay - + &Say: - + Concede - + Are you sure you want to concede this game? - + Leave game - + Are you sure you want to leave this game? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 - - - - - Game %1: %2 + + REPLAY @@ -4301,54 +4860,54 @@ Please enter a name: TabReplays - + Local file system - + Server replay storage - + Watch replay - - + + Delete - + Download replay - + Toggle expiration lock - + Delete local file - + Are you sure you want to delete "%1"? - + Delete remote replay - + Are you sure you want to delete the replay of game %1? @@ -4361,47 +4920,47 @@ Please enter a name: TabRoom - + &Say: - + Chat - + &Room - + &Leave room - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. @@ -4417,15 +4976,25 @@ Please enter a name: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4448,169 +5017,301 @@ Please enter a name: UserContextMenu - + User &details - + Private &chat - + Show this user's &games - + Add to &buddy list - + Remove from &buddy list - + Add to &ignore list - + Remove from &ignore list - + Kick from &game - + Ban from &server - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information - + Real name: - - Gender: + + Pronouns: - + Location: - + User level: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator - + Moderator - + Registered user - - + + Unregistered user - + Unknown - + Year - + Years - + Day - + Days + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings - + &Tap/untap animation @@ -4618,22 +5319,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 - + Buddies online: %1 / %2 - + Ignored users online: %1 / %2 @@ -4655,27 +5356,44 @@ Please enter a name: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4684,12 +5402,12 @@ Disabled sets will still be used for loading images. - + Success - + The sets database has been saved successfully. @@ -4717,4 +5435,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_nl.ts b/cockatrice/translations/cockatrice_nl.ts index 30b4e3e9..b04f2809 100644 --- a/cockatrice/translations/cockatrice_nl.ts +++ b/cockatrice/translations/cockatrice_nl.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... &Teller instellen... - + Set counter Teller instellen - + New value for counter '%1': Nieuwe waarde van teller '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Zone achtergrondafbeeldingen - + Hand background: Hand achtergrond: - + Stack background: Stack achtergrond: - + Table background: Tafel achtergrond: - + Player info background: Spelersinfo achtergrond: - + Card back: Achterzijde van kaarten: - + Card rendering Kaartweergave - + Display card names on cards having a picture Kaartnamen altijd weergeven - + Scale cards on mouse over - + Hand layout Handweergave - + Display hand horizontally (wastes space) Hand horizontaal weergeven (gebruikt extra ruimte) - + Enable left justification - + Table grid layout Tafelindeling - + Invert vertical coordinate Verticale coördinaat omkeren - + Minimum player count for multi-column layout: Minimaal aantal spelers voor kolommenindeling: - - - - - + + + + + Choose path Kies map @@ -107,97 +107,102 @@ BanDialog - + ban &user name ban &gebruikersnaam - + ban &IP address ban &IP-adres - + + ban client I&D + + + + Ban type Ban type - + &permanent ban &definitieve ban - + &temporary ban &tijdelijke ban - + &Days: &Dagen: - + &Hours: &Uur: - + &Minutes: &Minuten: - + Duration of the ban Duur van de ban - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Voer een reden voor de ban in. Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de gebannen persoon. - + Please enter the reason for the ban that will be visible to the banned person. Voer een reden in die zichtbaar zal zijn voor de gebannen persoon. - + &OK &OK - + &Cancel &Annuleren - + Ban user from server Ban gebruiker van server - + Error Fout - - You have to select a name-based or IP-based ban, or both. - Selecteer een IP-adres en/of gebruikersnaam. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g P/T + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: Naam: - + Mana cost: Manakosten: - + + Color(s): + + + + Card type: Kaarttype: - + P / T: P / T: - + Loyalty: Loyaliteit: @@ -276,27 +304,32 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g Toon beide - + Name: Naam: - + Mana cost: Manakosten: - + + Color(s): + + + + Card type: Kaarttype: - + P / T: P / T: - + Loyalty: Loyaliteit: @@ -560,12 +593,12 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g DeckEditorSettingsPage - + Nothing is here... yet - + General Algemeen @@ -573,17 +606,17 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g DeckListModel - + Number Nummer - + Card Kaart - + Price Prijs @@ -605,42 +638,42 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g DeckViewContainer - - Load &local deck - Laad &lokaal dek + + Load local deck + - - Load d&eck from server - Laad d&ek van server + + Load deck from server + - + Ready to s&tart Klaar om te s&tarten - + S&ideboard unlocked S&ideboard ontgrendeld - + S&ideboard locked S&ideboard vergrendeld - + Load deck Laad deck - + Error Fout - + The selected file could not be loaded. Het geselecteerde bestand kon niet geladen worden. @@ -686,37 +719,52 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Gastheer - + + Enter host name + + + + &Port: &Poort - + Player &name: Spelers&naam: - + P&assword: W&achtwoord: - + &Save password Wachtwoord &Opslaan - + A&uto connect at start A&utomatisch verbinden bij opstarten - + Connect to server Verbinden met server @@ -724,82 +772,92 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g DlgCreateGame - + &Description: &Omschrijving: - + &Password: &Wachtwoord: - + P&layers: Spe&lers: - + + Re&member settings + + + + Game type Spel type - + Only &buddies can join Alleen &vrienden kunnen meedoen - + Only &registered users can join Alleen ge&registreerde gebruikers kunnen meedoen - + Joining restrictions Restricties voor meedoen - + &Spectators can watch Toe&schouwers kunnen meekijken - + Spectators &need a password to watch Toeschouwers hebben een wachtwoord &nodig om mee te kijken - + Spectators can see &hands Toeschouwers kunnen &handen zien - + Spectators can &chat Toeschouwers kunnen &chatten - + Spectators Toeschouwers - + + &Clear + + + + Create game Spel aanmaken - + Game information Spel informatie - + Error Fout - + Server error. Server Fout @@ -807,96 +865,169 @@ Dit wordt opgeslagen voor het moderatorteam en zal niet zichtbaar zijn voor de g DlgCreateToken - + &Name: &Naam: - + Token Token - + C&olor: Kle&ur: - + white wit - + blue blauw - + black zwart - + red rood - + green groen - + multicolor veelkleurig - + colorless kleurloos - + &P/T: &P/T: - + &Annotation: &Annotatie: - + &Destroy token when it leaves the table &Vernietig token als het de tafel verlaat - + Token data Token data - + Show &all tokens Laat &alle tokens zien - + Show tokens from this &deck Laat tokens van dit &dek zien - + Choose token from list Kies token uit lijst - + Create token Maak token + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Verversen @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Laad dek van klembord - + + Error Fout - + + Invalid deck list. Niet geldige deck lijst. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Laad dek + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error Fout - + Unknown Error loading card database Onbekende Fout in het lezen van de kaart database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1378,7 @@ U zou oracle moeten herstarten om uw database te updaten. Zou u de locatie van uw database willen veranderen? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,21 +1395,21 @@ Dit zou opgelost kunnen worden door oracle opnieuw te starten en uw kaart databa Zou u de locatie van uw database willen veranderen? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1143,7 +1418,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1152,63 +1427,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? - + Settings - + General - + Appearance - + User Interface - + Deck Editor - + Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - - - - - &Join - - @@ -1218,7 +1488,7 @@ Would you like to change your database location setting? - + Error @@ -1263,37 +1533,47 @@ Would you like to change your database location setting? - + Join game - + Password: - + Please join the respective room first. - + Games - + &Filter games - + C&lear filter - + + C&reate + + + + + &Join + + + + J&oin as spectator @@ -1410,97 +1690,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path - + Success - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Personal settings - + Language: - + Download card pictures on the fly - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths - + Decks directory: - + Replays directory: - + Pictures directory: - + Card database: - + Token database: - + Picture cache size: - - + + English Nederlands (Dutch) @@ -1508,357 +1798,529 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. - + Scheduled server shutdown. - + Banned by moderator - + Expected end time: %1 - + This ban lasts indefinitely. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed - + The server has terminated your connection. Reason: %1 - + Scheduled server shutdown - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 - + Number of players - + Please enter the number of players. - - + + Player %1 - + Load replay - + About Cockatrice - + Version %1 - + Translators: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error - + Server timeout - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. - + + You are banned until %1. - + + You are banned indefinitely. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. - + Connecting to %1... - + + Registering to %1 as %2... + + + + Disconnected - + Connected, logging in at %1 - - Logged in as %1 at %2 - - - - + &Connect... - + &Disconnect - + Start &local game... - + &Watch replay... - + &Deck editor - + &Full screen - + + &Register to server... + + + + &Settings... - - + + &Exit - + A&ctions - + &Cockatrice - + &About Cockatrice - + &Help - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2097,62 +2559,62 @@ Local version is %1, remote version is %2. - + the bottom card of %1's library - + the bottom card of his library - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library - + from the bottom of her library - + the top card of %1's library - + the top card of his library - + the top card of her library - + from the top of %1's library - + from the top of his library - + from the top of her library @@ -2555,304 +3017,304 @@ Local version is %1, remote version is %2. - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - + %1 sets %2 to not untap normally. male - + %1 sets %2 to untap normally. female - + %1 sets %2 to untap normally. male - + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male + + + %1 is looking at %2. + female + + %1 is looking at %2. - female + male + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + - - %1 is looking at %2. - male - - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - - - %1 is looking at the top %n card(s) %2. - male - + %1 stops looking at %2. - female - - - - - %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male + + + %1 reveals %2. + female + + %1 reveals %2. - female - - - - - %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - + %1 randomly reveals %2%3 to %4. p1 male, p2 female - + %1 randomly reveals %2%3 to %4. p1 male, p2 male - + %1 randomly reveals %2%3. female - + %1 randomly reveals %2%3. male - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - + a card @@ -2915,12 +3377,12 @@ Local version is %1, remote version is %2. - + ending phase - + untap step @@ -3030,64 +3492,64 @@ Local version is %1, remote version is %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step - + draw step - + first main phase - + beginning of combat step - + declare attackers step - + declare blockers step - + combat damage step - + end of combat step - + second main phase - + It is now the %1. @@ -3095,62 +3557,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message - + Message: @@ -3216,336 +3690,337 @@ Local version is %1, remote version is %2. Player - + &View library - + Move top cards to &graveyard... - + View &top cards of library... - + &View graveyard - + &View sideboard - + Player "%1" + + + + + + &Hand + + + + + &Reveal hand to... + + + + + Reveal r&andom card to... + + + + + &Library + + + + + + + + &Graveyard + + + + + &Sideboard + + + + + Red + + + + + Yellow + + + + + Green + + + + + View top cards of library + + + + + Number of cards: + + + + + &Draw card + + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + - - &Hand - - - - - &Reveal hand to... - - - - - Reveal r&andom card to... - - - - - &Library - - - - - - - - &Graveyard - - - - - &Sideboard - - - - - Red - - - - - Yellow - - - - - Green - - - - - View top cards of library - - - - - Number of cards: - - - - - &Draw card - - - - - Reveal top cards of library - - - - - Number of cards: (max. %1) - - - - - &View exile - - - - - - - - &Exile - - - - Reveal t&op cards to... - - - - - D&raw cards... - - - - - Take &mulligan - - - - - &Shuffle - - - - - &Counters - - - - - &Untap all permanents - - - - - R&oll die... - - - - - &Create token... - - - - - C&reate another token - - - - - S&ay - - - - - &Move hand to... - - - - - - - - &Top of library - - - - - - - + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card - + O&pen deck in deck editor - + &Undo last draw - + Play top card &face down - + Move top cards to &exile... - + Put top card on &bottom - + Put bottom card &in graveyard - + Cr&eate predefined token - + C&ard - + &All players - + &Play - + &Hide - + Play &Face Down - + &Tap - + &Untap - + Toggle &normal untapping - + &Flip - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Increase power - + &Decrease power - + I&ncrease toughness - + D&ecrease toughness @@ -3555,22 +4030,22 @@ Local version is %1, remote version is %2. - + Dec&rease power and toughness - + Set &power and toughness... - + &Set annotation... - + &Add counter (%1) @@ -3580,103 +4055,108 @@ Local version is %1, remote version is %2. - + &Set counters (%1)... - + Draw cards - - - - + + + + Number: - + Move top cards to grave - + Move top cards to exile - + Roll die - + Number of sides: - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3684,7 +4164,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) @@ -3774,14 +4254,43 @@ Local version is %1, remote version is %2. - Players + Permissions + Players + + + + Games + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3824,7 +4333,7 @@ Local version is %1, remote version is %2. - + Shut down server @@ -3832,37 +4341,37 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3870,42 +4379,47 @@ Local version is %1, remote version is %2. TabAdmin - + Update server &message - + &Shut down server - + + &Reload configuration + + + + Server administration functions - + &Unlock functions - + &Lock functions - + Unlock administration functions - + Do you really want to unlock the administration functions? - + Administration @@ -3913,181 +4427,221 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... - + &Close - + &Edit sets... - - &Clear search + + Filters - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: - + &Comments: - + Hash: - + &New deck - + &Load deck... - + &Save deck - + Save deck &as... - + Load deck from cl&ipboard... - + Save deck to clip&board - + &Analyze deck on deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck - + Add card to &sideboard - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - Toon enkel kaarttekst - - - + &Remove row Verwijder rij - + &Increment number Verhoog getal - + &Decrement number Verlaag getal - + Edit &tokens... Bewerk jetons. - + Deck: %1 Dek: %1 - + Are you sure? Ben je zeker? - + The decklist has been modified. Do you want to save the changes? De deklijst is gewijzigd. Wil je de wijzigingen opslaan? - + Load deck Laad dek - - - + + + Error Fout - + The deck could not be saved. Het dek kon niet opslagen worden. - - + + The deck could not be saved. Please check that the directory is writable and try again. Het dek kon niet opgeslagen worden. Kijk a.u.b. na of je schrijfrechten op de folder hebt, en probeer opnieuw. - + Save deck Deck opslaan @@ -4184,94 +4738,99 @@ Please enter a name: TabGame - + &Phases Fases - + &Game Spel - + Next &phase Volgende fase - + Next &turn Volgende beurt - + &Remove all local arrows Verwijder alle lokale pijlen - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information Spelinformatie - + &Concede Geef op - + &Leave game Verlaat spel - + C&lose replay Sluit opgeslagen spel - + &Say: Zeg: - + Concede Opgeven - + Are you sure you want to concede this game? Ben je zeker dat je wilt opgeven voor dit spel? - + Leave game Verlaat spel - + Are you sure you want to leave this game? Ben je zeker dat je dit spel wilt verlaten? - + You are flooding the game. Please wait a couple of seconds. Je bent het spel aan het overstelpen. Wacht enkele seconden aub. - + You have been kicked out of the game. Je bent uit het spel gegooid. - - Replay %1: %2 - Opgeslagen spel %1: %2 - - - - Game %1: %2 - Spel %1: %2 + + REPLAY + @@ -4315,54 +4874,54 @@ Please enter a name: TabReplays - + Local file system Lokaal bestandssysteem - + Server replay storage Opslag gespeelde spellen op server - + Watch replay Bekijk opgeslagen spel - - + + Delete Verwijder - + Download replay Download opgeslagen spel. - + Toggle expiration lock Schakel expiratieslot om - + Delete local file Verwijder lokaal opgeslagen spel - + Are you sure you want to delete "%1"? Ben je zeker dat je "%1" wilt verwijderen? - + Delete remote replay Verwijder op server opgeslagen spel - + Are you sure you want to delete the replay of game %1? Ben je zeker dat je het opgeslagen spel %1 wilt verwijderen? @@ -4375,47 +4934,47 @@ Please enter a name: TabRoom - + &Say: Zeg: - + Chat Gesprek - + &Room Kamer - + &Leave room Verlaat kamer - + &Clear chat Maak gespreksgeschiedenis leeg - + Chat Settings... Gespreksinstellingen... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. Je bent de chat aan het overstelpen. Wacht enkele seconden a.u.b. @@ -4431,15 +4990,25 @@ Please enter a name: TabSupervisor - + Are you sure? Ben je zeker? - + There are still open games. Are you sure you want to quit? Er staan nog spellen open. Ben je zeker dat je wilt afsluiten? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4462,169 +5031,301 @@ Please enter a name: UserContextMenu - + User &details Gebruikersdetails - + Private &chat Privaat gesprek - + Show this user's &games Toon spellen van deze gebruiker - + Add to &buddy list Voeg toe aan vriendenlijst - + Remove from &buddy list Verwijder uit vriendenlijst - + Add to &ignore list Voeg toe aan negatielijst - + Remove from &ignore list Verwijder uit negatielijst - + Kick from &game Gooi uit spel - + Ban from &server Verban van de &server - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games %s's spellen + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Gebruikersinformatie - + Real name: Echte naam: - - Gender: - Geslacht: + + Pronouns: + - + Location: Locatie: - + User level: Gebruikersniveau: - + Account Age: Leeftijd lidmaatschap - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator Administrator - + Moderator Moderator - + Registered user Geregistreerde gebruiker - - + + Unregistered user Ongeregistreerde gebruiker - + Unknown Onbekend - + Year Jaar - + Years Jaren - + Day Dag - + Days Dagen + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Algemene weergave-instellingen - + Enable notifications in taskbar Activeer meldingen in de taakbalk - + Notify in the taskbar for game events while you are spectating Meld spelgebeurtenissen in de taakbalk terwijl je toekijkt - + &Double-click cards to play them (instead of single-click) Dubbel-klik op kaarten om ze te spelen (in plaats van enkele klik) - + &Play all nonlands onto the stack (not the battlefield) by default Speel standaard alle niet-landen op de stapel (niet op het slagveld) - + + Annotate card text on tokens + + + + Animation settings Animatie-instellingen - + &Tap/untap animation &Tap/Untap animatie @@ -4632,22 +5333,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 Gebruikers in de kamer: %1 - + Buddies online: %1 / %2 Vrienden on-line: %1 / %2 - + Ignored users online: %1 / %2 Genegeerde gebruikers on-line: %1 / %2 @@ -4669,27 +5370,44 @@ Please enter a name: Move selected set up Verplaats de geselecteerde set naar boven + + + Move selected set to the top + + Move selected set down Verplaats de geselecteerde set naar beneden - - - Move selected set to top - Verplaats de geselecteerde set naar het begin van de lijst - - Move selected set to bottom - Verplaats de geselecteerde set naar het einde van de lijst + Move selected set to the bottom + - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4698,12 +5416,12 @@ Disabled sets will still be used for loading images. Bewerk sets - + Success Succesvol - + The sets database has been saved successfully. De sets databank is succesvol opgeslagen. @@ -4731,4 +5449,561 @@ Disabled sets will still be used for loading images. Stapelweergave + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_pl.ts b/cockatrice/translations/cockatrice_pl.ts index c17aacac..348f7663 100644 --- a/cockatrice/translations/cockatrice_pl.ts +++ b/cockatrice/translations/cockatrice_pl.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... U&staw licznik… - + Set counter Ustaw licznik - + New value for counter '%1': Nowa wartość dla licznika '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Obrazki tła - + Hand background: Tło dla ręki: - + Stack background: Tło dla stosu: - + Table background: Tło dla stołu: - + Player info background: Tło dla pola informacji o graczu: - + Card back: Rewers karty: - + Card rendering Renderowanie kart - + Display card names on cards having a picture Wyświetl nazwy kart na kartach z obrazkami - + Scale cards on mouse over Skaluj karty po najechaniu kursorem myszy - + Hand layout Układ ręki - + Display hand horizontally (wastes space) Wyświetl karty na ręce poziomo (marnuje miejsce) - + Enable left justification - + Wyrównuj karty do lewej strony pola - + Table grid layout Układ stołu - + Invert vertical coordinate Odwróć współrzędne pionowe - + Minimum player count for multi-column layout: Minimalna liczba graczy dla widoku wielokolumnowego - - - - - + + + + + Choose path Wybierz ścieżkę @@ -107,99 +107,104 @@ BanDialog - + ban &user name zbanuj nazwę &użytkownika - + ban &IP address zbanuj adres &IP - + + ban client I&D + + + + Ban type Rodzaj bana - + &permanent ban ban &permanentny - + &temporary ban ban &tymczasowy - + &Days: &Dni: - + &Hours: &Godziny: - + &Minutes: &Minuty: - + Duration of the ban Czas trwania bana - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Podaj przyczynę bana. Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowanej osoby. - + Please enter the reason for the ban that will be visible to the banned person. Podaj przyczynę bana do wglądu dla banowanej osoby. - + &OK &OK - + &Cancel &Anuluj - + Ban user from server Zbanuj użytkownika z serwera - + Error Błąd - - You have to select a name-based or IP-based ban, or both. - Musisz wybrać bana na nazwę, IP lub oba. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + Znaleziono nowe dodatki - + %1 new set(s) have been found in the card database. Do you want to enable them? - + W bazie kart pojawiły się nowe dodatki (%1). Czy chcesz je włączyć? @@ -230,30 +235,53 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan S/W + + CardFrame + + + Image + Karta + + + + Description + Opis + + + + Both + Karta + opis + + CardInfoText - + Name: Nazwa: - + Mana cost: Koszt many: - + + Color(s): + + + + Card type: Typ karty: - + P / T: P / T: - + Loyalty: Lojalność: @@ -276,27 +304,32 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan Pokazuj pełne informacje - + Name: Nazwa: - + Mana cost: Koszt many: - + + Color(s): + + + + Card type: Typ karty: - + P / T: P / T: - + Loyalty: Lojalność: @@ -560,12 +593,12 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan DeckEditorSettingsPage - + Nothing is here... yet - + Nic tu nie ma.... jeszcze! - + General Ogólne @@ -573,17 +606,17 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan DeckListModel - + Number Ilość - + Card Karta - + Price Cena @@ -605,42 +638,42 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan DeckViewContainer - - Load &local deck - Wczytaj &lokalną talię + + Load local deck + - - Load d&eck from server - Wczytaj talię z s&erwera + + Load deck from server + - + Ready to s&tart Go&towy do gry - + S&ideboard unlocked Tal&ia poboczna otwarta - + S&ideboard locked Tal&ia poboczna zamknięta - + Load deck Wczytaj talię - + Error Błąd - + The selected file could not be loaded. Wybrany plik nie mógł zostać wczytany. @@ -686,37 +719,52 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Host: - + + Enter host name + + + + &Port: &Port: - + Player &name: &Nazwa gracza: - + P&assword: H&asło: - + &Save password Zapi&sz hasło - + A&uto connect at start A&utomatycznie połącz przy uruchomieniu - + Connect to server Połącz z serwerem @@ -724,82 +772,92 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan DlgCreateGame - + &Description: &Opis: - + &Password: &Hasło: - + P&layers: &Liczba graczy: - + + Re&member settings + + + + Game type Format - + Only &buddies can join Tylko dla zn&ajomych - + Only &registered users can join Tylko dla &zarejestrowanych użytkowników - + Joining restrictions Ograniczenia dostępu - + &Spectators can watch &Widzowie dozwoleni - + Spectators &need a password to watch Widzowie muszą z&nać hasło - + Spectators can see &hands Widzowie mogą oglądać &ręce - + Spectators can &chat Widzowie mogą korzystać z &czatu - + Spectators Widzowie - + + &Clear + + + + Create game Stwórz grę - + Game information Informacje o grze - + Error Błąd - + Server error. Błąd serwera. @@ -807,96 +865,169 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan DlgCreateToken - + &Name: &Nazwa: - + Token Token - + C&olor: &Kolor: - + white biały - + blue niebieski - + black czarny - + red czerwony - + green zielony - + multicolor wielokolorowy - + colorless bezkolorowy - + &P/T: &S/W: - + &Annotation: &Adnotacja: - + &Destroy token when it leaves the table &Zniszcz tokena kiedy opuści stół - + Token data Dane tokena - + Show &all tokens Pok&azuj wszystkie tokeny - + Show tokens from this &deck Pokazuj tokeny z tej &talii - + Choose token from list Wybierz token z listy: - + Create token Tworzenie tokena + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -989,6 +1120,55 @@ Informacja ta zachowywana jest dla moderatorów i nie jest widoczna dla zbanowan The chosen name conflicts with an existing card or token. Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. + Wybrana nazwa koliduje z istniejącą już kartą lub tokenem. +Upewnij się, że 'Dodatek zawierający tokeny' jest włączony w oknie 'Edytuj listę dodatków' + + + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile @@ -1002,7 +1182,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Show &password protected games - + &Pokaż gry wymagające hasła @@ -1043,7 +1223,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Odśwież @@ -1053,12 +1233,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Wczytaj talię ze schowka - + + Error Błąd - + + Invalid deck list. Nieprawidłowa lista talii. @@ -1071,22 +1253,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Wczytaj talię + + DlgRegister + + + &Host: + &Host: + + + + &Port: + &Port: + + + + Player &name: + &Nazwa gracza: + + + + P&assword: + H&asło: + + + + Password (again): + + + + + Email: + Email: + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + Nie wiadomo + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + Kraj: + + + + Real name: + Prawdziwe imię: + + + + Register to server + Zarejestruj się + + DlgSettings - - - + + + Error Błąd - + Unknown Error loading card database W trakcie wczytywania bazy kart wystąpił nieznany błąd - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1379,7 @@ Może istnieć potrzeba uruchomienia Oracle w celu uaktualnienia bazy kart. Czy chcesz zmienić ustawienia położenia bazy kart? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1396,7 @@ Zwykle można temu zaradzić poprzez uruchomienie narzędzia Oracle i uaktualnie Czy chcesz zmienić ustawienia położenia bazy kart? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1405,7 @@ Would you like to change your database location setting? Czy chcesz ustawić nową lokalizację bazy kart? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1414,7 @@ Would you like to change your database location setting? Czy chcesz ustawić nową lokalizację bazy kart? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1147,7 +1423,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1156,63 +1432,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Ścieżka dostępu do twojego katalogu z taliami jest nieprawidłowa. Czy chcesz wrócić i ustawić poprawną ścieżkę? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Ścieżka dostępu do twojego katalogu z obrazkami jest nieprawidłowa. Czy chcesz wrócić i ustawić poprawną ścieżkę? - + Settings Ustawienia - + General Ogólne - + Appearance Wygląd - + User Interface - + Interfejs - + Deck Editor - + Edytor Talii - + Chat - + Czat - + Sound + Dźwięk + + + + Shortcuts GameSelector - - - C&reate - Utwó&rz - - - - &Join - &Dołącz - @@ -1222,7 +1493,7 @@ Would you like to change your database location setting? - + Error Błąd @@ -1267,37 +1538,47 @@ Would you like to change your database location setting? Twórca tej rozgrywki cię ignoruje. - + Join game Dołącz do gry - + Password: Hasło: - + Please join the respective room first. Proszę najpierw dołączyć do odpowiedniego pokoju. - + Games Gry - + &Filter games - &Filtruj gry + - + C&lear filter - Wyczyść fi&ltrowanie + - + + C&reate + + + + + &Join + + + + J&oin as spectator D&ołącz jako widz @@ -1322,32 +1603,32 @@ Would you like to change your database location setting? <1m ago - + <1 minutę temu <5m ago - + <5 minut temu %1m ago - + %1 min temu 1hr %1m ago - + Godzinę i %1 min temu %1hr ago - + %1 godziny temu 5+ hrs ago - + Ponad 5 godzin temu @@ -1414,97 +1695,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Wymaż/usuń pobrane obrazki - - - - - + + + + + Choose path Wybierz ścieżkę - + Success Powodzenie - + Downloaded card pictures have been reset. Pobrane obrazki zostały wymazane. - + Error Błąd - + One or more downloaded card pictures could not be cleared. Co najmniej jeden z pobranych obrazków nie mógł zostać wymazany. - + Personal settings Ustawienia osobiste - + Language: Język: - + Download card pictures on the fly Ściągaj obrazki na bieżąco - - Download high-quality card pictures - Pobieraj obrazki w wysokiej jakości + + Download card pictures from a custom URL + Pobieraj obrazki kart z zewnętrznego URL - + + Custom Card Download URL: + Adres zewnętrznego URL z obrazkami kart: + + + + Linking FAQ + + + + Paths Ścieżki - + Decks directory: Katalog z taliami: - + Replays directory: Katalog z powtórkami: - + Pictures directory: Katalog z obrazkami: - + Card database: Baza kart: - + Token database: Baza tokenów: - + Picture cache size: Rozmiar pamięci podręcznej dla obrazków: - - + + English Polski (Polish) @@ -1512,56 +1803,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Trwa zbyt wiele równoległych połączeń z twojego adresu. - + Scheduled server shutdown. Planowe wyłączenie serwera. - + Banned by moderator Zbanowany przez moderatora. - + Expected end time: %1 Oczekiwany czas zakończenia: %1 - + This ban lasts indefinitely. Ten ban jest bezterminowy. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Połączenie zakończone - + The server has terminated your connection. Reason: %1 Serwer zakończył twoje połączenie. Przyczyna: %1 - + Scheduled server shutdown Planowe wyłączenie serwera - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1574,304 +1858,483 @@ Wszystkie trwające gry zostaną utracone. Przyczyna zamknięcia: %1 - + Number of players Liczba graczy - + Please enter the number of players. Wprowadź liczbę graczy. - - + + Player %1 Gracz %1 - + Load replay Wczytaj powtórkę - + About Cockatrice O Cockatrice - + Version %1 Wersja %1 - + Translators: Tłumacze: - + Project Manager: + Menadżer projektu: + + + + The server has reached its maximum user capacity, please check back later. - + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Poprzedni menadżerowie projektu: - - Developers: - - - - - Our Developers - - - - - Help Develop! - - - - - Recognition Page - - - - - Help Translate! - - - - - Support: - - - - - Report an Issue - - - - - Suggest an Improvement - - - - - - - - - + Developers: + Twórcy: + + + + Our Developers + Lista współpracowników + + + + Help Develop! + Dołącz do grona twórców! + + + + Recognition Page + Lista tłumaczy + + + + Help Translate! + Pomóż w tłumaczeniu! + + + + Support: + Wsparcie: + + - - - - + Report an Issue + Zgłoś problem + + + + + + + + + + + + + + + + + + + + + Error Błąd - + Server timeout Upłynął limit czasu odpowiedzi serwera - + Incorrect username or password. Please check your authentication information and try again. Nieprawidłowa nazwa użytkownika lub hasło. Sprawdź swoje dane weryfikacyjne i spróbuj ponownie. - + There is already an active session using this user name. Please close that session first and re-login. Istnieje już aktywna sesja dla tej nazwy użytkownika. Zakończ tamtą sesję i zaloguj się ponownie. - + + You are banned until %1. Twój ban trwa do: %1. - + + You are banned indefinitely. Zostałeś zbanowany bezterminowo. - - This server requires user registration. - Ten serwer wymaga rejestracji użytkowników. + + This server requires user registration. Do you want to register now? + - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 Nieznany błąd logowania: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 Błąd gniazda: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Próbujesz połączyć się z nieaktualnym serwerem. Zainstaluj starszą wersję Cockatrice lub wybierz odpowiedni serwer. Lokalna wersja to %1, wersja zdalna to %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Twój klient Cockatrice jest nieaktualny. Uaktualnij Cockatrice do nowszej wersji. Lokalna wersja to %1, wersja zdalna to %2. - + Connecting to %1... %1 — łączenie… - + + Registering to %1 as %2... + + + + Disconnected Rozłączony - + Connected, logging in at %1 Połączenie ustanowione; logowanie jako %1 - - Logged in as %1 at %2 - Zalogowano jako %1 na %2 - - - + &Connect... &Połącz… - + &Disconnect &Rozłącz - + Start &local game... Rozpocznij &lokalną grę… - + &Watch replay... &Obejrzyj powtórkę… - + &Deck editor &Edytor talii - + &Full screen Pełny ekra&n - + + &Register to server... + + + + &Settings... &Ustawienia… - - + + &Exit &Wyjdź - + A&ctions Ak&cje - + &Cockatrice &Cockatrice - + &About Cockatrice &O Cockatrice - + &Help Po&moc - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information + Informacja + + + + Troubleshooting - - A card update is already ongoing. + + F.A.Q. - - Unable to run the card updater: + + Your account has not been activated yet. +You need to provide the activation token received in the activation email - + failed to start. - + crashed. - + timed out. - + write error. - + błąd zapisu. - + read error. - + błąd odczytu. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + nieznany błąd. @@ -2111,62 +2574,62 @@ Lokalna wersja to %1, wersja zdalna to %2. ze strefy wygnania - + the bottom card of %1's library karta z wierzchu biblioteki gracza %1 - + the bottom card of his library dno jego biblioteki - + the bottom card of her library dno jej biblioteki - + from the bottom of %1's library z dna biblioteki gracza %1 - + from the bottom of his library z dna jego biblioteki - + from the bottom of her library z dna jej biblioteki - + the top card of %1's library karta z wierzchu biblioteki gracza %1 - + the top card of his library wierzch jego biblioteki - + the top card of her library wierzch jej biblioteki - + from the top of %1's library z wierzchu biblioteki gracza %1 - + from the top of his library z wierzchu jego biblioteki - + from the top of her library z wierzchu jej biblioteki @@ -2569,304 +3032,304 @@ Lokalna wersja to %1, wersja zdalna to %2. %1 usuwa jeden %2 znacznik z karty %3 (pozostało: %4).%1 usuwa %2 znaczniki (%n) z karty %3 (pozostało: %4).%1 usuwa %2 znaczniki (%n) z karty %3 (pozostało: %4). - + %1 taps her permanents. female %1 tapuje swoje permanenty. - + %1 untaps her permanents. female %1 odtapowuje swoje permanenty. - + %1 taps his permanents. male %1 tapuje swoje permanenty. - + %1 untaps his permanents. male %1 odtapowuje swoje permanenty. - + %1 taps %2. female %1 tapuje kartę %2. - + %1 untaps %2. female %1 odtapowuje kartę %2. - + %1 taps %2. male %1 tapuje kartę %2. - + %1 untaps %2. male %1 odtapowuje kartę %2. - + %1 sets counter %2 to %3 (%4%5). female %1 ustawia licznik (%2) na %3 (%4%5) - + %1 sets counter %2 to %3 (%4%5). male %1 ustawia licznik (%2) na %3 (%4%5) - + %1 sets %2 to not untap normally. female %1 blokuje karcie %2 normalne odtapowywanie. - + %1 sets %2 to not untap normally. male %1 blokuje karcie %2 normalne odtapowywanie. - + %1 sets %2 to untap normally. female %1 umożliwia karcie %2 normalne odtapowywanie. - + %1 sets %2 to untap normally. male %1 umożliwia karcie %2 normalne odtapowywanie. - + %1 sets PT of %2 to %3. female %1 ustala PT karty %2 na %3. - + %1 sets PT of %2 to %3. male %1 ustala PT karty %2 na %3. - + %1 sets annotation of %2 to %3. female %1 ustala adnotację karty %2 na %3. - + %1 sets annotation of %2 to %3. male %1 ustala adnotację karty %2 na %3. + + + %1 is looking at %2. + female + %1 przegląda %2. + %1 is looking at %2. - female + male %1 przegląda %2. + + %1 is looking at the top %n card(s) %2. + female + %1 ogląda kartę z wierzchu %2.%1 przegląda %n kart z wierzchu %2.%1 przegląda %n kart z wierzchu %2. + + + %1 is looking at the top %n card(s) %2. + male + %1 ogląda kartę z wierzchu %2.%1 przegląda %n kart z wierzchu %2.%1 przegląda %n kart z wierzchu %2. + - - %1 is looking at %2. - male - %1 przegląda %2. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1 ogląda kartę z wierzchu %2.%1 przegląda %n kart z wierzchu %2.%1 przegląda %n kart z wierzchu %2. - - - %1 is looking at the top %n card(s) %2. - male - %1 ogląda kartę z wierzchu %2.%1 przegląda %n kart z wierzchu %2.%1 przegląda %n kart z wierzchu %2. + %1 kończy przeglądać %2. %1 stops looking at %2. - female - %1 kończy przeglądać %2. - - - - %1 stops looking at %2. male %1 kończy przeglądać %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 pokazuje kartę %2 graczowi %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 pokazuje kartę %2 graczowi %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 pokazuje kartę %2 graczowi %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 pokazuje kartę %2 graczowi %3. + + + %1 reveals %2. + female + Gracz %1 pokazuje %2. + %1 reveals %2. - female - Gracz %1 pokazuje %2. - - - - %1 reveals %2. male Gracz %1 pokazuje %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 pokazuje losowo kartę %2 %3 graczowi %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 pokazuje losowo kartę %2 %3 graczowi %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 pokazuje losowo kartę %2 %3 graczowi %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 pokazuje losowo kartę %2 %3 graczowi %4. - + %1 randomly reveals %2%3. female %1 pokazuje losowo kartę %2 %3. - + %1 randomly reveals %2%3. male %1 pokazuje losowo kartę %2 %3. - + %1 peeks at face down card #%2. female %1 spogląda na kartę odwróconą koszulką do góry #%2. - + %1 peeks at face down card #%2. male %1 spogląda na kartę odwróconą koszulką do góry #%2. - + %1 peeks at face down card #%2: %3. female %1 spogląda na kartę odwróconą koszulką do góry #%2: %3. - + %1 peeks at face down card #%2: %3. male %1 spogląda na kartę odwróconą koszulką do góry #%2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 pokazuje kartę %2 %3 graczowi %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 pokazuje kartę %2 %3 graczowi %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 pokazuje kartę %2 %3 graczowi %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 odsłania kartę %2 %3 graczowi %4. - + %1 reveals %2%3. female %1 pokazuje %2 %3. - + %1 reveals %2%3. male %1 pokazuje %2 %3. - + %1 is now keeping the top card %2 revealed. %1 od teraz gra z odwróconą kartą z wierzchu %2. - + %1 is not revealing the top card %2 any longer. %1 już nie gra z odwróconą kartą z wierzchu %2. - + It is now %1's turn. female Tura gracza %1. - + It is now %1's turn. male Tura gracza %1. - + a card kartę @@ -2929,12 +3392,12 @@ Lokalna wersja to %1, wersja zdalna to %2. - + ending phase faza końcowa - + untap step krok odtapowania @@ -3044,64 +3507,64 @@ Lokalna wersja to %1, wersja zdalna to %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step krok utrzymania - + draw step krok dobierania - + first main phase pierwsza faza główna - + beginning of combat step krok początku walki - + declare attackers step krok deklaracji atakujących - + declare blockers step krok deklaracji broniących - + combat damage step krok obrażeń bitewnych - + end of combat step krok zakończenia walki - + second main phase druga faza główna - + It is now the %1. Obecnie trwa %1. @@ -3109,62 +3572,74 @@ Lokalna wersja to %1, wersja zdalna to %2. MessagesSettingsPage - + Chat settings Ustawienia czatu - + + Custom alert words + + + + Enable chat mentions Włącz wywołania na czacie - + + Enable mention completer + + + + In-game message macros Makra wiadomości w trakcie gry - - Ignore unregistered users in main chat - Ignoruj niezarejestrowanych użytkowników na czacie głównym. + + Ignore chat room messages sent by unregistered users + - - Ignore chat room messages sent by unregistered users. - Ignoruj wiadomości na czacie od niezarejestrowanych użytkowników. + + Ignore private messages sent by unregistered users + - - Ignore private messages sent by unregistered users. - Ignoruj prywatne wiadomości od niezarejestrowanych użytkowników. + + Enable desktop notifications for private messages + - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color Odwróć kolor tekstu - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) (Kolor w kodzie heksadecymalnym) - + Add message Dodaj wiadomość - + Message: Wiadomość: @@ -3230,336 +3705,337 @@ Lokalna wersja to %1, wersja zdalna to %2. Player - + &View library Przegląda&j bibliotekę - + Move top cards to &graveyard... Przemieść kart&y z wierzchu na cmentarz… - + View &top cards of library... Przeglądaj karty z &wierzchu biblioteki… - + &View graveyard Przegląda&j cmentarz - + &View sideboard Przegląda&j talię poboczną - + Player "%1" Gracz "%1" - - - + + + + &Hand Rę&ka - + &Reveal hand to... Pokaż &rękę innym… - + Reveal r&andom card to... Pok&aż losową kartę innym… - + &Library &Biblioteka - - - + + + &Graveyard C&mentarz - + &Sideboard Ta&lia poboczna - + Red - + Czerwony - + Yellow - + Żółty - + Green - + Zielony - + View top cards of library Przeglądaj karty z wierzchu biblioteki - + Number of cards: Liczba kart: - + &Draw card &Dobierz kartę - + Reveal top cards of library - + Number of cards: (max. %1) - + &View exile Przeglądaj strefę &wygnania - - - + + + &Exile Str&efa wygnania - + Reveal t&op cards to... - + D&raw cards... Dobie&rz karty… - + Take &mulligan Weź &mulligan - + &Shuffle Przeta&suj - + &Counters Li&czniki - + &Untap all permanents Odtap&uj wszystkie permanenty - + R&oll die... Rzuć k&ostką… - + &Create token... Ut&wórz token… - + C&reate another token Utwó&rz kolejny token - + S&ay P&owiedz - + &Move hand to... Prze&mieść rękę na… - - - - + + + + &Top of library Wierzch biblio&teki - - - - + + + + &Bottom of library Dno &biblioteki - + &Move graveyard to... Prze&mieść cmentarz na… - + &Move exile to... Przen&ieś strefę wygnania na… - + Reveal &library to... Pokaż bib&liotekę innym… - + &Always reveal top card Z&awsze graj z odwróconą kartą z wierzchu - + O&pen deck in deck editor &Otwórz talię w edytorze talii - + &Undo last draw Co&fnij ostatnie dobranie karty - + Play top card &face down - + Move top cards to &exile... Przemi&eść karty z wierzchu do strefy wygnania… - + Put top card on &bottom Połóż kartę z wierzc&hu na dnie - + Put bottom card &in graveyard Przen&ieś kartę z dna na na cmentarz - + Cr&eate predefined token Utwórz zd&efiniowany token - + C&ard K&arta - + &All players Wszyscy gr&acze - + &Play &Zagraj - + &Hide Sc&howaj - + Play &Face Down Zagraj k&oszulką do góry - + &Tap &Tapnij - + &Untap Odtap&uj - + Toggle &normal untapping Przełącz &normalne odtapowywanie - + &Flip O&dwróć - + &Peek at card face S&pójrz na awers karty - + &Clone Klo&nuj - + Attac&h to card... Przyłącz do kart&y… - + Unattac&h Odłą&cz - + &Draw arrow... Narysuj wskaźni&k - + &Increase power Zw&iększenie siły - + &Decrease power Re&dukcja siły - + I&ncrease toughness Zwiększe&nie wytrzymałości - + D&ecrease toughness R&edukcja wytrzymałości @@ -3569,22 +4045,22 @@ Lokalna wersja to %1, wersja zdalna to %2. Zwięk&sz siłę i wytrzymałość - + Dec&rease power and toughness Red&ukuj siłę i wytrzymałość - + Set &power and toughness... Ustaw siłę i w&ytrzymałość - + &Set annotation... U&staw adnotację… - + &Add counter (%1) Dod&aj znacznik (%1) @@ -3594,103 +4070,108 @@ Lokalna wersja to %1, wersja zdalna to %2. Zde&jmij znacznik (%1) - + &Set counters (%1)... U&staw znaczniki (%1)… - + Draw cards Dobierz karty - - - - + + + + Number: Liczba: - + Move top cards to grave Przenieś karty z wierzchu na cmentarz - + Move top cards to exile Przenieś karty z wierzchu do strefy wygnania - + Roll die Rzuć kostką - + Number of sides: Liczba ścian: - + Set power/toughness Ustaw siłę/wytrzymałość - + Please enter the new PT: Podaj nową watość SW: - + Set annotation Ustaw adnotację - + Please enter the new annotation: Podaj treść nowej adnotacji: - + Set counters Ustaw znaczniki + + + Cr&eate related card + + QMenuBar - + Services Usługi - + Hide %1 Ukryj %1 - + Hide Others Ukryj pozostałe - + Show All Pokaż wszystkie - + Preferences... Preferencje… - + Quit %1 Zakończ %1 - + About %1 O programie %1 @@ -3698,7 +4179,7 @@ Lokalna wersja to %1, wersja zdalna to %2. QObject - + Cockatrice replays (*.cor) Powtórki Cockatrice (*.cor) @@ -3788,14 +4269,43 @@ Lokalna wersja to %1, wersja zdalna to %2. + Permissions + + + + Players Gracze - + Games Gry + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3838,7 +4348,7 @@ Lokalna wersja to %1, wersja zdalna to %2. Czas do zamknięcia (w minu&tach): - + Shut down server Zamknij serwer @@ -3846,37 +4356,37 @@ Lokalna wersja to %1, wersja zdalna to %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3884,42 +4394,47 @@ Lokalna wersja to %1, wersja zdalna to %2. TabAdmin - + Update server &message Aktualizuj wiado&mość serwera - + &Shut down server Zamknij &serwer - + + &Reload configuration + + + + Server administration functions Pokaż funkcje administracyjne - + &Unlock functions Odblok&uj funkcje - + &Lock functions Zab&lokuj funkcje - + Unlock administration functions Odblokuj funkcje administracyjne - + Do you really want to unlock the administration functions? Czy na pewno chcesz odblokować funkcje administracyjne? - + Administration Administracja @@ -3927,181 +4442,223 @@ Lokalna wersja to %1, wersja zdalna to %2. TabDeckEditor - + &Print deck... &Drukuj talię - + &Close &Zakończ - + &Edit sets... &Edytuj listę dodatków - - &Clear search - &Wyczyść kryteria wyszukiwania + + Filters + - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: &Nazwa talii: - + &Comments: &Komentarze: - + Hash: Hash: - + &New deck &Nowa talia - + &Load deck... &Wczytaj ta&lię… - + &Save deck Zapi&sz talię - + Save deck &as... Zaisz talię j&ako… - + Load deck from cl&ipboard... Wczytaj tal&ię ze schowka… - + Save deck to clip&board Zapisz talię do schow&ka - + &Analyze deck on deckstats.net &Analizuj talię na deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck Dodaj kartę do talii &głównej - + Add card to &sideboard Dodaj kartę do talii po&bocznej - + &Deck Editor - + E&dytor talii - + C&ard Database + B&aza kart + + + + Show/Hide card information - + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - + Witaj - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. - +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + Czołem! Wygląda na to, że uruchamiasz tę wersję Cockatrice po raz pierwszy. +Wszystkie dodatki w bazie kart zostały włączone. +Aby dowiedzieć się więcej na temat zmiany kolejności lub wyłączania poszczególnych dodatków przeczytaj informacje znajdujące się w oknie 'Edytuj listę dodatków'. - - Show card text only - &Pokaż tylko tekst kart - - - + &Remove row Usuń wie&rsz - + &Increment number Zwiększ l&iczbę - + &Decrement number Zmniejsz &liczbę - + Edit &tokens... Edytuj &tokeny… - + Deck: %1 Talia: %1 - + Are you sure? Czy na pewno? - + The decklist has been modified. Do you want to save the changes? Talia została zmodyfikowana. Czy chcesz zachować zmiany? - + Load deck Wczytaj talię - - - + + + Error Błąd - + The deck could not be saved. Talia nie mogła zostać zapisana. - - + + The deck could not be saved. Please check that the directory is writable and try again. Zapisanie talii nie mogło zostać ukończone. Sprawdź, czy włączone są uprawnienia do zapisu w katalogu docelowym i spróbuj ponownie. - + Save deck Zapisz talię @@ -4199,94 +4756,99 @@ Wprowadź nazwę: TabGame - + &Phases &Fazy - + &Game &Gra - + Next &phase Następna &faza - + Next &turn Następna &tura - + &Remove all local arrows Usuń wszystkie &wskaźniki - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information &Informacje o grze - + &Concede &Poddaj grę - + &Leave game &Opuść grę - + C&lose replay &Zamknij powtórkę - + &Say: &Powiedz: - + Concede Poddaj grę - + Are you sure you want to concede this game? Czy na pewno chcesz poddać tę grę? - + Leave game Opuść grę - + Are you sure you want to leave this game? Czy na pewno chcesz zakończyć tę rozgrywkę? - + You are flooding the game. Please wait a couple of seconds. - + Spamujesz. Poczekaj chwilę. - + You have been kicked out of the game. Wyrzucono cię z gry. - - Replay %1: %2 - Powtórka %1: %2 - - - - Game %1: %2 - Gra %1: %2 + + REPLAY + @@ -4330,54 +4892,54 @@ Wprowadź nazwę: TabReplays - + Local file system Lokalny system plików - + Server replay storage Powtórki na serwerze - + Watch replay Obejrzyj powtórkę - - + + Delete Usuń - + Download replay Pobierz powtórkę - + Toggle expiration lock Wł./Wył. ochronę przed automatycznym usunięciem - + Delete local file Usuń plik lokalny - + Are you sure you want to delete "%1"? Czy na pewno chcesz usunąć "%1"? - + Delete remote replay Usuń przechowywaną powtórkę - + Are you sure you want to delete the replay of game %1? Czy na pewno chcesz usunąć powtórkę gry %1? @@ -4390,47 +4952,47 @@ Wprowadź nazwę: TabRoom - + &Say: &Powiedz: - + Chat Czat - + &Room P&okój - + &Leave room Op&uść pokój - + &Clear chat Wyczyść &czat - + Chat Settings... Ustawienia czatu… - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. Spamujesz. Poczekaj chwilę. @@ -4446,15 +5008,25 @@ Wprowadź nazwę: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4471,175 +5043,307 @@ Wprowadź nazwę: Account - + Konto UserContextMenu - + User &details &Dane użytkownika - + Private &chat - + Show this user's &games Wyświetl &gry użytkownika - + Add to &buddy list &Dodaj do listy znajomych - + Remove from &buddy list &Usuń z listy znajomych - + Add to &ignore list Dodaj do listy &ignorowanych - + Remove from &ignore list Usuń z listy &ignorowanych - + Kick from &game Wyrzuć z &gry - + Ban from &server Zbanuj z &serwera - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games Gry gracza %1 + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Informacje o użytkowniku - + Real name: Imię: - - Gender: - Płeć: + + Pronouns: + - + Location: Lokacja: - + User level: Rodzaj użytkownika: - + Account Age: Wiek konta: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator Administrator - + Moderator Moderator - + Registered user Zarejestrowany użytkownik - - + + Unregistered user Niezarejestrowany użytkownik - + Unknown Nieznany - + Year Rok - + Years Lata - + Day Dzień - + Days Dni + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Ogólne ustawienia interfejsu - + Enable notifications in taskbar Włącz &powiadomienia na pasku zadań - + Notify in the taskbar for game events while you are spectating Powiadomienia na pasku zadań dla gier, które &obserwujesz - + &Double-click cards to play them (instead of single-click) Zagrywaj karty po&dwójnym kliknięciem (zamiast pojedynczym) - + &Play all nonlands onto the stack (not the battlefield) by default Karty nie będące lądami zagrywaj domyślnie na stos (zamiast na &pole bitwy) - + + Annotate card text on tokens + + + + Animation settings Ustawienia animacji - + &Tap/untap animation Animacja &tapowania/odtapowania @@ -4647,22 +5351,22 @@ Wprowadź nazwę: UserList - + Users connected to server: %1 - + Users in this room: %1 Użytkownicy w tym pokoju: %1 - + Buddies online: %1 / %2 Znajomi online: %1 / %2 - + Ignored users online: %1 / %2 Ignorowani użytkownicy online: %1 / %2 @@ -4672,39 +5376,56 @@ Wprowadź nazwę: Enable all sets - + Włącz wszystkie dodatki Disable all sets - + Wyłącz wszystkie dodatki Move selected set up Przesuć wybrany dodatek w górę + + + Move selected set to the top + + Move selected set down Przesuń wybrany dodatek w dół - - - Move selected set to top - Przesuń wybrany dodatek na początek - - Move selected set to bottom - Przesuń wybrany dodatek na koniec + Move selected set to the bottom + - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4713,12 +5434,12 @@ Disabled sets will still be used for loading images. Edytuj listę dodatków - + Success Powodzenie - + The sets database has been saved successfully. Zapisywanie bazy dodatków zakończone powodzeniem. @@ -4746,4 +5467,561 @@ Disabled sets will still be used for loading images. widok sterty + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_pt.ts b/cockatrice/translations/cockatrice_pt.ts index 383eb960..1f7e61c3 100644 --- a/cockatrice/translations/cockatrice_pt.ts +++ b/cockatrice/translations/cockatrice_pt.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... Definir &marcador... - + Set counter Definir marcador - + New value for counter '%1': Novo valor para o marcador '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Zona das imagens de fundo - + Hand background: Imagem de fundo da Mão: - + Stack background: Imagem de fundo da Pilha: - + Table background: Imagem de fundo da Mesa: - + Player info background: Imagem de fundo da informação do jogador: - + Card back: - + Card rendering Rendering da carta - + Display card names on cards having a picture Mostrar o nome em cartas com imagem - + Scale cards on mouse over - + Hand layout Disposição da Mão - + Display hand horizontally (wastes space) Mostrar mão horizontalmente (desperdiça espaço) - + Enable left justification - + Table grid layout Esquema da mesa - + Invert vertical coordinate Inverter coordenada vertical - + Minimum player count for multi-column layout: Número mínimo de kogadores para layout com múltiplas colunas: - - - - - + + + + + Choose path Escolher directório @@ -107,99 +107,104 @@ BanDialog - + ban &user name Banir nome de &utilizador - + ban &IP address Banir endereço de &IP - + + ban client I&D + + + + Ban type Tipo de banimento - + &permanent ban Banimento &permanente - + &temporary ban Banimento &temporário - + &Days: &Dias: - + &Hours: &Horas: - + &Minutes: &Minutos: - + Duration of the ban Duração do banimento - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Por favor introduza o motivo do banimento. Isto apenas é guardado para os moderadores e não é visível para a pessoa banida. - + Please enter the reason for the ban that will be visible to the banned person. Por favor introduza o motivo do banimento que será visível à pessoa banida. - + &OK &OK - + &Cancel &Cancelar - + Ban user from server Banir utilizador do servidor - + Error Erro - - You have to select a name-based or IP-based ban, or both. - Tem de seleccionar um banimento baseado no nome, no IP ou ambos. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + Há novas expansões - + %1 new set(s) have been found in the card database. Do you want to enable them? - + %1 novas expansões estão disponíveis na base de dados. Quer activá-las? @@ -230,30 +235,53 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban P/R + + CardFrame + + + Image + + + + + Description + Descrição + + + + Both + + + CardInfoText - + Name: Nome: - + Mana cost: Custo de mana: - - Card type: + + Color(s): - + + Card type: + Tipo de carta: + + + P / T: P / D: - + Loyalty: Lealdade: @@ -276,27 +304,32 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban Mostrar informação completa - + Name: Nome: - + Mana cost: Custo de Mana: - + + Color(s): + + + + Card type: Tipo de carta: - + P / T: P / R: - + Loyalty: Lealdade: @@ -560,12 +593,12 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban DeckEditorSettingsPage - + Nothing is here... yet - + General Geral @@ -573,17 +606,17 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban DeckListModel - + Number Número - + Card Carta - + Price Preço @@ -605,42 +638,42 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban DeckViewContainer - - Load &local deck - Carregar deck l&ocal + + Load local deck + Carregar deck local - - Load d&eck from server - Carregar deck do &servidor + + Load deck from server + Carregar deck do servidor - + Ready to s&tart &Pronto para começar - + S&ideboard unlocked S&ideboard desbloqueado - + S&ideboard locked S&ideboard bloqueado - + Load deck Carregar deck - + Error Erro - + The selected file could not be loaded. O ficheiro seleccionado não pôde ser carregado. @@ -686,37 +719,52 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Servidor: - + + Enter host name + + + + &Port: &Porta: - + Player &name: &Nome do jogador: - + P&assword: P&assword: - + &Save password &Guardar password - + A&uto connect at start Conectar a&utomaticamente no arranque - + Connect to server Ligar ao servidor @@ -724,82 +772,92 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban DlgCreateGame - + &Description: &Descrição: - + &Password: &Password: - + P&layers: &Jogadores: - + + Re&member settings + + + + Game type Tipo de jogo - + Only &buddies can join Apenas &amigos podem entrar - + Only &registered users can join Apenas utilizadores &registados podem entrar - + Joining restrictions Restrições para ligar - + &Spectators can watch E&spectadores podem ver - + Spectators &need a password to watch Espectadores &necessitam de palavra-passe - + Spectators can see &hands &Espectadores podem ver as mãos - + Spectators can &chat Espectadores podem c&onversar - + Spectators Espectadores - + + &Clear + + + + Create game Criar jogo - + Game information Informação do jogo - + Error Erro - + Server error. Erro do servidor. @@ -807,96 +865,169 @@ Isto apenas é guardado para os moderadores e não é visível para a pessoa ban DlgCreateToken - + &Name: &Nome: - + Token Ficha - + C&olor: C&or: - + white branco - + blue azul - + black preto - + red vermelho - + green verde - + multicolor multicolor - + colorless incolor - + &P/T: &P/R: - + &Annotation: &Nota: - + &Destroy token when it leaves the table &Destruir ficha quando ela deixar a mesa - + Token data Informação de ficha - + Show &all tokens Mostrar &todas as fichas - + Show tokens from this &deck Mostrar &fichas deste deck - + Choose token from list Escolha ficha da lista - + Create token Criar ficha + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + Erro + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + Nome real: + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Refrescar @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Carregar deck da memória - + + Error Erro - + + Invalid deck list. Lista de deck inválida. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Carregar deck + + DlgRegister + + + &Host: + &Servidor: + + + + &Port: + &Porta: + + + + Player &name: + &Nome do jogador: + + + + P&assword: + P&assword: + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + Nome real: + + + + Register to server + + + DlgSettings - - - + + + Error Erro - + Unknown Error loading card database Erro desconhecido ao carregar a base de dados das cartas - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1378,7 @@ Poderá necessitar de correr o Oracle para actualizar a base de dados. Gostaria de alterar a localização da base de dados? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1395,7 @@ Pode ser reparado correndo o Oracle de novo para actualizar a base de dados das Gostaria de alterar a localização da base de dados? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1404,7 @@ Would you like to change your database location setting? Gostaria de alterar a localização da base de dados? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1413,7 @@ Would you like to change your database location setting? Gostaria de alterar a localização da base de dados? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1151,7 +1426,7 @@ Por favor, envie um ticket para http://github.com/Cockatrice/Cockatrice/issues c Gostaria de mudar a definição da localização da sua base de dados? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1160,63 +1435,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? O directório do seu deck é inválido. Gostaria de voltar atrás e corrigir o directório? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? O directório das imagens das cartas é inválido. Gostaria de voltar atrás e corrigir o directório? - + Settings Definições - + General Geral - + Appearance Aparência - + User Interface Interface do utilizador - + Deck Editor Editor de Decks - + Chat Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - &Criar - - - - &Join - &Entrar - @@ -1226,7 +1496,7 @@ Would you like to change your database location setting? - + Error Erro @@ -1271,37 +1541,47 @@ Would you like to change your database location setting? Você está a ser ignorado pelo criador deste jogo. - + Join game Entrar no jogo - + Password: Password: - + Please join the respective room first. Por favor entre na sala respectiva primeiro. - + Games Jogos - + &Filter games - &Filtrar jogos + - + C&lear filter - &Limpar filtros + - + + C&reate + + + + + &Join + + + + J&oin as spectator Entrar como &espectador @@ -1341,7 +1621,7 @@ Would you like to change your database location setting? 1hr %1m ago - + há 1 hora %1 min @@ -1418,97 +1698,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Limpar/reiniciar Imagens descarregadas, - - - - - + + + + + Choose path Escolher directório - + Success Sucesso - + Downloaded card pictures have been reset. Imagens de cartas descarregadas reiniciadas. - + Error Erro - + One or more downloaded card pictures could not be cleared. Uma ou mais imagens de cartas descarregadas não podem ser apagadas. - + Personal settings Defenições pessoais - + Language: Língua: - + Download card pictures on the fly Baixar a imagem das cartas ao passar - - Download high-quality card pictures - Descarregar imagens em alta qualidade + + Download card pictures from a custom URL + - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths Directórios - + Decks directory: Directório dos decks: - + Replays directory: Directório de replays: - + Pictures directory: Directório das imagens: - + Card database: Base de dados das cartas: - + Token database: Base de dados das fichas: - + Picture cache size: Tamanho da cache de imagens: - - + + English Português (Portuguese) @@ -1516,57 +1806,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Há demasiadas ligações concorrentes do seu endereço. - + Scheduled server shutdown. Encerramento do servidor agendado. - + Banned by moderator Banido por moderador - + Expected end time: %1 Tempo previsto para o final:%1 - + This ban lasts indefinitely. Este banimento dura indefinidamente. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - Nome de utilizador inválido. -Apenas A-Z, a-z, 0-9, _, ., e - são permitidos. - - - + Connection closed Ligação terminada - + The server has terminated your connection. Reason: %1 O servidor terminou a sua ligação. Motivo: %1 - + Scheduled server shutdown Encerramento do servidor agendado - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1575,304 +1857,483 @@ Todos os jogos a decorrer serão perdidos. Motivo para o encerramento: %1 - + Number of players Número de jogadores - + Please enter the number of players. Por favor introduza o número de jogadores. - - + + Player %1 Jogador %1 - + Load replay Carregar replay - + About Cockatrice Sobre o Cockatrice - + Version %1 Versão %1 - + Translators: Tradutores: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + Nome de utilizador invalido. + + + + You have been logged out due to logging in at another location. + + + + + + Success + Sucesso + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error Erro - + Server timeout Tempo do servidor esgotado - + Incorrect username or password. Please check your authentication information and try again. Nome de utilizador ou palavra-passe incorrecta. Por favor verifique as suas informações e tente de novo. - + There is already an active session using this user name. Please close that session first and re-login. Já existe uma sessão activa com este nome de utilizador. Por favor termine essa sessão e volte a ligar-se. - + + You are banned until %1. Está banido até %1. - + + You are banned indefinitely. Está banido indefinidamente. - - This server requires user registration. - Este servidor só permite acesso a utilizadores registados. + + This server requires user registration. Do you want to register now? + - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 Erro de login desconhecido:%1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + Apenas A-Z, a-z, 0-9, _, ., e - são permitidos. + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 Erro de ligação:%1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Está a tentar ligar-se a um servidor obsoleto. Por favor faça downgrade à sua versão do Cockatrice ou ligue-se a servidor adequado. Versão local é %1, versão remota é %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. A sua versão do Cockatrice é obsoleta. Por favor actualize-a. Versão local é %1, versão remota é %2. - + Connecting to %1... Ligando a %1... - + + Registering to %1 as %2... + + + + Disconnected Desligado - + Connected, logging in at %1 Conectado, logando em %1 - - Logged in as %1 at %2 - - - - + &Connect... &Ligar... - + &Disconnect &Desligar - + Start &local game... Começar &jogo local... - + &Watch replay... &Ver replay... - + &Deck editor &Editor de decks - + &Full screen Ecrã &inteiro - + + &Register to server... + + + + &Settings... &Configurações... - - + + &Exit &Sair - + A&ctions - + &Cockatrice &Cockatrice - + &About Cockatrice S&obre o Cockatrice - + &Help &Ajuda - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information + Informação + + + + Troubleshooting - - A card update is already ongoing. + + F.A.Q. - - Unable to run the card updater: + + Your account has not been activated yet. +You need to provide the activation token received in the activation email - + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + erro desconhecido. @@ -2112,62 +2573,62 @@ Versão local é %1, versão remota é %2. vindo do exílio - + the bottom card of %1's library a carta do fundo do grimório de %1 - + the bottom card of his library a carta do fundo do seu grimório - + the bottom card of her library a carta do fundo do seu grimório - + from the bottom of %1's library do fundo do grimório de %1 - + from the bottom of his library do fundo do seu grimório - + from the bottom of her library do fundo do seu grimório - + the top card of %1's library a carta do topo do grimório de %1 - + the top card of his library a carta do topo do seu grimório - + the top card of her library a carta do topo do seu grimório - + from the top of %1's library do topo do grimório de %1 - + from the top of his library do topo do seu grimório - + from the top of her library do topo do seu grimório @@ -2570,304 +3031,304 @@ Versão local é %1, versão remota é %2. %1 remove %n %2 marcador de %3 (agora com %4).%1 remove %n %2 marcadores de %3 (agora com %4). - + %1 taps her permanents. female %1 vira as suas permanentes. - + %1 untaps her permanents. female %1 desvira as suas permanentes. - + %1 taps his permanents. male %1 vira as suas permanentes. - + %1 untaps his permanents. male %1 desvira as suas permanentes. - + %1 taps %2. female %1 vira %2. - + %1 untaps %2. female %1 desvira %2. - + %1 taps %2. male %1 vira %2. - + %1 untaps %2. male %1 desvira %2. - + %1 sets counter %2 to %3 (%4%5). female %1 altera o número de marcadores %2 para %3(%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 altera o número de marcadores %2 para %3(%4%5). - + %1 sets %2 to not untap normally. female %1 define %2 para não desvirar normalmente. - + %1 sets %2 to not untap normally. male %1 define %2 para não desvirar normalmente. - + %1 sets %2 to untap normally. female %1 define %2 para desvirar normalmente. - + %1 sets %2 to untap normally. male %1 define %2 para desvirar normalmente. - + %1 sets PT of %2 to %3. female %1 define o P/R de %2 como %3. - + %1 sets PT of %2 to %3. male %1 define o P/R de %2 como %3. - + %1 sets annotation of %2 to %3. female %1 coloca uma nota de %2 em%3. - + %1 sets annotation of %2 to %3. male %1 coloca uma nota de %2 em%3. + + + %1 is looking at %2. + female + %1 está a olhar para %2. + %1 is looking at %2. - female + male %1 está a olhar para %2. + + %1 is looking at the top %n card(s) %2. + female + %1 está a olhar para as %n carta(s) do topo %2. + + + %1 is looking at the top %n card(s) %2. + male + %1 está a olhar para as %n carta(s) do topo %2. + - - %1 is looking at %2. - male - %1 está a olhar para %2. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1 está a olhar para as %n carta(s) do topo %2. - - - %1 is looking at the top %n card(s) %2. - male - %1 está a olhar para as %n carta(s) do topo %2. + %1 pára de olhar para %2. %1 stops looking at %2. - female - %1 pára de olhar para %2. - - - - %1 stops looking at %2. male %1 pára de olhar para %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 revela %2 a %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 revela %2 a %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 revela %2 a %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 revela %2 a %3. + + + %1 reveals %2. + female + %1 revela %2. + %1 reveals %2. - female - %1 revela %2. - - - - %1 reveals %2. male %1 revela %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 revela aleatoreamente %2%3. a %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 revela aleatoreamente %2%3. a %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 revela aleatoreamente %2%3. a %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 revela aleatoreamente %2%3. a %4. - + %1 randomly reveals %2%3. female %1 revela aleatoreamente %2%3. - + %1 randomly reveals %2%3. male %1 revela aleatoreamente %2%3. - + %1 peeks at face down card #%2. female %1 espreita a carta virada para baixo #%2. - + %1 peeks at face down card #%2. male %1 espreita a carta virada para baixo #%2. - + %1 peeks at face down card #%2: %3. female %1 espreita a carta virada para baixo #%2: %3. - + %1 peeks at face down card #%2: %3. male %1 espreita a carta virada para baixo #%2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 revela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 revela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 revela %2%3 a %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 revela %2%3 a %4. - + %1 reveals %2%3. female %1 revela %2%3. - + %1 reveals %2%3. male %1 revela %2%3. - + %1 is now keeping the top card %2 revealed. %1 está agora a manter a carta do topo %2 revelada. - + %1 is not revealing the top card %2 any longer. %1 já não está a manter a carta do topo %2 revelada. - + It is now %1's turn. female É agora o turno de %1. - + It is now %1's turn. male É agora o turno de %1. - + a card uma carta @@ -2930,12 +3391,12 @@ Versão local é %1, versão remota é %2. - + ending phase Fase Final - + untap step Etapa de Desvirar @@ -2979,12 +3440,12 @@ Versão local é %1, versão remota é %2. %1 puts %2%3 into her graveyard. - + %1 coloca %2%3 no seu cemitério. %1 puts %2%3 into his graveyard. - + %1 coloca %2%3 no seu cemitério. @@ -3024,85 +3485,85 @@ Versão local é %1, versão remota é %2. %1 places %2 %3 counter(s) on %4 (now %5). female - + %1 coloca %2 %3 marcadores em %4 (agora com %5). %1 places %2 %3 counter(s) on %4 (now %5). male - + %1 coloca %2 %3 marcadores em %4 (agora com %5). %1 removes %2 %3 counter(s) from %4 (now %5). female - + %1 remove %2 %3 marcadores de %4 (agora com %5). %1 removes %2 %3 counter(s) from %4 (now %5). male - + %1 remove %2 %3 marcadores de %4 (agora com %5). - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step Etapa de Manutenção - + draw step Etapa de Compra - + first main phase 1ª Fase Principal (pré-combate) - + beginning of combat step Etapa de Início de Combate - + declare attackers step Etapa de Declaração de Atacantes - + declare blockers step Etapa de Declaração de Bloqueadores - + combat damage step Etapa de Dano de Combate - + end of combat step Etapa de Fim de Combate - + second main phase 2ª Fase Principal (pós-combate) - + It is now the %1. É agora a %1. @@ -3110,62 +3571,74 @@ Versão local é %1, versão remota é %2. MessagesSettingsPage - + Chat settings Definições do Chat - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat - Ignorar utilizadores não registados no chat principal + + Ignore chat room messages sent by unregistered users + - - Ignore chat room messages sent by unregistered users. - Ignorar mensagens de chat enviadas por utilizadores não registados + + Ignore private messages sent by unregistered users + - - Ignore private messages sent by unregistered users. - Ignorar mensagens privadas enviadas por utilizadores não registados + + Enable desktop notifications for private messages + - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color Inverter cor do texto - - Enable desktop notifications for private messages. - Permitir notificações de mensagens privadas no Ambiente de Trabalho - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message Adicionar mensagem - + Message: Mensagem: @@ -3231,336 +3704,337 @@ Versão local é %1, versão remota é %2. Player - + &View library &Ver grimório - + Move top cards to &graveyard... Mover as cartas do topo para o &cemitério... - + View &top cards of library... Ver as cartas do &topo do grimório... - + &View graveyard &Ver cemitério - + &View sideboard &Ver sideboard - + Player "%1" Jogador "%1" - - - + + + + &Hand &Mão - + &Reveal hand to... &Mostrar mão a... - + Reveal r&andom card to... - + &Library &Grimório - - - + + + &Graveyard &Cemitério - + &Sideboard &Sideboard - + Red Vermelho - + Yellow Amarelo - + Green Verde - + View top cards of library Ver as cartas do topo do grimório - + Number of cards: Número de cartas: - + &Draw card &Comprar carta - + Reveal top cards of library - + Mostrar as cartas do topo do grimório - + Number of cards: (max. %1) - + &View exile &Ver exílio - - - + + + &Exile &Exílio - + Reveal t&op cards to... - + D&raw cards... C&omprar cartas... - + Take &mulligan Fazer &mulligan - + &Shuffle &Baralhar - + &Counters &Marcadores - + &Untap all permanents &Desvirar topas as permanentes - + R&oll die... &Lançar dado... - + &Create token... Criar fic&ha... - + C&reate another token Cr&iar outra ficha - + S&ay &Dizer - + &Move hand to... &Mover mão para... - - - - + + + + &Top of library &Topo do grimório - - - - + + + + &Bottom of library &Fundo do grimório - + &Move graveyard to... &Mover cemitério para... - + &Move exile to... - + Reveal &library to... - + &Mostrar grimório a... - + &Always reveal top card &Revelar sempre carta do topo - + O&pen deck in deck editor &Abrir deck no editor de decks - + &Undo last draw Desfa&zer a última compra - + Play top card &face down - + Move top cards to &exile... Mover as cartas do topo para o &exílio... - + Put top card on &bottom Colocar carta do topo no &fundo - + Put bottom card &in graveyard - + Cr&eate predefined token Criar fic&ha predefinida - + C&ard C&arta - + &All players Todos os &jogadores - + &Play &Jogar - + &Hide Esco&nder - + Play &Face Down - + &Tap &Virar - + &Untap Desv&irar - + Toggle &normal untapping A&lterar desvirar normalmente - + &Flip Vol&tar - + &Peek at card face &Espreitar a face da carta - + &Clone Copi&ar - + Attac&h to card... Ane&xar a carta... - + Unattac&h De&sanexar - + &Draw arrow... Desen&har seta... - + &Increase power &Aumentar poder - + &Decrease power &Diminuir poder - + I&ncrease toughness A&umentar resistência - + D&ecrease toughness Di&minuir resistência @@ -3570,22 +4044,22 @@ Versão local é %1, versão remota é %2. Aumen&tar poder e resistência - + Dec&rease power and toughness Dimin&uir poder e resistência - + Set &power and toughness... Definir &poder e resistência... - + &Set annotation... Colocar &nota... - + &Add counter (%1) Adicionar &marcador (%1) @@ -3595,103 +4069,108 @@ Versão local é %1, versão remota é %2. &Remover marcador (%1) - + &Set counters (%1)... &Denifir marcadores (%1)... - + Draw cards Comprar cartas - - - - + + + + Number: Número: - + Move top cards to grave Mover as cartas to topo para o cemitério - + Move top cards to exile Mover as cartas to topo para o exílio - + Roll die Lançar dado - + Number of sides: Número de faces: - + Set power/toughness Definir poder/resistência - + Please enter the new PT: Por favor introduza o novo P/R: - + Set annotation Colocar nota - + Please enter the new annotation: Por favor introduza a nova nota: - + Set counters Definir marcadores + + + Cr&eate related card + + QMenuBar - + Services Serviços - + Hide %1 Ocultar %1 - + Hide Others Ocultar outros - + Show All Mostrar tudo - + Preferences... Preferências... - + Quit %1 Sair %1 - + About %1 Cerca %1 @@ -3699,7 +4178,7 @@ Versão local é %1, versão remota é %2. QObject - + Cockatrice replays (*.cor) Replays do Cockatrice (*.cor) @@ -3789,14 +4268,43 @@ Versão local é %1, versão remota é %2. + Permissions + + + + Players Jogadores - + Games Jogos + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3839,7 +4347,7 @@ Versão local é %1, versão remota é %2. &Tempo até ao encerramento (minutos): - + Shut down server Encerrar servidor @@ -3847,37 +4355,37 @@ Versão local é %1, versão remota é %2. SoundSettingsPage - + Choose path - + Escolher directório - + Enable &sounds - + Permitir &sons - + Path to sounds directory: - + Caminho para o directório dos sons: - + Test system sound engine - + Sound settings - + Definições de Som - + Master volume requires QT5 - + Master volume @@ -3885,42 +4393,47 @@ Versão local é %1, versão remota é %2. TabAdmin - + Update server &message &Actualizar mensagem do servidor - + &Shut down server &Encerrar servidor - + + &Reload configuration + + + + Server administration functions Funções do administrador do servidor - + &Unlock functions &Desbloquear funções - + &Lock functions &Bloquear funções - + Unlock administration functions Desbloquear funções de administração - + Do you really want to unlock the administration functions? Quer mesmo desbloquear as funçõesde administração? - + Administration Administração @@ -3928,181 +4441,221 @@ Versão local é %1, versão remota é %2. TabDeckEditor - + &Print deck... &Imprimir deck... - + &Close &Fechar - + &Edit sets... &Editar expansões... - - &Clear search - &Limpar pesquisa + + Filters + - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: &Nome do deck: - + &Comments: &Comentários: - + Hash: Hash: - + &New deck &Novo deck - + &Load deck... &Carregar deck... - + &Save deck &Guardar deck - + Save deck &as... G&uardar deck como... - + Load deck from cl&ipboard... Carregar dec&k da memória... - + Save deck to clip&board Guardar deck na &memória - + &Analyze deck on deckstats.net Anali&zar o deck em deckstats.net - + Open custom image folder Abrir pasta de imagens personalizadas - + + Open custom sets folder + + + + Add card to &maindeck Adicionar carta ao &maindeck - + Add card to &sideboard Adicionar carta ao &sideboard - + &Deck Editor %Editor de Decks - + C&ard Database + &Base de dados das cartas + + + + Show/Hide card information - + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - Visualizar apenas texto das cartas - - - + &Remove row &Remover linha - + &Increment number &Aumentar o número - + &Decrement number &Diminuir o número - + Edit &tokens... Editar &fichas... - + Deck: %1 Deck:%1 - + Are you sure? Tem a certeza? - + The decklist has been modified. Do you want to save the changes? A lista foi modificada. Gostaria de guardar as alterações? - + Load deck Carregar deck - - - + + + Error Erro - + The deck could not be saved. O deck não pode ser guardado. - - + + The deck could not be saved. Please check that the directory is writable and try again. O deck não pode ser guardado. Por favor confirme se é possível escrever do directório e tente de novo. - + Save deck Guardar deck @@ -4200,94 +4753,99 @@ Por favor introduza um nome: TabGame - + &Phases Fa&ses - + &Game &Jogo - + Next &phase Próxima &fase - + Next &turn Próximo &turno - + &Remove all local arrows &Remover todas as setas locais - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information &Informação do jogo - + &Concede &Conceder - + &Leave game Sair do &jogo - + C&lose replay &Fechar replay - + &Say: &Dizer: - + Concede Conceder - + Are you sure you want to concede this game? Tem a certeza que deseja conceder este jogo? - + Leave game Sair do jogo - + Are you sure you want to leave this game? Tem a certeza que deseja sair deste jogo? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. Foi expulso do jogo. - - Replay %1: %2 - Replay %1: %2 - - - - Game %1: %2 - Jogo %1: %2 + + REPLAY + @@ -4295,7 +4853,7 @@ Por favor introduza um nome: Private &chat - + Chat &privado @@ -4331,54 +4889,54 @@ Por favor introduza um nome: TabReplays - + Local file system Ficheiros locais - + Server replay storage Armazenamento de replays no servidor - + Watch replay Ver replay - - + + Delete Apagar - + Download replay Download replay - + Toggle expiration lock Alternar bloqueio de expiração - + Delete local file Apagar ficheiro local - + Are you sure you want to delete "%1"? Tem a certeza que deseja apagar %1? - + Delete remote replay Apagar replay remoto - + Are you sure you want to delete the replay of game %1? Tem a certeza que deseja apagar o replay do jogo %1? @@ -4391,47 +4949,47 @@ Por favor introduza um nome: TabRoom - + &Say: &Dizer: - + Chat Chat - + &Room &Sala - + &Leave room &Abandonar a sala - + &Clear chat &Limpar chat - + Chat Settings... Definições do chat... - + mentioned you. mencionou-o. - + Click to view Clicar para ver - + You are flooding the chat. Please wait a couple of seconds. Estás a inundar o chat .Por favor aguarde alguns segundos. @@ -4447,15 +5005,25 @@ Por favor introduza um nome: TabSupervisor - + Are you sure? Tem a certeza? - + There are still open games. Are you sure you want to quit? Ainda estão a decorrer alguns jogos. Tem a certeza que quer sair? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4478,169 +5046,301 @@ Por favor introduza um nome: UserContextMenu - + User &details Detalhes do &utilizador - + Private &chat Chat &privado - + Show this user's &games Mostrar os &jogos deste utilizador - + Add to &buddy list Adicionar à &lista de amigos - + Remove from &buddy list Remover da lista de &amigos - + Add to &ignore list Adicionar a lista a i&gnorar - + Remove from &ignore list Remover da lista a &ignorar - + Kick from &game Expulsar do &jogo - + Ban from &server Banir do &servidor - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games jogos de %1 + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Informação do utilizador - + Real name: Nome real: - - Gender: - Sexo: + + Pronouns: + - + Location: Localização: - + User level: Nível de utilizador: - + Account Age: - + + Edit + Editar + + + + Change password + + + + + Change avatar + + + + Administrator Administrador - + Moderator Moderador - + Registered user Utilizador registado - - + + Unregistered user Utilizador não registado - + Unknown Desconhecido - + Year Ano - + Years Anos - + Day Dia - + Days Dias + + + + + Information + Informação + + + + User information updated. + + + + + + + + + + + + Error + Erro + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Configurações gerais da interface - + Enable notifications in taskbar Permitir notificações na barra de tarefas - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) Clicar &duas vezes nas cartas para as jogar (ao invés de clicar apenas uma vez) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings Configurações de Animações - + &Tap/untap animation Animação de &virar/desvirar @@ -4648,22 +5348,22 @@ Por favor introduza um nome: UserList - + Users connected to server: %1 Utilizadores online: %1 - + Users in this room: %1 Utilizadores nesta sala:%1 - + Buddies online: %1 / %2 Amigos online: %1 / %2 - + Ignored users online: %1 / %2 Utilizadores ignorados online %1 / %2 @@ -4685,27 +5385,44 @@ Por favor introduza um nome: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4714,12 +5431,12 @@ Disabled sets will still be used for loading images. Editar expansões - + Success Sucesso - + The sets database has been saved successfully. A base de dados das expansões foi guardada com sucesso @@ -4747,4 +5464,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_pt_BR.ts b/cockatrice/translations/cockatrice_pt_BR.ts index b295cfe3..14390bfe 100644 --- a/cockatrice/translations/cockatrice_pt_BR.ts +++ b/cockatrice/translations/cockatrice_pt_BR.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... Definir &marcador... - + Set counter Definir marcador - + New value for counter '%1': Novo valor para o marcador '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Imagens de fundo das zonas - + Hand background: Imagem de fundo da mão: - + Stack background: Imagem de fundo da pilha: - + Table background: Imagem de fundo da mesa: - + Player info background: Imagem de fundo do jogador: - + Card back: Parte de trás da carta: - + Card rendering Renderização do card - + Display card names on cards having a picture Mostrar o nome dos cards nos cards que tem imagem - + Scale cards on mouse over Redimensionar cartas ao passar o mouse - + Hand layout Layout da mão - + Display hand horizontally (wastes space) Mostrar a mão na horizontal (desperdiça espaço) - + Enable left justification Habilitada justificação à esquerda - + Table grid layout Layout do campo de batalha - + Invert vertical coordinate Inverter a coordenada vertical - + Minimum player count for multi-column layout: Contador mínimo de jogadores para o layout de multicolunas - - - - - + + + + + Choose path Escolher caminho @@ -107,97 +107,102 @@ BanDialog - + ban &user name banido usuário &user - + ban &IP address banido endereço &IP - + + ban client I&D + + + + Ban type Tipo de banimento - + &permanent ban banido &permanentemente - + &temporary ban Banido &temporariamente - + &Days: &Dias: - + &Hours: &Horas: - + &Minutes: &Minutos: - + Duration of the ban Duração do banimento - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Por favor, entre com o motivo do banimento. Isto será visto somente por moderadores e não pode ser visto pela pessoa banida. - + Please enter the reason for the ban that will be visible to the banned person. Por favor, entre com o motivo para o banimento que será visível pela pessoa banida. - + &OK &OK - + &Cancel &Cancelar - + Ban user from server Banir usuário do servidor - + Error Erro - - You have to select a name-based or IP-based ban, or both. - Você tem selecionado um nome ou IP banido ou ambos. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + Novas expansões encontradas - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid P/R + + CardFrame + + + Image + Imagem + + + + Description + Descrição + + + + Both + Ambos + + CardInfoText - + Name: Nome: - + Mana cost: Custo de mana: - + + Color(s): + + + + Card type: Tipo de card: - + P / T: P / R: - + Loyalty: Lealdade: @@ -276,27 +304,32 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid Mostrar informação completa - + Name: Nome: - + Mana cost: Custo de mana: - + + Color(s): + + + + Card type: Tipo de card: - + P / T: P / R: - + Loyalty: Lealdade: @@ -560,12 +593,12 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid DeckEditorSettingsPage - + Nothing is here... yet Nada aqui... ainda - + General General @@ -573,17 +606,17 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid DeckListModel - + Number Número - + Card Card - + Price Preço @@ -605,42 +638,42 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid DeckViewContainer - - Load &local deck - Carregar dec&k local + + Load local deck + Carregar deck local - - Load d&eck from server - Carregar deck do &servidor + + Load deck from server + Carregar deck do servidor - + Ready to s&tart &Pronto para começar - + S&ideboard unlocked Sideboard liberado - + S&ideboard locked Sideboard travado - + Load deck Carregar deck - + Error Erro - + The selected file could not be loaded. O arquivo selecionado não pode ser carregado @@ -686,37 +719,52 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Servidor: - + + Enter host name + + + + &Port: &Porta: - + Player &name: Nome do &jogador: - + P&assword: S&enha: - + &Save password &salvar senha - + A&uto connect at start &Começar automaticamente no início - + Connect to server Conectar ao servidor @@ -724,82 +772,92 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid DlgCreateGame - + &Description: &Descrição: - + &Password: S&enha: - + P&layers: &Jogadores: - + + Re&member settings + + + + Game type Tipo de jogo - + Only &buddies can join Apenas ami&gos podem entrar - + Only &registered users can join Apenas usuários re&gistrados podem entrar - + Joining restrictions Restrições para entrar - + &Spectators can watch &Espectadores podem assistir - + Spectators &need a password to watch &Espectadores precisam de uma senha para assistir - + Spectators can see &hands Espectadores podem ver &mãos - + Spectators can &chat Visitantes podem c&onversar - + Spectators Visitantes - + + &Clear + + + + Create game Criar jogo - + Game information Informação de jogo - + Error Erro - + Server error. Erro do servidor. @@ -807,96 +865,169 @@ Isto será visto somente por moderadores e não pode ser visto pela pessoa banid DlgCreateToken - + &Name: &Nome: - + Token Ficha - + C&olor: C&or: - + white branco - + blue azul - + black preto - + red vermelho - + green verde - + multicolor multicolorido - + colorless incolor - + &P/T: &P/R: - + &Annotation: &Nota: - + &Destroy token when it leaves the table Destruir a &ficha quando ela sair do campo de batalha - + Token data Informação de ficha - + Show &all tokens Mostre &todas fichas - + Show tokens from this &deck Mostre fichas deste &deck - + Choose token from list Mostre fichas da lista - + Create token Criar ficha + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + Erro + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + Email: + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + País: + + + + Undefined + Indefinido + + + + Real name: + Nome real: + + + + Edit user profile + + + DlgFilterGames @@ -1002,7 +1181,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Show &password protected games - + Mostrar &senha de jogos protegidos @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Atualizar @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Carregar deck da área de transferência - + + Error Erro - + + Invalid deck list. Lista de deck inválida. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Carregar deck + + DlgRegister + + + &Host: + &Servidor: + + + + &Port: + &Porta: + + + + Player &name: + Nome do &jogador: + + + + P&assword: + S&enha: + + + + Password (again): + + + + + Email: + Email: + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + Indefinido + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + País: + + + + Real name: + Nome real: + + + + Register to server + Registrar ao servidor: + + DlgSettings - - - + + + Error Erro - + Unknown Error loading card database Erro desconhecido ao carregar bando de dados de cartas - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1378,7 @@ Você pode precisar rodar o Oracle novamente para atualizar o banco de dados de Você gostaria de mudar o local da configuração de seu banco de dados? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1395,7 @@ Usualmente isto pode ser consertado rodando novamente o Oracle para atualizar se Você gostaria de alterar o local da configuração de seu banco de dados? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1404,7 @@ Would you like to change your database location setting? Você gostaria de alterar seu local de configuração de seu banco de dados? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1413,7 @@ Would you like to change your database location setting? Você gostaria de alterar seu local de configuração de banco de dados? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1151,7 +1426,7 @@ Por favor, crie um ticket em http://github.com/Cockatrice/Cockatrice/issues com Você gostaria de alterar seu local de configuração do banco de dados? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1164,63 +1439,58 @@ Por favor, crie um ticket em http://github.com/Cockatrice/Cockatrice/issues Você gostaria de alterar seu local de configuração do banco de dados? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? O caminho para a sua pasta de decks é inválido. Você gostaria de voltar e corrigir o caminho? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? O caminho para a sua pasta de imagens de cards é inválido. Você gostaria de voltar e corrigir o caminho? - + Settings Configurações - + General Geral - + Appearance Aparência - + User Interface Interface do usuário - + Deck Editor Editor de &decks - + Chat Conversa - + Sound + Som + + + + Shortcuts GameSelector - - - C&reate - &Criar - - - - &Join - &Entrar - @@ -1230,7 +1500,7 @@ Você gostaria de alterar seu local de configuração do banco de dados? - + Error Erro @@ -1275,37 +1545,47 @@ Você gostaria de alterar seu local de configuração do banco de dados?Você está sendo ignorado pelo criador deste jogo. - + Join game Entrar no jogo - + Password: Senha: - + Please join the respective room first. Por favor, entre na respectiva sala primeiro. - + Games Jogos - + &Filter games - &Filtrar jogos + - + C&lear filter - &Limpar filtros + - + + C&reate + + + + + &Join + + + + J&oin as spectator E&ntrar como visitante @@ -1330,32 +1610,32 @@ Você gostaria de alterar seu local de configuração do banco de dados? <1m ago - + <1min atrás <5m ago - + <5min atrás %1m ago - + %1min atrás 1hr %1m ago - + 1h %1min atrás %1hr ago - + %1h atrás 5+ hrs ago - + 5+ horas atrás @@ -1422,97 +1702,107 @@ Você gostaria de alterar seu local de configuração do banco de dados? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Reiniciar/Limpar imagens baixadas - - - - - + + + + + Choose path Escolher caminho - + Success Sucesso - + Downloaded card pictures have been reset. Imagens de cartas baixadas foram reiniciadas. - + Error Erro - + One or more downloaded card pictures could not be cleared. Um ou mais imagens baixadas não puderam ser limpas. - + Personal settings Configurações pessoais - + Language: Língua: - + Download card pictures on the fly Baixar a imagem dos cards em tempo real - - Download high-quality card pictures - Baixar imagens de cartas em alta qualidade + + Download card pictures from a custom URL + Baixar a imagem de cartas de uma URL diferente - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths Caminhos - + Decks directory: Pasta de decks: - + Replays directory: Diretório de replays: - + Pictures directory: Pasta de imagens: - + Card database: Banco de dados de cartas: - + Token database: Banco de dados de fichas: - + Picture cache size: Tamanho imagem: - - + + English Português do Brasil (Brazilian Portuguese) @@ -1520,56 +1810,49 @@ Você gostaria de alterar seu local de configuração do banco de dados? MainWindow - + There are too many concurrent connections from your address. Há conexões concorrentes demais vinds do seu endereço. - + Scheduled server shutdown. Servidor de eventos fora do ar. - + Banned by moderator Banido pelo moderador - + Expected end time: %1 Esperado tempo para fim: %1 - + This ban lasts indefinitely. Este banido indefinidamente. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Conexão fechada - + The server has terminated your connection. Reason: %1 O servidor terminou sua conexão. Razão: %1 - + Scheduled server shutdown Servidor de eventos fora do ar - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1580,304 +1863,483 @@ Todos os jogos serão perdidos. Motivo para desligar: %1 - + Number of players Número de jogadores - + Please enter the number of players. Por favor, entre o número de jogadores. - - + + Player %1 Jogador %1 - + Load replay Carregar replay - + About Cockatrice Sobre o Cockatrice - + Version %1 Versão %1 - + Translators: Tradutores: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + Usuário inválido + + + + You have been logged out due to logging in at another location. + + + + + + Success + Sucesso + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error Erro - + Server timeout Tempo esgotado do servidor - + Incorrect username or password. Please check your authentication information and try again. Usuário ou senha incorretos. Por favor, verifique suas informações de autenticação e tente novamente. - + There is already an active session using this user name. Please close that session first and re-login. Já existe uma sessão ativa usando este nome de usuário. Por favor, feche a sessão primeiro e logue novamente. - + + You are banned until %1. Você está banido até %1. - + + You are banned indefinitely. Você está banido indefinidamente. - - This server requires user registration. - Este servidor requer registro de usuário. + + This server requires user registration. Do you want to register now? + - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 Erro de login desconhecido: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 Erro de ligação:%1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Você está tentando conectar a um servidor obsoleto. Por favor, faça um downgrade na versão do seu Cockatrice ou conecte-se ao servidor correto. A versão local é %1 e a versão remota é %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. A versão do seu Cockatrice é obsoleta. Por favor, atualize a sua versão. A versão local é %1 e a versão remota é %2. - + Connecting to %1... Conectando a %1... - + + Registering to %1 as %2... + + + + Disconnected Desconectado - + Connected, logging in at %1 Conectado, logando em %1 - - Logged in as %1 at %2 - Logado em %1 como %2 - - - + &Connect... &Conectar... - + &Disconnect &Desconectar - + Start &local game... Iniciar jogo &local... - + &Watch replay... &Assistir replay... - + &Deck editor Editor de &decks - + &Full screen Tela &cheia - + + &Register to server... + &Registrar ao servidor: + + + &Settings... &Configurações... - - + + &Exit &Sair - + A&ctions Açõ&es - + &Cockatrice &Cockatrice - + &About Cockatrice So&bre o Cockatrice - + &Help &Ajuda - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information + Informação + + + + Troubleshooting - - A card update is already ongoing. + + F.A.Q. - - Unable to run the card updater: + + Your account has not been activated yet. +You need to provide the activation token received in the activation email - + failed to start. - + crashed. - + timed out. - + write error. - + erro na escrita. - + read error. - + erro na leitura. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + erro desconhecido. @@ -2117,62 +2579,62 @@ A versão local é %1 e a versão remota é %2. vindo do exílio - + the bottom card of %1's library a carta do fundo do grimório de %1 - + the bottom card of his library o card do fundo do seu grimório - + the bottom card of her library a carta do fundo de seu grimório - + from the bottom of %1's library do fundo do grimório de %1 - + from the bottom of his library vindo do fundo do seu grimório - + from the bottom of her library do fundo de seu grimório - + the top card of %1's library a carta do topo do grimório de %1 - + the top card of his library o card do topo do seu grimório - + the top card of her library a carta do topo de seu grimório - + from the top of %1's library do topo do grimório de %1 - + from the top of his library vindo do topo do seu grimório - + from the top of her library do topo de seu grimório @@ -2575,121 +3037,121 @@ A versão local é %1 e a versão remota é %2. %1 remove um %2 marcadores de %3 (agora %4).%1 remove %n %2 marcadores de %3 (agora %4). - + %1 taps her permanents. female %1 vira suas permanentes. - + %1 untaps her permanents. female %1 desvira suas permanentes. - + %1 taps his permanents. male %1 vira suas permanentes. - + %1 untaps his permanents. male %1 desvira suas permanentes. - + %1 taps %2. female %1 vira %2 - + %1 untaps %2. female %1 desvira %2. - + %1 taps %2. male %1 vira %2. - + %1 untaps %2. male %1 desvira %2. - + %1 sets counter %2 to %3 (%4%5). female %1 define marcadores %2 para %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 define marcadores %2 para %3 (%4%5). - + %1 sets %2 to not untap normally. female %1 define %2 não desviram normalmente. - + %1 sets %2 to not untap normally. male %1 define %2 não desviram normalmente. - + %1 sets %2 to untap normally. female %1 define %2 desviram normalmente. - + %1 sets %2 to untap normally. male %1 define %2 desviram normalmente. - + %1 sets PT of %2 to %3. female %1 define P/R de %2 para %3. - + %1 sets PT of %2 to %3. male %1 define P/R de %2 para %3. - + %1 sets annotation of %2 to %3. female %1 altera a nota de %2 para %3. - + %1 sets annotation of %2 to %3. male %1 altera a nota de %2 para %3. - + %1 is looking at %2. female %1 está olhando em %2. - + %1 is looking at %2. male %1 está olhando em %2. @@ -2705,174 +3167,174 @@ A versão local é %1 e a versão remota é %2. %1 está olhando o topo de %2.%1 está olhando %n cartas do topo de %2. - + %1 stops looking at %2. female %1 parou de olhar em %2. - + %1 stops looking at %2. male %1 parou de olhar em %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 revela %2 para %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 revela %2 para %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 revela %2 para %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 revela %2 para %3. + + + %1 reveals %2. + female + %1 revela %2. + %1 reveals %2. - female - %1 revela %2. - - - - %1 reveals %2. male %1 revela %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 aleatoriamente revela %2 %3 para %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 aleatoriamente revela %2 %3 para %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 aleatoriamente revela %2 %3 para %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 aleatoriamente revela %2 %3 para %4. - + %1 randomly reveals %2%3. female %1 aleatoriamente revela %2 %3. - + %1 randomly reveals %2%3. male %1 aleatoriamente revela %2 %3. - + %1 peeks at face down card #%2. female %1 olha a carta face-para-baixo #%2. - + %1 peeks at face down card #%2. male %1 olha a carta face-para-baixo #%2. - + %1 peeks at face down card #%2: %3. female %1 olha a carta face-para-baixo #%2: %3. - + %1 peeks at face down card #%2: %3. male %1 olha a carta face-para-baixo #%2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 revela %2 %3 para %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 revela %2 %3 para %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 revela %2 %3 para %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 revela %2 %3 para %4. - + %1 reveals %2%3. female %1 revela %2 %3. - + %1 reveals %2%3. male %1 revela %2 %3. - + %1 is now keeping the top card %2 revealed. %1 está mantendo as carta do topo %2 reveladas. - + %1 is not revealing the top card %2 any longer. %1 não está mais revelando as cartas do topo %2. - + It is now %1's turn. female Agora é o turno de %1. - + It is now %1's turn. male Agora é o turno de %1. - + a card um card @@ -2935,12 +3397,12 @@ A versão local é %1 e a versão remota é %2. - + ending phase fase final - + untap step etapa de desvirar @@ -2984,12 +3446,12 @@ A versão local é %1 e a versão remota é %2. %1 puts %2%3 into her graveyard. - + %1 põe %2%3 em seu cemitério. %1 puts %2%3 into his graveyard. - + %1 põe %2%3 em seu cemitério. @@ -3050,64 +3512,64 @@ A versão local é %1 e a versão remota é %2. %1 remove %2 %3 marcador(es) em %4 (agora %5). - + %1 is looking at the top %2 card(s) %3. female %1 está olhando o topo do %2 carta(s) %3. - + %1 is looking at the top %2 card(s) %3. male %1 está olhando o topo do %2 carta(s) %3. - + upkeep step etapa de manutenção - + draw step etapa de compra - + first main phase primeira fase principal - + beginning of combat step etapa de início de combate - + declare attackers step etapa de declaracão de atacantes - + declare blockers step etapa de declaração de bloqueadores - + combat damage step etapa de dano de combate - + end of combat step etapa de fim de combate - + second main phase segunda fase principal - + It is now the %1. Agora é a %1. @@ -3115,62 +3577,74 @@ A versão local é %1 e a versão remota é %2. MessagesSettingsPage - + Chat settings Configurações de Conversa - + + Custom alert words + + + + Enable chat mentions Habilitar menções em conversa - + + Enable mention completer + + + + In-game message macros Mensagem no jogo - - Ignore unregistered users in main chat - Ignorar usuários não registrados na Conversa principal + + Ignore chat room messages sent by unregistered users + - - Ignore chat room messages sent by unregistered users. - Ignorar sala de conversa por mensagens enviadas por usuários não registrados. + + Ignore private messages sent by unregistered users + - - Ignore private messages sent by unregistered users. - Ignorar mensagens privadas enviadas por usuários não registrados. + + Enable desktop notifications for private messages + - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color Inverter cor do texto - - Enable desktop notifications for private messages. - Habilitar notificações para mensagens privadas. - - - + Enable desktop notification for mentions. Habilitar notificações no desktop - + + (Color is hexadecimal) (Cor em hexadecimal) - + Add message Adicionar mensagem - + Message: Mensagem: @@ -3236,336 +3710,337 @@ A versão local é %1 e a versão remota é %2. Player - + &View library &Ver grimório - + Move top cards to &graveyard... Mover os cards do topo para o ce&mitério... - + View &top cards of library... Ver os cards do to&po do grimório... - + &View graveyard V&er cemitério - + &View sideboard &Ver sideboard - + Player "%1" Jogador "%1" - - - + + + + &Hand &Mão - + &Reveal hand to... &Revelar carta para... - + Reveal r&andom card to... Revelar uma carta &aleatória para... - + &Library &Grimório - - - + + + &Graveyard &Cemitério - + &Sideboard &Sideboard - + Red - + Vermelho - + Yellow - + Amarelho - + Green - + Verde - + View top cards of library Ver os cards do topo do grimório - + Number of cards: Número de cards: - + &Draw card Co&mprar card - + Reveal top cards of library - + Revelar os cards do topo do grimório - + Number of cards: (max. %1) - + &View exile &Ver exílio - - - + + + &Exile &Exílio - + Reveal t&op cards to... - + Revelar os cards do t&opo para... - + D&raw cards... Comprar car&ds... - + Take &mulligan Pedir mu&lligan - + &Shuffle &Embaralhar - + &Counters &Marcadores - + &Untap all permanents Des&virar todos as permanentes - + R&oll die... &Jogar dado... - + &Create token... Criar fich&a... - + C&reate another token Criar &outra ficha - + S&ay &Falar - + &Move hand to... &Mover mão para... - - - - + + + + &Top of library &Topo do grimório - - - - + + + + &Bottom of library &Fundo do grimório - + &Move graveyard to... &Mover cemitério para... - + &Move exile to... &Mover exílio para... - + Reveal &library to... Revelar &grimório para... - + &Always reveal top card &Sempre revelar a carta do topo - + O&pen deck in deck editor &Abrir no editor de decks - + &Undo last draw Desfa&zer última compra - + Play top card &face down - + Move top cards to &exile... Mover os cards do topo para o e&xílio... - + Put top card on &bottom Colocar o card do topo no &fundo - + Put bottom card &in graveyard Colocar a carta do fundo no cemitério - + Cr&eate predefined token Cr&iar uma ficha predefinida - + C&ard C&ard - + &All players To&dos os jogadores - + &Play &Jogar - + &Hide &Ocultar - + Play &Face Down Jogar com a &Face-para-Baixo - + &Tap &Virar - + &Untap &Desvirar - + Toggle &normal untapping &Trocar o modo de desvirar - + &Flip Virar a &face - + &Peek at card face &Olhar a face da carta - + &Clone Clo&nar - + Attac&h to card... Ane&xar na carta... - + Unattac&h De&sanexar - + &Draw arrow... &Comprar de... - + &Increase power Au&mentar poder - + &Decrease power Dimi&nuir poder - + I&ncrease toughness A&umentar resistência - + D&ecrease toughness D&iminuir resistência @@ -3575,22 +4050,22 @@ A versão local é %1 e a versão remota é %2. Aumen&tar poder e resistência - + Dec&rease power and toughness Diminuir p&oder e resistência - + Set &power and toughness... Definir poder e resis&tência... - + &Set annotation... Definir &nota... - + &Add counter (%1) &Adicionar contador (%1) @@ -3600,103 +4075,108 @@ A versão local é %1 e a versão remota é %2. &Remover marcador (%1) - + &Set counters (%1)... &Definir contadores (%1)... - + Draw cards Comprar cards - - - - + + + + Number: Número: - + Move top cards to grave Mover os cards do topo para o cemitério - + Move top cards to exile Mover os cards do topo para o exílio - + Roll die Jogar dado - + Number of sides: Número de lados: - + Set power/toughness Alterar poder/resistência - + Please enter the new PT: Por favor, entre com o novo P/R: - + Set annotation Alterar nota - + Please enter the new annotation: Por favor, entre com a nova nota: - + Set counters Alterar marcadores + + + Cr&eate related card + + QMenuBar - + Services Serviços - + Hide %1 Esconder %1 - + Hide Others Esconder outros - + Show All Mostrar tudo - + Preferences... Preferências - + Quit %1 Sair %1 - + About %1 Sobre %1 @@ -3704,7 +4184,7 @@ A versão local é %1 e a versão remota é %2. QObject - + Cockatrice replays (*.cor) Cockatrice replays (*.cor) @@ -3794,14 +4274,43 @@ A versão local é %1 e a versão remota é %2. + Permissions + + + + Players Jogadores - + Games Jogos + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3844,7 +4353,7 @@ A versão local é %1 e a versão remota é %2. &Tempo até o desligamento (minutos): - + Shut down server Servidor desligado @@ -3852,37 +4361,37 @@ A versão local é %1 e a versão remota é %2. SoundSettingsPage - + Choose path - + Escolher caminho - + Enable &sounds - + Habilitar &sons - + Path to sounds directory: - + Caminho para diretório de sons - + Test system sound engine - + Testar sistema de som - + Sound settings - + Configurações de som - + Master volume requires QT5 - + Master volume @@ -3890,42 +4399,47 @@ A versão local é %1 e a versão remota é %2. TabAdmin - + Update server &message &Atualizar mensagem do servidor - + &Shut down server &Servidor desligado - + + &Reload configuration + + + + Server administration functions Funções do administrador do servidor - + &Unlock functions &Desbloquear funções - + &Lock functions &Bloquear funções - + Unlock administration functions Desbloquear funções do administrador - + Do you really want to unlock the administration functions? Você quer mesmo desbloquear as funções do administrador? - + Administration Administração @@ -3933,181 +4447,221 @@ A versão local é %1 e a versão remota é %2. TabDeckEditor - + &Print deck... &Imprimir deck... - + &Close &Fechar - + &Edit sets... &Editar expansões... - - &Clear search - &Limpar busca + + Filters + - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: Nome do &deck: - + &Comments: &Comentários: - + Hash: Hash: - + &New deck &Novo deck - + &Load deck... &Carregar deck - + &Save deck &Salvar deck - + Save deck &as... Salvar deck &como... - + Load deck from cl&ipboard... Carregar deck da área de &transferência... - + Save deck to clip&board Salvar deck para a área de &transferência - + &Analyze deck on deckstats.net &Analisar deck no deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck Adicionar carta ao &maindeck - + Add card to &sideboard Adicionar carta ao &sideboard - + &Deck Editor - + Editor de &Decks - + C&ard Database + Banco de dados de &Cartas + + + + Show/Hide card information - + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - Mostrar somente texto da carta - - - + &Remove row &Remover linha - + &Increment number &Aumentar número - + &Decrement number &Diminuir número - + Edit &tokens... Editar ficha - + Deck: %1 Deck: %1 - + Are you sure? Você tem certeza? - + The decklist has been modified. Do you want to save the changes? O deck foi modificado. Você deseja salvar as alterações? - + Load deck Carregar deck - - - + + + Error Erro - + The deck could not be saved. O deck não pode ser salvo. - - + + The deck could not be saved. Please check that the directory is writable and try again. O deck não pôde ser salvo. Por favor, verifique se o diretório não é somente leitura e tente novamente. - + Save deck Salvar deck @@ -4205,94 +4759,99 @@ Por favor, entre um nome: TabGame - + &Phases &Etapas - + &Game &Jogo - + Next &phase Próxima &etapa - + Next &turn Próximo &turno - + &Remove all local arrows &Apagar todas as setas locais - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information &Informação de jogo - + &Concede &Conceder - + &Leave game &Sair do jogo - + C&lose replay &Fechar replay - + &Say: &Falar: - + Concede Conceder - + Are you sure you want to concede this game? Você tem certeza que deseja conceder este jogo? - + Leave game Sair do jogo - + Are you sure you want to leave this game? Você tem certeza que deseja sair deste jogo? - + You are flooding the game. Please wait a couple of seconds. Você está 'flodando' o chat (jogo). Por favor, espere alguns segundos. - + You have been kicked out of the game. Você foi 'chutado' do jogo. - - Replay %1: %2 - Replay %1: %2 - - - - Game %1: %2 - Jogo %1: %2 + + REPLAY + @@ -4336,54 +4895,54 @@ Por favor, entre um nome: TabReplays - + Local file system Sistema de arquivos local - + Server replay storage Servidor de armazenamento de replays - + Watch replay Assistir replay - - + + Delete Excluir - + Download replay Baixar replay - + Toggle expiration lock Trocar expiração da trava - + Delete local file Excluir arquivo local - + Are you sure you want to delete "%1"? ^Você tem certeza que quer excluir "%1"? - + Delete remote replay Excluir replay remoto - + Are you sure you want to delete the replay of game %1? Você tem certeza que quer excluir o replay do jogo %1? @@ -4396,47 +4955,47 @@ Por favor, entre um nome: TabRoom - + &Say: &Falar: - + Chat Chat - + &Room &Sala - + &Leave room S&air da sala - + &Clear chat Limpar conversa - + Chat Settings... Configurações de Conversa - + mentioned you. mencionou você. - + Click to view Clique para ver - + You are flooding the chat. Please wait a couple of seconds. Você está flodando o chat. Por favor, espere alguns segundos. @@ -4452,15 +5011,25 @@ Por favor, entre um nome: TabSupervisor - + Are you sure? Você tem certeza? - + There are still open games. Are you sure you want to quit? Ainda existem jogos abertos. Você tem certeza que deseja sair? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4483,169 +5052,301 @@ Por favor, entre um nome: UserContextMenu - + User &details &Detalhes do usuário - + Private &chat Conversa privada - + Show this user's &games Mostrar este usuário de jogo - + Add to &buddy list Adicionar à &lista de amigos - + Remove from &buddy list Remover da li&sta de amigos - + Add to &ignore list Adicionar à li&sta dos ignorados - + Remove from &ignore list Remover da lista dos i&gnorados - + Kick from &game C&hutar do jogo - + Ban from &server Ban&ir do servidor - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games Jogos de %1 + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Informação do usuário - + Real name: Nome real: - - Gender: - Sexo: + + Pronouns: + - + Location: Localização: - + User level: Nível do usuário: - + Account Age: Idade conta: - + + Edit + Editar + + + + Change password + + + + + Change avatar + + + + Administrator Administrador - + Moderator Moderador - + Registered user Usuário registrado - - + + Unregistered user Usuário não registrado - + Unknown Desconhecido - + Year Ano - + Years Anos - + Day Dia - + Days Dias + + + + + Information + Informação + + + + User information updated. + + + + + + + + + + + + Error + Erro + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Configurações gerais de interface - + Enable notifications in taskbar Habilitar notificações na barra de tarefas - + Notify in the taskbar for game events while you are spectating Notificar na barra de tarefas para eventos do jogo enquanto você está como espectador - + &Double-click cards to play them (instead of single-click) &Duplo clique nos cards para jogá-los (ao invés de clique simples) - + &Play all nonlands onto the stack (not the battlefield) by default &Jogar todos não terrenos na pilha (não campo de batalha) por padrão - + + Annotate card text on tokens + + + + Animation settings Configurações de animação - + &Tap/untap animation Animação de &virar/desvirar @@ -4653,22 +5354,22 @@ Por favor, entre um nome: UserList - + Users connected to server: %1 - + Users in this room: %1 Usuários nesta sala: %1 - + Buddies online: %1 / %2 Amigos online: %1 / %2 - + Ignored users online: %1 / %2 Usuários ignorados online: %1 / %2 @@ -4690,27 +5391,44 @@ Por favor, entre um nome: Move selected set up Mover o selecionado para cima + + + Move selected set to the top + Mover o selecionado para o topo + Move selected set down Mover o selecionado para baixo - - - Move selected set to top - Mover o selecionado para o topo - - Move selected set to bottom + Move selected set to the bottom Mover o selecionado para o fundo - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4719,12 +5437,12 @@ Disabled sets will still be used for loading images. Editar expansões - + Success Sucesso - + The sets database has been saved successfully. Estas edições foram salvas com sucesso. @@ -4752,4 +5470,561 @@ Disabled sets will still be used for loading images. visualização da pilha + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_ru.ts b/cockatrice/translations/cockatrice_ru.ts index c029ba9d..4840c745 100644 --- a/cockatrice/translations/cockatrice_ru.ts +++ b/cockatrice/translations/cockatrice_ru.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... &Установить жетоны... - + Set counter Установить жетоны - + New value for counter '%1': Количество жетонов '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Фоновые изображения - + Hand background: Фон руки - + Stack background: Фон стека - + Table background: Фон стола - + Player info background: - + Фон информации о игроке - + Card back: Рубашка - + Card rendering Отрисовка карт - + Display card names on cards having a picture Отображать название карты поверх изображения - + Scale cards on mouse over - + Увеличивать карты при наведении мыши - + Hand layout Расположение руки - + Display hand horizontally (wastes space) Отбражать руку горизонтально - + Enable left justification - + Table grid layout Сетка - + Invert vertical coordinate Инвертировать вертикальные координаты - + Minimum player count for multi-column layout: Минимальное количество игроков для столбчатого расположения: - - - - - + + + + + Choose path Выберите путь @@ -107,97 +107,102 @@ BanDialog - + ban &user name - + ban &IP address - - Ban type - - - - - &permanent ban - - - - - &temporary ban + + ban client I&D + Ban type + + + + + &permanent ban + + + + + &temporary ban + + + + &Days: - + &Hours: - + &Minutes: - + Duration of the ban - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Пожалуйста, назовите причину бана. Это необходимо для модераторов и не будет прочитано забаненным игроком. - + Please enter the reason for the ban that will be visible to the banned person. - + Пожалуйста, назовите причину бана, которая будет видна забаненному игроку. - + &OK &Ок - + &Cancel &Отмена - + Ban user from server Забанить игрока на этом сервере - + Error Ошибка - - You have to select a name-based or IP-based ban, or both. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. CardDatabase - + New sets found - + Найдены новые издания - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ This is only saved for moderators and cannot be seen by the banned person.Сила/Выносливость: + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: Название: - + Mana cost: Мановая стоимость: - + + Color(s): + + + + Card type: Тип: - + P / T: Сила/Выносливость: - + Loyalty: Преданность: @@ -276,29 +304,34 @@ This is only saved for moderators and cannot be seen by the banned person.Показывать полную информацию - + Name: Название: - + Mana cost: Мановая стоимость: - + + Color(s): + + + + Card type: Тип: - + P / T: - Сила/Защита: + Сила/Выносливость: - + Loyalty: - + Преданность @@ -306,7 +339,7 @@ This is only saved for moderators and cannot be seen by the banned person. &Power / toughness - &Сила / защита + &Сила / выносливость @@ -560,12 +593,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - + Nothing is here... yet - + Здесь ничего нет... пока что - + General Основные @@ -573,17 +606,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Number Номер - + Card Название - + Price Цена @@ -599,48 +632,48 @@ This is only saved for moderators and cannot be seen by the banned person. The reply from the server could not be parsed. - + Ответ от сервера не может быть распознан DeckViewContainer - - Load &local deck - Загрузить &колоду с диска + + Load local deck + - - Load d&eck from server - Загрузить к&олоду с сервера + + Load deck from server + - + Ready to s&tart &Готов - + S&ideboard unlocked - - - - - S&ideboard locked - + Сайдборд разблокирован + S&ideboard locked + Сайдборд заблокирован + + + Load deck Загрузить колоду - + Error Ошибка - + The selected file could not be loaded. Выбранный файл не может быть загружен @@ -686,37 +719,52 @@ This is only saved for moderators and cannot be seen by the banned person. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Хост: - + + Enter host name + + + + &Port: &Порт: - + Player &name: &Ник: - + P&assword: П&ароль: - + &Save password &Сохранить пароль - + A&uto connect at start - + Подключаться при старте - + Connect to server Соединение @@ -724,82 +772,92 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateGame - + &Description: &Подпись: - + &Password: &Пароль: - + P&layers: &Количество игроков: - + + Re&member settings + + + + Game type Формат игры - + Only &buddies can join Только для &своих - + Only &registered users can join Только для &зарег. пользователей - + Joining restrictions Ограничения - + &Spectators can watch & Зрители могут смотреть игру - + Spectators &need a password to watch Зрителям & необходим пароль для просмотра игры - + Spectators can see &hands Зрители могут видеть& руки игроков - + Spectators can &chat Позволить зрителям &комментировать - + Spectators Зрители - + + &Clear + + + + Create game Создать игру - + Game information Информация об игре - + Error Ошибка - + Server error. Ошибка сервера. @@ -807,107 +865,180 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateToken - + &Name: &Название: - + Token Фишка - + C&olor: &Цвет: - + white белый - + blue синий - + black черный - + red красный - + green зеленый - + multicolor многоцветный - + colorless бесцветный - + &P/T: &Сила/Защита: - + &Annotation: &Пометить... - + &Destroy token when it leaves the table &Уничтожить фишку, когда она покинет поле битвы - + Token data - + Show &all tokens Показать & все фишки - + Show tokens from this &deck Показать фишки из указанной & колоды - + Choose token from list Выбрать фишку из списка - + Create token Создать фишку + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens &Name: - + &Название C&olor: - + &Цвет @@ -937,7 +1068,7 @@ This is only saved for moderators and cannot be seen by the banned person. multicolor - + мультицветный @@ -947,12 +1078,12 @@ This is only saved for moderators and cannot be seen by the banned person. &P/T: - + &С/В: &Annotation: - + &Примечание: @@ -963,22 +1094,22 @@ This is only saved for moderators and cannot be seen by the banned person. Add token - + Добавить токен Remove token - + Удалить фишку Edit tokens - + Редактировать фишки Please enter the name of the token: - + Введите имя токена: @@ -992,73 +1123,123 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames Show &unavailable games - + Показывать &недоступные игры Show &password protected games - + Показывать игры с &паролем Game &description: - + &Описание игры &Creator name: - + Имя &создателя &Game types - + Форматы &игры at &least: - + не &менее at &most: - + не &более Maximum player count - + Макс. количество игроков Filter games - + Фильтровать игры DlgLoadDeckFromClipboard - + &Refresh &Обновить Load deck from clipboard - Взять колоду из буфера + Загрузить колоду из буфера - + + Error Ошибка - + + Invalid deck list. Неверный деклист. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Загрузить колоду + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error Ошибка - + Unknown Error loading card database - + Неизвестная ошибка во время загрузки - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1097,7 +1372,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1108,21 +1383,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1131,7 +1406,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1140,63 +1415,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Ваши колоды отсутствуют в указанной папке. Вернуться и задать правильный путь? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Изображения карт не найдены. Вернуться и задать правильный путь? - + Settings Настройки - + General Основные - + Appearance Внешний вид - + User Interface - + Пользовательский интерфейс - + Deck Editor - + Редактор колод - + Chat - + Чат - + Sound + Звук + + + + Shortcuts GameSelector - - - C&reate - С&оздать - - - - &Join - &Присоединиться - @@ -1206,7 +1476,7 @@ Would you like to change your database location setting? - + Error Ошибка @@ -1251,37 +1521,47 @@ Would you like to change your database location setting? Вы добавлены в игнор-лист данного игрока. - + Join game Присоединиться - + Password: Пароль: - + Please join the respective room first. - + Games Игры - + &Filter games - + C&lear filter - + + C&reate + + + + + &Join + + + + J&oin as spectator П&рисоединиться как зритель @@ -1306,12 +1586,12 @@ Would you like to change your database location setting? <1m ago - + <1мин назад <5m ago - + <5мин назад @@ -1331,7 +1611,7 @@ Would you like to change your database location setting? 5+ hrs ago - + 5+ час назад @@ -1398,97 +1678,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures Обновить/удалить загруженные картинки - - - - - + + + + + Choose path Путь - + Success - + Выполнено - + Downloaded card pictures have been reset. Загруженные картинки были обновлены - + Error Ошибка - + One or more downloaded card pictures could not be cleared. - + Personal settings Личные настройки - + Language: Язык: - + Download card pictures on the fly Загружать изображения карт автоматически - - Download high-quality card pictures - Загружать изображения карт высокого качества + + Download card pictures from a custom URL + - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths Расположение - + Decks directory: Колоды: - + Replays directory: - + Повторы: - + Pictures directory: Изображения карт: - + Card database: - + База данных карт: - + Token database: - + База данных токенов: - + Picture cache size: - + Размер кеша изображений: - - + + English Русский (Russian) @@ -1496,56 +1786,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Слишком много одновременных подключений с Вашего адреса. - + Scheduled server shutdown. Плановый перерыв в работе сервера. - + Banned by moderator Забанен модератором - + Expected end time: %1 - + This ban lasts indefinitely. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Соединение прервано - + The server has terminated your connection. Reason: %1 Ваше подключение было прервано сервером. Причина: %1 - + Scheduled server shutdown Плановый перерыв в работе сервера - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1554,304 +1837,483 @@ Reason for shutdown: %1 Причина перезагрузки: %1 - + Number of players Количество игроков - + Please enter the number of players. Введите количество игроков. - - + + Player %1 Игрок %1 - + Load replay Загрузить повтор - + About Cockatrice О программе - + Version %1 Версия %1 - + Translators: Переводчики: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Разработчики: - + Our Developers - + Наши разработчики - + Help Develop! - + Recognition Page - + Help Translate! - + Помоги перевести! - + Support: - + Поддержка: - - Report an Issue - - - - - Suggest an Improvement - - - - - - - - - - - - - - + Report an Issue + Сообщить о проблеме + + + + + + + + + + + + + + + + + + + + + Error Ошибка - + Server timeout Нет связи с сервером - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. Пользователь с таким именем уже подключен. Пожалуйста, закройте это подключение и войдите заново. - + + You are banned until %1. - + + You are banned indefinitely. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 Ошибка сокета: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Вы пытаетесь подключиться к несуществующему серверу. Пожалуйста, обновите Cockatrice или выберите другой сервер. Локальная версия %1, удаленная версия %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Ваш клиент Cockatrice устарел. Пожалуйста, обновите Cockatrice. Локальная версия %1, удаленная версия %2. - + Connecting to %1... Подключение к %1... - + + Registering to %1 as %2... + + + + Disconnected Подключение прервано - + Connected, logging in at %1 - - Logged in as %1 at %2 - - - - + &Connect... Подключение... &С - + &Disconnect П&рервать подключение - + Start &local game... &Начать локальную игру... - + &Watch replay... - + &Смотреть повтор... - + &Deck editor Редактор &колод - + &Full screen Полный экран &F - + + &Register to server... + + + + &Settings... Н&астройки - - + + &Exit &Выход - + A&ctions - + &Cockatrice - + &Cockatrice - + &About Cockatrice О про&грамме - + &Help &Справка - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information + Информация + + + + Troubleshooting - - A card update is already ongoing. + + F.A.Q. - - Unable to run the card updater: + + Your account has not been activated yet. +You need to provide the activation token received in the activation email - + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - + неизвестная ошибка. @@ -2091,62 +2553,62 @@ Local version is %1, remote version is %2. из изгнания - + the bottom card of %1's library - + the bottom card of his library нижнюю карту своей библиотеки - + the bottom card of her library нижнюю карту своей библиотеки - + from the bottom of %1's library - + from the bottom of his library со дна своей библиотеки - + from the bottom of her library со дна своей библиотеки - + the top card of %1's library - + the top card of his library верхнюю карту своей библиотеки - + the top card of her library верхнюю карту своей библиотеки - + from the top of %1's library - + from the top of his library с верха своей библиотеки - + from the top of her library с верха своей библиотеки @@ -2549,122 +3011,122 @@ Local version is %1, remote version is %2. %1 снял %n %2 жетон с %3 (теперь %4).%1 снял %n %2 жетона с %3 (теперь %4).%1 снял %n %2 жетонов с %3 (теперь %4).%1 снял %n %2 жетонов с %3 (теперь %4). - + %1 taps her permanents. female %1 повернула свои перманенты. - + %1 untaps her permanents. female %1 развернула свои перманенты. - + %1 taps his permanents. male %1 повернул свои перманенты. - + %1 untaps his permanents. male %1 развернул свои перманенты. - + %1 taps %2. female %1 повернула %2. - + %1 untaps %2. female %1 развернула %2. - + %1 taps %2. male %1 повернул %2. - + %1 untaps %2. male %1 развернул %2. - + %1 sets counter %2 to %3 (%4%5). female %1 установила жетон %2 на %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 установил жетон %2 на %3 (%4%5). - + %1 sets %2 to not untap normally. female %2 теперь не разворачивается как обычно (%1). - + %1 sets %2 to not untap normally. male %2 теперь не разворачивается как обычно (%1). - + %1 sets %2 to untap normally. female %2 теперь разворачивается как обычно (%1). - + %1 sets %2 to untap normally. male %2 теперь разворачивается как обычно (%1). - + %1 sets PT of %2 to %3. female %1 установила Силу/Защиту %2 %3. - + %1 sets PT of %2 to %3. male %1 установил Силу/Защиту %2 %3. - + %1 sets annotation of %2 to %3. female %1 пометила %2 "%3". - + %1 sets annotation of %2 to %3. male %1 пометил %2 "%3". + + + %1 is looking at %2. + female + %1 просматривает %2. + %1 is looking at %2. - female - %1 просматривает %2. - - - - %1 is looking at %2. male %1 просматривает %2. @@ -2679,174 +3141,174 @@ Local version is %1, remote version is %2. - + %1 stops looking at %2. female %1 закончила просматривать %2. - + %1 stops looking at %2. male %1 закончил просматривать %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 показывает её %2 %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 показывает её %2 %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 показывает его %2 %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 показывает его %2 %3. - + %1 reveals %2. female %1 открыла её %2. - + %1 reveals %2. male %1 открыл его %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 показывает случайно выбранную%3 карту (%2) %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 показывает случайно выбранную%3 карту (%2) %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 показывает случайно выбранную%3 карту (%2) %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 показывает случайно выбранную%3 карту (%2) %4. - + %1 randomly reveals %2%3. female %1 показывает случайно выбранную%3 карту (%2). - + %1 randomly reveals %2%3. male %1 показывает случайно выбранную%3 карту (%2). - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female %1 показывает%2%3 %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 показывает%2%3 %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 показывает%2%3 %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 показывает%2%3 %4. - + %1 reveals %2%3. female %1 показывает%2%3. - + %1 reveals %2%3. male %1 показывает%2%3. - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female Ход очаровательной %1. - + It is now %1's turn. male Ход игрока %1. - + a card карту @@ -2909,12 +3371,12 @@ Local version is %1, remote version is %2. - + ending phase заключительный шаг - + untap step шаг разворота @@ -2948,12 +3410,12 @@ Local version is %1, remote version is %2. from her hand - + из ее руки from his hand - + из его руки @@ -3024,64 +3486,64 @@ Local version is %1, remote version is %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step шаг поддержки - + draw step шаг взятия карты - + first main phase первая главная фаза - + beginning of combat step шаг начала битвы - + declare attackers step шаг назначения атакующих - + declare blockers step шаг назначения блокирующих - + combat damage step шаг нанесения повреждений - + end of combat step шаг завершения битвы - + second main phase вторая главная фаза - + It is now the %1. Сейчас %1. @@ -3089,62 +3551,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings Настройки чата - - Enable chat mentions + + Custom alert words - + + Enable chat mentions + Включить упоминания в чате + + + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat - Игнорировать незарегистрированных пользователей в основном чате - - - - Ignore chat room messages sent by unregistered users. + + Ignore chat room messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Ignore private messages sent by unregistered users - + + Enable desktop notifications for private messages + + + + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - + Инвертировать цвет текста - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message Добавить сообщение - + Message: Сообщение: @@ -3210,336 +3684,337 @@ Local version is %1, remote version is %2. Player - + &View library Просмортеть &библиотеку - + Move top cards to &graveyard... Поместить верхние карты на кладби&ще... - + View &top cards of library... Посмтореть верхние карт&ы... - + &View graveyard &Посмотреть кладбище - + &View sideboard Просмотреть &сайд - + Player "%1" Игрок "%1" - - - + + + + &Hand Р&ука - + &Reveal hand to... Показать руку игроку.. &R - + Reveal r&andom card to... Показать случайную карту с руки игроку .. &a - + &Library &Библиотека - - - + + + &Graveyard &Кладбище - + &Sideboard &Сайд - + Red - + Красный - + Yellow - + Желтый - + Green - + Зеленый - + View top cards of library Просмотр верхних карт - + Number of cards: Количество: - + &Draw card В&зять карту - + Reveal top cards of library - + Показать верхние карты библиотеки - + Number of cards: (max. %1) - + Количество карт: (макс. %1) - + &View exile П&осмотреть изгнанные карты - - - + + + &Exile &Изгнание - + Reveal t&op cards to... - + D&raw cards... Вз&ять карты... - + Take &mulligan Взять стра&ховку - + &Shuffle Переме&шать - + &Counters &Жетоны - + &Untap all permanents &Развернуть все перманенты - + R&oll die... Бросить &кубик... - + &Create token... Создать &фишку... - + C&reate another token Создать &еще одну фишку - + S&ay Ска&зать - + &Move hand to... - - - - + + + + &Top of library - - - - + + + + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card - + O&pen deck in deck editor - + &Undo last draw &Отменить последнее взятие - + Play top card &face down - + Move top cards to &exile... Поместить верхние карты в и&згнание... - + Put top card on &bottom Поместить верхн&юю карту на дно - + Put bottom card &in graveyard - + Cr&eate predefined token - + Создать готовый токен - + C&ard Ка&рта - + &All players &Все игроки - + &Play - + &Hide - + Play &Face Down - + &Tap - + &Повернуть - + &Untap - + &Развернуть - + Toggle &normal untapping - + &Flip - + &Peek at card face - + &Clone - + Attac&h to card... - + Unattac&h - + &Draw arrow... - + &Increase power - + &Decrease power - + I&ncrease toughness - + D&ecrease toughness @@ -3549,22 +4024,22 @@ Local version is %1, remote version is %2. - + Dec&rease power and toughness - + Set &power and toughness... - + &Set annotation... - + &Add counter (%1) @@ -3574,103 +4049,108 @@ Local version is %1, remote version is %2. - + &Set counters (%1)... - + Draw cards Взять карты - - - - + + + + Number: Количество: - + Move top cards to grave Поместить верхние карты на кладбище - + Move top cards to exile Поместить верхние карты в изгнание - + Roll die Бросить кубик - + Number of sides: Количество граней: - + Set power/toughness Установить Силу/Защиту - + Please enter the new PT: Введите новые Силу/Защиту: - + Set annotation Пометка - + Please enter the new annotation: Введите текст: - + Set counters Установить жетоны + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3678,7 +4158,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) @@ -3768,14 +4248,43 @@ Local version is %1, remote version is %2. + Permissions + + + + Players Игроки - + Games Игры + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3818,7 +4327,7 @@ Local version is %1, remote version is %2. &Время до отключения (в минутах): - + Shut down server Отключение сервера @@ -3826,37 +4335,37 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3864,42 +4373,47 @@ Local version is %1, remote version is %2. TabAdmin - + Update server &message Обновить сооб&щения сервера - + &Shut down server &Отключение сервера - + + &Reload configuration + + + + Server administration functions Функции администрирования сервера - + &Unlock functions &Разблокировать функции - + &Lock functions &Заблокировать функции - + Unlock administration functions Разблокировать административные права - + Do you really want to unlock the administration functions? Вы действительно хотите разблокировать административные права? - + Administration @@ -3907,179 +4421,219 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... - + &Close - + &Edit sets... - - &Clear search + + Filters - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: - + &Comments: - + Hash: - + &New deck - + &Load deck... - + &Save deck - + Save deck &as... - + Load deck from cl&ipboard... - + Save deck to clip&board - + &Analyze deck on deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck - + Add card to &sideboard - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row - + &Increment number - + &Decrement number - + Edit &tokens... - + Deck: %1 - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4177,95 +4731,100 @@ Please enter a name: TabGame - + &Phases &Фазы - + &Game &Игра - + Next &phase Следующая &фаза - + Next &turn Следующий &ход - + &Remove all local arrows &Удалить все указатели - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information Информация &об игре - + &Concede &Сдаться... - + &Leave game Покинуть и&гру - + C&lose replay З &акрыть повтор - + &Say: Ска&зать: - + Concede Сдаться - + Are you sure you want to concede this game? Вы точно хотите сдаться? - + Leave game Покинуть игру - + Are you sure you want to leave this game? Вы уверены, что хотите уйти? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 + + REPLAY - - - Game %1: %2 - Игра %1: %2 - TabMessage @@ -4308,54 +4867,54 @@ Please enter a name: TabReplays - + Local file system - + Server replay storage - + Watch replay Посмотреть повтор - - + + Delete Удалить - + Download replay - + Toggle expiration lock - + Delete local file - + Are you sure you want to delete "%1"? - + Delete remote replay - + Are you sure you want to delete the replay of game %1? @@ -4368,47 +4927,47 @@ Please enter a name: TabRoom - + &Say: &Сказать: - + Chat Чат - + &Room &Комната - + &Leave room &Покинуть комнату - + &Clear chat & Обновить чат - + Chat Settings... Установки чата - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. Кажется, Вы нафлудили. Пожалуйста, подождите пару секунд. @@ -4424,15 +4983,25 @@ Please enter a name: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4455,169 +5024,301 @@ Please enter a name: UserContextMenu - + User &details - + Private &chat - + Show this user's &games - + Add to &buddy list - + Remove from &buddy list - + Add to &ignore list - + Remove from &ignore list - + Kick from &game - + Ban from &server - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Информация о пользователе - + Real name: Настоящее имя: - - Gender: - Пол: + + Pronouns: + - + Location: Местонахождение: - + User level: Уровень: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator Администратор - + Moderator Модератор - + Registered user Зарегистрированный пользователь - - + + Unregistered user Рандом - + Unknown Неизвестный - + Year Год - + Years Лет - + Day День - + Days Дней + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Основные настройки интерфейса - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) &Двойной клик чтобы разыграть карту (вместо одинарного) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings Настройки анимации - + &Tap/untap animation &Анимировать поворот/разворот карты @@ -4625,22 +5326,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 Пользователей в этой комнате: %1 - + Buddies online: %1 / %2 Друзей онлайн: %1 / %2 - + Ignored users online: %1 / %2 Игнорируемых пользователей онлайн: %1 / %2 @@ -4662,27 +5363,44 @@ Please enter a name: Move selected set up Переместить выбранный сет вверх + + + Move selected set to the top + + Move selected set down Переместить выбранный сет вниз - - - Move selected set to top - Сделать выбранный сет верхним - - Move selected set to bottom - Сделать выбранный сет нижним + Move selected set to the bottom + - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4691,12 +5409,12 @@ Disabled sets will still be used for loading images. Редактировать издания - + Success Выполнено - + The sets database has been saved successfully. База данных по сетам успешно сохранена. @@ -4724,4 +5442,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_sv.ts b/cockatrice/translations/cockatrice_sv.ts index 4ae55bee..cdb791a5 100644 --- a/cockatrice/translations/cockatrice_sv.ts +++ b/cockatrice/translations/cockatrice_sv.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... &Placera polett... - + Set counter Placera polett - + New value for counter '%1': Nytt värde för '%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures Zonbakgrundsbilder - + Hand background: - + Stack background: - + Table background: - + Player info background: - + Card back: - + Card rendering Kortrendering - + Display card names on cards having a picture Visa kortnamn på kort som har bilder - + Scale cards on mouse over - + Hand layout Handlayout - + Display hand horizontally (wastes space) Visa hand horisontellt (slösar plats) - + Enable left justification - + Table grid layout Bordets rutnätlayout - + Invert vertical coordinate Invertera vertikal koordinat - + Minimum player count for multi-column layout: Minst antal spelare för multi-kolumnlayout: - - - - - + + + + + Choose path Välj sökväg @@ -107,97 +107,102 @@ BanDialog - + ban &user name bannlys användar&namn - + ban &IP address bannlys &IP address - + + ban client I&D + + + + Ban type Typ av bannlysning - + &permanent ban &permanent bannlysning - + &temporary ban &temporär bannlysning - + &Days: &Dagar: - + &Hours: &Timmar: - + &Minutes: &Minuter: - + Duration of the ban Bannlysningens längd - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. Vänligen ange anledningen till bannlysningen. Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. - + Please enter the reason for the ban that will be visible to the banned person. Vänligen ange en anledning för bannlysningen som kommer att visas för den bannlysta personen. - + &OK &OK - + &Cancel &Avbryt - + Ban user from server Bannlys användaren från servern - + Error Fel - - You have to select a name-based or IP-based ban, or both. - Du måste väja en namnbaserad eller IP-baserad bannlysning, eller både och. + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. P/T + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: Namn: - + Mana cost: Manakostnad: - + + Color(s): + + + + Card type: Korttyp: - + P / T: P / T: - + Loyalty: Lojalitet: @@ -276,27 +304,32 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. Vissa full info - + Name: Namn: - + Mana cost: Manakostnad: - + + Color(s): + + + + Card type: Korttyp: - + P / T: P / T: - + Loyalty: Lojalitet: @@ -560,12 +593,12 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. DeckEditorSettingsPage - + Nothing is here... yet - + General Allmänt @@ -573,17 +606,17 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. DeckListModel - + Number Nummer - + Card Kort - + Price Pris @@ -605,42 +638,42 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. DeckViewContainer - - Load &local deck - &Ladda lokal lek + + Load local deck + - - Load d&eck from server - Ladda lek från &servern + + Load deck from server + - + Ready to s&tart Redo att &börja - + S&ideboard unlocked S&idbräda upplåst - + S&ideboard locked S&idbräda låst - + Load deck Ladda lek - + Error Fel - + The selected file could not be loaded. Den valda filen kunde ej laddas. @@ -686,37 +719,52 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: &Värd: - + + Enter host name + + + + &Port: &Port: - + Player &name: Spelar&namn: - + P&assword: &Lösenord: - + &Save password &Spara lösenord - + A&uto connect at start - + Connect to server Anslut till server @@ -724,82 +772,92 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. DlgCreateGame - + &Description: &Beskrivning: - + &Password: &Lösenord: - + P&layers: &Spelare: - + + Re&member settings + + + + Game type Speltyp - + Only &buddies can join Endast &vänner kan ansluta - + Only &registered users can join Endast &registerade användare kan ansluta - + Joining restrictions Anslutingsbegränsningar - + &Spectators can watch - + Spectators &need a password to watch - + Spectators can see &hands - + Spectators can &chat Åskådare kan &chatta - + Spectators Åskådare - + + &Clear + + + + Create game Skapa spel - + Game information Spelinformation - + Error Fel - + Server error. Serverfel. @@ -807,96 +865,169 @@ Detta sparas endast för moderatorer och kan inte ses av den bannlysta personen. DlgCreateToken - + &Name: &Namn: - + Token Jetong - + C&olor: &Färg: - + white vit - + blue blå - + black svart - + red röd - + green grön - + multicolor multifärgad - + colorless färglös - + &P/T: &P/T: - + &Annotation: &Annotering: - + &Destroy token when it leaves the table &Förstör jetong när den lämnar bordet - + Token data Jetongdata - + Show &all tokens Visa alla &jetonger - + Show tokens from this &deck Visa jetonger från denna &lek - + Choose token from list Välj jetong från lista - + Create token Skapa jetong + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh &Uppdatera @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Ladda lek från urklipp - + + Error Fel - + + Invalid deck list. Ogiltig leklista. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di Ladda lek + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error Fel - + Unknown Error loading card database - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1097,7 +1372,7 @@ Would you like to change your database location setting? - + Your card database version is too old. This can cause problems loading card information or images @@ -1108,21 +1383,21 @@ Would you like to change your database location setting? - + File Error loading your card database. Would you like to change your database location setting? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1131,7 +1406,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1140,63 +1415,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? Sökvägen till din lekkatalog är ogiltig. Vill du gå tillbaka och ange den korrekta sökvägen? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? Sökvägen till din kortbildsdatabas är ogiltig. Vill du gå tillbaka och ange den korrekta sökvägen? - + Settings Inställningar - + General Allmänt - + Appearance Utseende - + User Interface - + Deck Editor - + Chat - + Sound + + + Shortcuts + + GameSelector - - - C&reate - &Skapa - - - - &Join - &Anslut - @@ -1206,7 +1476,7 @@ Would you like to change your database location setting? - + Error Fel @@ -1251,37 +1521,47 @@ Would you like to change your database location setting? Spelets skapare ignorerar dig. - + Join game Anslut till spel - + Password: Lösenord: - + Please join the respective room first. Vänligen anslut till respektive rum först. - + Games Spel - + &Filter games - &Filtrera spel + - + C&lear filter - &Rensa filter + - + + C&reate + + + + + &Join + + + + J&oin as spectator Anslut som &åskådare @@ -1398,97 +1678,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path Välj sökväg - + Success - + Downloaded card pictures have been reset. - + Error - + One or more downloaded card pictures could not be cleared. - + Personal settings Personliga inställningar - + Language: Språk: - + Download card pictures on the fly Ladda ner kortbilder på direkten - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths Sökvägar - + Decks directory: Lekkatalog: - + Replays directory: Repriskatalog: - + Pictures directory: Bildkatalog: - + Card database: - + Token database: - + Picture cache size: - - + + English Svenska (Swedish) @@ -1496,56 +1786,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. Din adress har för många uppkopplingar samtidigt. - + Scheduled server shutdown. Schemalagd serverstängning. - + Banned by moderator Bannlyst av moderator - + Expected end time: %1 Förväntad sluttid: %1 - + This ban lasts indefinitely. Denna bannlysning varar för evigt. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed Uppkoppling avslutad - + The server has terminated your connection. Reason: %1 Servern har avslutat din uppkoppling. Anledning: %1 - + Scheduled server shutdown Schemalagd serverstängning - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1556,305 +1839,484 @@ Alla pågående spel kommer att gå förlorade. Anledning till nedstängning: %1 - + Number of players Antal spelare - + Please enter the number of players. Vänligen ange antal spelare. - - + + Player %1 Spelare %1 - + Load replay Ladda repris - + About Cockatrice Om Cockatrice - + Version %1 Version %1 - + Translators: Översättare: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error Fel - + Server timeout Server timeout - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. Det finns redan en aktiv session med det användarnamnet. Vänligen stäng den sessionen först och försök igen. - + + You are banned until %1. Du är bannlyst till %1. - + + You are banned indefinitely. Du är bannlyst för evigt. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 Okänt inloggningsfel: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 Socketfel: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. Du försöker koppla upp dig till en föråldrad server. Vänligen nergradera din version av Cockatrice eller koppla upp dig till en lämplig server. Lokal version är %1, avlägsen version är %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. Din version av Cockatrice är föråldrad. Vänligen uppdatera din version av Cockatrice. Lokal version är %1, avlägsen version är %2. - + Connecting to %1... Ansluter till %1... - + + Registering to %1 as %2... + + + + Disconnected Frånkopplad - + Connected, logging in at %1 Uppkopplad, loggar in hos %1 - - Logged in as %1 at %2 - - - - + &Connect... &Anslut... - + &Disconnect &Frånkoppla - + Start &local game... Starta &lokalt spel... - + &Watch replay... &Titta på repris... - + &Deck editor Lek&redigeraren - + &Full screen &Fullskärmsläge - + + &Register to server... + + + + &Settings... &Inställningar... - - + + &Exit A&vsluta - + A&ctions - + &Cockatrice &Cockatrice - + &About Cockatrice &Om Cockatrice - + &Help &Hjälp - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2093,62 +2555,62 @@ Lokal version är %1, avlägsen version är %2. från exil - + the bottom card of %1's library det sista kortet i %1's lek - + the bottom card of his library det sista kortet i sin lek - + the bottom card of her library det sista kortet i sin lek - + from the bottom of %1's library från botten av %1's lek - + from the bottom of his library från botten av sin lek - + from the bottom of her library från botten av sin lek - + the top card of %1's library det översta kortet av %1's lek - + the top card of his library det översta kortet avsin lek - + the top card of her library det översta kortet av sin lek - + from the top of %1's library från toppen av %1's lek - + from the top of his library från botten av sin lek - + from the top of her library från toppen av sin lek @@ -2551,304 +3013,304 @@ Lokal version är %1, avlägsen version är %2. %1 tar bort %n %2 polett från %3 (%4 totalt).%1 tar bort %n %2 poletter från %3 (%4 totalt). - + %1 taps her permanents. female %1 tappar sina permanents. - + %1 untaps her permanents. female %1 tappar upp sina permanents. - + %1 taps his permanents. male %1 tappar sina permanents. - + %1 untaps his permanents. male %1 tappar upp sina permanents. - + %1 taps %2. female %1 tappar %2. - + %1 untaps %2. female %1 tappar upp %2. - + %1 taps %2. male %1 tappar %2. - + %1 untaps %2. male %1 tappar upp %2. - + %1 sets counter %2 to %3 (%4%5). female %1 ändrar antalet %2 poletter till %3 (%4%5). - + %1 sets counter %2 to %3 (%4%5). male %1 ändrar antalet %2 poletter till %3 (%4%5). - + %1 sets %2 to not untap normally. female %1 har inaktiverat normal upptappning för %2. - + %1 sets %2 to not untap normally. male %1 har inaktiverat normal upptappning för %2. - + %1 sets %2 to untap normally. female %1 har aktiverat normal upptappning för %2. - + %1 sets %2 to untap normally. male %1 har aktiverat normal upptappning för %2. - + %1 sets PT of %2 to %3. female %1 ställer %2's PT till %3. - + %1 sets PT of %2 to %3. male %1 ställer %2's PT till %3. - + %1 sets annotation of %2 to %3. female %1 ställer %2's annotering till %3. - + %1 sets annotation of %2 to %3. male %1 ställer %2's annotering till %3. + + + %1 is looking at %2. + female + %1 tittar på %2. + %1 is looking at %2. - female + male %1 tittar på %2. + + %1 is looking at the top %n card(s) %2. + female + %1 tittar på det översta kortet %2.%1 tittar på de översta %n korten %2. + + + %1 is looking at the top %n card(s) %2. + male + %1 tittar på det översta kortet %2.%1 tittar på de översta %n korten %2. + - - %1 is looking at %2. - male - %1 tittar på %2. - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - %1 tittar på det översta kortet %2.%1 tittar på de översta %n korten %2. - - - %1 is looking at the top %n card(s) %2. - male - %1 tittar på det översta kortet %2.%1 tittar på de översta %n korten %2. + %1 slutar titta på %2. %1 stops looking at %2. - female - %1 slutar titta på %2. - - - - %1 stops looking at %2. male %1 slutar titta på %2. - + %1 reveals %2 to %3. p1 female, p2 female %1 visar %2 för %3. - + %1 reveals %2 to %3. p1 female, p2 male %1 visar %2 för %3. - + %1 reveals %2 to %3. p1 male, p2 female %1 visar %2 för %3. - + %1 reveals %2 to %3. p1 male, p2 male %1 visar %2 för %3. + + + %1 reveals %2. + female + %1 visar %2. + %1 reveals %2. - female - %1 visar %2. - - - - %1 reveals %2. male %1 visar %2. - + %1 randomly reveals %2%3 to %4. p1 female, p2 female %1 visar slumpmässigt %2%3 till %4. - + %1 randomly reveals %2%3 to %4. p1 female, p2 male %1 visar slumpmässigt %2%3 till %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 female %1 visar slumpmässigt %2%3 till %4. - + %1 randomly reveals %2%3 to %4. p1 male, p2 male %1 visar slumpmässigt %2%3 till %4. - + %1 randomly reveals %2%3. female %1 visar slumpmässigt %2%3. - + %1 randomly reveals %2%3. male %1 visar slumpmässigt %2%3. - + %1 peeks at face down card #%2. female %1 kikar på nedåtvända kort #%2. - + %1 peeks at face down card #%2. male %1 kikar på nedåtvända kort #%2. - + %1 peeks at face down card #%2: %3. female %1 kikar på nedåtvända kort #%2: %3. - + %1 peeks at face down card #%2: %3. male %1 kikar på nedåtvända kort #%2: %3. - + %1 reveals %2%3 to %4. p1 female, p2 female %1 visar %2%3 till %4. - + %1 reveals %2%3 to %4. p1 female, p2 male %1 visar %2%3 till %4. - + %1 reveals %2%3 to %4. p1 male, p2 female %1 visar %2%3 till %4. - + %1 reveals %2%3 to %4. p1 male, p2 male %1 visar %2%3 till %4. - + %1 reveals %2%3. female %1 visar %2%3. - + %1 reveals %2%3. male %1 visar %2%3. - + %1 is now keeping the top card %2 revealed. %1 håller nu det översta kortet %2 avslöjat. - + %1 is not revealing the top card %2 any longer. %1 håller inte längre det översta kortet %2 avslöjat. - + It is now %1's turn. female Det är nu %1's tur. - + It is now %1's turn. male Det är nu %1's tur. - + a card ett kort @@ -2911,12 +3373,12 @@ Lokal version är %1, avlägsen version är %2. - + ending phase slutfasen - + untap step upptappningssteget @@ -3026,64 +3488,64 @@ Lokal version är %1, avlägsen version är %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step underhållssteget - + draw step dragsteget - + first main phase första huvudfasen - + beginning of combat step början av stridssteget - + declare attackers step attacksteget - + declare blockers step blockeringssteget - + combat damage step skadesteget - + end of combat step slutet av stridssteget - + second main phase andra huvudfasen - + It is now the %1. Det är nu %1. @@ -3091,62 +3553,74 @@ Lokal version är %1, avlägsen version är %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message Lägg till meddelande - + Message: Meddelande: @@ -3212,336 +3686,337 @@ Lokal version är %1, avlägsen version är %2. Player - + &View library &Titta på leken - + Move top cards to &graveyard... Flytta översta korten till &kyrkogården... - + View &top cards of library... Titta på de &översta korten i leken... - + &View graveyard &Titta på kyrkogård - + &View sideboard &Titta på sidbrädan - + Player "%1" Spelare "%1" - - - + + + + &Hand &Hand - + &Reveal hand to... - + Reveal r&andom card to... - + &Library &Lek - - - + + + &Graveyard &Kyrkogård - + &Sideboard S&idbräda - + Red - + Yellow - + Green - + View top cards of library Titta på de översta korten i leken - + Number of cards: Antal kort: - + &Draw card &Dra kort - + Reveal top cards of library - + Number of cards: (max. %1) - + &View exile &Titta på exil - - - + + + &Exile &Exil - + Reveal t&op cards to... - + D&raw cards... D&ra kort... - + Take &mulligan &Mulligan - + &Shuffle &Blanda - + &Counters &Poletter - + &Untap all permanents Tappa upp alla perma&nenta kort - + R&oll die... Rulla t&ärning... - + &Create token... &Skapa jetong... - + C&reate another token S&kapa en till jetong - + S&ay S&äg - + &Move hand to... - - - - + + + + &Top of library - - - - + + + + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card &Avslöja alltid det översta kortet - + O&pen deck in deck editor &Öppna lek i lekredigeraren - + &Undo last draw &Ångra senaste drag - + Play top card &face down - + Move top cards to &exile... Flytta översta korten till &exil... - + Put top card on &bottom Placera översta kortet &underst - + Put bottom card &in graveyard - + Cr&eate predefined token Skapa fördefinierad &jetong - + C&ard K&ort - + &All players A&lla spelare - + &Play &Spela - + &Hide &Göm - + Play &Face Down - + &Tap &Tappa - + &Untap Tappa &upp - + Toggle &normal untapping Växla &normal upptappning - + &Flip &Vänd - + &Peek at card face Kika på kort&framsida - + &Clone &Klona - + Attac&h to card... &Fäst på kort... - + Unattac&h Se&parera - + &Draw arrow... &Rita pil... - + &Increase power &Öka power - + &Decrease power &Minska power - + I&ncrease toughness Öka toug&hness - + D&ecrease toughness Minska t&oughness @@ -3551,22 +4026,22 @@ Lokal version är %1, avlägsen version är %2. Öka power o&ch toughness - + Dec&rease power and toughness M&inska power och toughness - + Set &power and toughness... An&ge power och toughness... - + &Set annotation... Ang&e annotering... - + &Add counter (%1) P&lacera polett (%1) @@ -3576,103 +4051,108 @@ Lokal version är %1, avlägsen version är %2. Ta &bort polett (%1) - + &Set counters (%1)... Pl&acera poletter (%1)... - + Draw cards Dra kort - - - - + + + + Number: Antal: - + Move top cards to grave Flytta översta korten till kyrkogården - + Move top cards to exile Flytta översta korten till exil - + Roll die Rulla tärning - + Number of sides: Antal sidor: - + Set power/toughness Ange power/toughness - + Please enter the new PT: Vänligen ange ny PT: - + Set annotation Ange annotering - + Please enter the new annotation: Vänligen ange den nya annoteringen: - + Set counters Placera poletter + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 @@ -3680,7 +4160,7 @@ Lokal version är %1, avlägsen version är %2. QObject - + Cockatrice replays (*.cor) Cockatricerepriser (*.cor) @@ -3770,14 +4250,43 @@ Lokal version är %1, avlägsen version är %2. + Permissions + + + + Players Spelare - + Games Spel + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3820,7 +4329,7 @@ Lokal version är %1, avlägsen version är %2. &Tid till nedstängning (minuter): - + Shut down server Stäng ned server @@ -3828,37 +4337,37 @@ Lokal version är %1, avlägsen version är %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3866,42 +4375,47 @@ Lokal version är %1, avlägsen version är %2. TabAdmin - + Update server &message &Uppdatera servermeddelande - + &Shut down server &Stäng ned server - + + &Reload configuration + + + + Server administration functions Serveradministrationsfunktioner - + &Unlock functions &Lås upp funktioner - + &Lock functions Lås &funktioner - + Unlock administration functions Lås upp administrationsfunktioner - + Do you really want to unlock the administration functions? Vill du verkligen låsa upp administrationsfunktionerna? - + Administration @@ -3909,180 +4423,220 @@ Lokal version är %1, avlägsen version är %2. TabDeckEditor - + &Print deck... Skri&v ut lek... - + &Close S&täng - + &Edit sets... Redigera utg&åvor... - - &Clear search - &Rensa sökning + + Filters + - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: &Leknamn: - + &Comments: &Kommentarer: - + Hash: Hash: - + &New deck &Ny lek - + &Load deck... &Ladda lek... - + &Save deck S&para lek - + Save deck &as... Spa&ra lek som... - + Load deck from cl&ipboard... Ladda lek &från urklipp... - + Save deck to clip&board Spara lek som u&tklipp - + &Analyze deck on deckstats.net &Anslysera lek på deckstats.net - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck Lägg till kort till &huvudlek - + Add card to &sideboard Lägg till kort i sidbr&äda - + &Deck Editor - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row Ta bort ra&d - + &Increment number &Öka antal - + &Decrement number &Minska antal - + Edit &tokens... Redigera &jetonger... - + Deck: %1 Lek: %1 - + Are you sure? Är du säker? - + The decklist has been modified. Do you want to save the changes? Denna leklista har modifierats. Vill du spara ändringarna? - + Load deck Ladda lek - - - + + + Error Fel - + The deck could not be saved. Leken kunde inte sparas. - - + + The deck could not be saved. Please check that the directory is writable and try again. Leken kunde inte sparas. Vänligen se till att katalogen är skrivbar och försök igen. - + Save deck Spara lek @@ -4179,94 +4733,99 @@ Please enter a name: TabGame - + &Phases &Faser - + &Game &Spel - + Next &phase Nästa &fas - + Next &turn Nästa &tur - + &Remove all local arrows Ta &bort alla lokala pilar - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information Spel&information - + &Concede &Ge upp - + &Leave game &Lämna spel - + C&lose replay S&täng repris - + &Say: S&äg: - + Concede Ge upp - + Are you sure you want to concede this game? Är du säker på att du vill ge upp detta spel? - + Leave game Lämna spel - + Are you sure you want to leave this game? Är du säker på att du vill lämna detta spel? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 - Repris %1: %2 - - - - Game %1: %2 - Spel %1: %2 + + REPLAY + @@ -4310,54 +4869,54 @@ Please enter a name: TabReplays - + Local file system Lokalt filsystem - + Server replay storage Serverns reprislagring - + Watch replay Titta på repris - - + + Delete Radera - + Download replay Ladda ner repris - + Toggle expiration lock Växla utgångslås - + Delete local file Radera lokal fil - + Are you sure you want to delete "%1"? Är du säker på att du vill radera "%1"? - + Delete remote replay Radera avlägsen repris - + Are you sure you want to delete the replay of game %1? Är du säker på att du vill radera reprisen av spel %1? @@ -4370,47 +4929,47 @@ Please enter a name: TabRoom - + &Say: &Säg: - + Chat Chatt - + &Room &Rum - + &Leave room &Lämna rum - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. Du översvämmar chatten. Var vänlig vänta ett par sekunder. @@ -4426,15 +4985,25 @@ Please enter a name: TabSupervisor - + Are you sure? - + There are still open games. Are you sure you want to quit? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4457,169 +5026,301 @@ Please enter a name: UserContextMenu - + User &details Användar&detaljer - + Private &chat - + Show this user's &games Visa denna användarens &spel - + Add to &buddy list Lägg till som &vän - + Remove from &buddy list Ta bort som &vän - + Add to &ignore list &Ignorera - + Remove from &ignore list Sluta &ignorera - + Kick from &game &Sparka från spelet - + Ban from &server &Bannlys från servern - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games %1's spel + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information Användarinformation - + Real name: Riktiga namn: - - Gender: - Kön: + + Pronouns: + - + Location: Plats: - + User level: Användarnivå: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator Administratör - + Moderator Moderator - + Registered user Registrerad användare - - + + Unregistered user Oregistrerad användare - + Unknown - + Year - + Years - + Day - + Days + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings Allmänna gränssnittsinställningar - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) &Dubbelklicka på kort för att spela dem (istället för enkelklick) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings Animationsinställningar - + &Tap/untap animation &Tappnings/Upptappningsanimation @@ -4627,22 +5328,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 Användare i detta rum: %1 - + Buddies online: %1 / %2 Vänner online: %1 / %2 - + Ignored users online: %1 / %2 Ignorerade användare online: %1 / %2 @@ -4664,27 +5365,44 @@ Please enter a name: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4693,12 +5411,12 @@ Disabled sets will still be used for loading images. Redigera utgåvor - + Success - + The sets database has been saved successfully. @@ -4726,4 +5444,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_uk.ts b/cockatrice/translations/cockatrice_uk.ts new file mode 100644 index 00000000..ce3501c2 --- /dev/null +++ b/cockatrice/translations/cockatrice_uk.ts @@ -0,0 +1,5994 @@ + + + AbstractCounter + + + &Set counter... + + + + + Set counter + + + + + New value for counter '%1': + + + + + AppearanceSettingsPage + + + Zone background pictures + + + + + Hand background: + + + + + Stack background: + + + + + Table background: + + + + + Player info background: + + + + + Card back: + + + + + Card rendering + + + + + Display card names on cards having a picture + + + + + Scale cards on mouse over + + + + + Hand layout + + + + + Display hand horizontally (wastes space) + + + + + Enable left justification + + + + + Table grid layout + + + + + Invert vertical coordinate + + + + + Minimum player count for multi-column layout: + + + + + + + + + Choose path + + + + + BanDialog + + + ban &user name + + + + + ban &IP address + + + + + ban client I&D + + + + + Ban type + + + + + &permanent ban + + + + + &temporary ban + + + + + &Days: + + + + + &Hours: + + + + + &Minutes: + + + + + Duration of the ban + + + + + Please enter the reason for the ban. +This is only saved for moderators and cannot be seen by the banned person. + + + + + Please enter the reason for the ban that will be visible to the banned person. + + + + + &OK + + + + + &Cancel + + + + + Ban user from server + + + + + Error + + + + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + + + + + CardDatabase + + + New sets found + + + + + %1 new set(s) have been found in the card database. Do you want to enable them? + + + + + CardDatabaseModel + + + Name + + + + + Sets + + + + + Mana cost + + + + + Card type + + + + + P/T + + + + + CardFrame + + + Image + + + + + Description + + + + + Both + + + + + CardInfoText + + + Name: + + + + + Mana cost: + + + + + Color(s): + + + + + Card type: + + + + + P / T: + + + + + Loyalty: + + + + + CardInfoWidget + + + Show card only + + + + + Show text only + + + + + Show full info + + + + + Name: + + + + + Mana cost: + + + + + Color(s): + + + + + Card type: + + + + + P / T: + + + + + Loyalty: + + + + + CardItem + + + &Power / toughness + + + + + &Move to + + + + + CardZone + + + her hand + nominative, female owner + + + + + %1's hand + nominative, female owner + + + + + his hand + nominative, male owner + + + + + %1's hand + nominative, male owner + + + + + her library + look at zone, female owner + + + + + %1's library + look at zone, female owner + + + + + his library + look at zone, male owner + + + + + %1's library + look at zone, male owner + + + + + of her library + top cards of zone, female owner + + + + + of %1's library + top cards of zone, female owner + + + + + of his library + top cards of zone, male owner + + + + + of %1's library + top cards of zone, male owner + + + + + her library + reveal zone, female owner + + + + + %1's library + reveal zone, female owner + + + + + his library + reveal zone, male owner + + + + + %1's library + reveal zone, male owner + + + + + her library + shuffle, female owner + + + + + %1's library + shuffle, female owner + + + + + his library + shuffle, male owner + + + + + %1's library + shuffle, male owner + + + + + her sideboard + look at zone, female owner + + + + + %1's sideboard + look at zone, female owner + + + + + his sideboard + look at zone, male owner + + + + + %1's sideboard + look at zone, male owner + + + + + her library + nominative, female owner + + + + + %1's library + nominative, female owner + + + + + his library + nominative, male owner + + + + + %1's library + nominative, male owner + + + + + her graveyard + nominative, female owner + + + + + %1's graveyard + nominative, female owner + + + + + his graveyard + nominative, male owner + + + + + %1's graveyard + nominative, male owner + + + + + her exile + nominative, female owner + + + + + %1's exile + nominative, female owner + + + + + his exile + nominative, male owner + + + + + %1's exile + nominative, male owner + + + + + her sideboard + nominative, female owner + + + + + %1's sideboard + nominative, female owner + + + + + his sideboard + nominative, male owner + + + + + %1's sideboard + nominative, male owner + + + + + DeckEditorSettingsPage + + + Nothing is here... yet + + + + + General + + + + + DeckListModel + + + Number + + + + + Card + + + + + Price + + + + + DeckStatsInterface + + + + Error + + + + + The reply from the server could not be parsed. + + + + + DeckViewContainer + + + Load local deck + + + + + Load deck from server + + + + + Ready to s&tart + + + + + S&ideboard unlocked + + + + + S&ideboard locked + + + + + Load deck + + + + + Error + + + + + The selected file could not be loaded. + + + + + DlgCardSearch + + + Card name: + + + + + Card text: + + + + + Card type (OR): + + + + + Color (OR): + + + + + O&K + + + + + &Cancel + + + + + Card search + + + + + DlgConnect + + + Previous Host + + + + + New Host + + + + + &Host: + + + + + Enter host name + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + &Save password + + + + + A&uto connect at start + + + + + Connect to server + + + + + DlgCreateGame + + + &Description: + + + + + &Password: + + + + + P&layers: + + + + + Re&member settings + + + + + Game type + + + + + Only &buddies can join + + + + + Only &registered users can join + + + + + Joining restrictions + + + + + &Spectators can watch + + + + + Spectators &need a password to watch + + + + + Spectators can see &hands + + + + + Spectators can &chat + + + + + Spectators + + + + + &Clear + + + + + Create game + + + + + Game information + + + + + Error + + + + + Server error. + + + + + DlgCreateToken + + + &Name: + + + + + Token + + + + + C&olor: + + + + + white + + + + + blue + + + + + black + + + + + red + + + + + green + + + + + multicolor + + + + + colorless + + + + + &P/T: + + + + + &Annotation: + + + + + &Destroy token when it leaves the table + + + + + Token data + + + + + Show &all tokens + + + + + Show tokens from this &deck + + + + + Choose token from list + + + + + Create token + + + + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + + + DlgEditTokens + + + &Name: + + + + + C&olor: + + + + + white + + + + + blue + + + + + black + + + + + red + + + + + green + + + + + multicolor + + + + + colorless + + + + + &P/T: + + + + + &Annotation: + + + + + Token data + + + + + + Add token + + + + + Remove token + + + + + Edit tokens + + + + + Please enter the name of the token: + + + + + Error + + + + + The chosen name conflicts with an existing card or token. +Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. + + + + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + + + DlgFilterGames + + + Show &unavailable games + + + + + Show &password protected games + + + + + Game &description: + + + + + &Creator name: + + + + + &Game types + + + + + at &least: + + + + + at &most: + + + + + Maximum player count + + + + + Filter games + + + + + DlgLoadDeckFromClipboard + + + &Refresh + + + + + Load deck from clipboard + + + + + + Error + + + + + + Invalid deck list. + + + + + DlgLoadRemoteDeck + + + Load deck + + + + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + + + DlgSettings + + + + + Error + + + + + Unknown Error loading card database + + + + + Your card database is invalid. + +Cockatrice may not function correctly with an invalid database + +You may need to rerun oracle to update your card database. + +Would you like to change your database location setting? + + + + + Your card database version is too old. + +This can cause problems loading card information or images + +Usually this can be fixed by rerunning oracle to to update your card database. + +Would you like to change your database location setting? + + + + + File Error loading your card database. + +Would you like to change your database location setting? + + + + + Your card database was loaded but contains no cards. + +Would you like to change your database location setting? + + + + + Your card database did not finish loading + +Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached + +Would you like to change your database location setting? + + + + + Unknown card database load status + +Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues + +Would you like to change your database location setting? + + + + + The path to your deck directory is invalid. Would you like to go back and set the correct path? + + + + + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? + + + + + Settings + + + + + General + + + + + Appearance + + + + + User Interface + + + + + Deck Editor + + + + + Chat + + + + + Sound + + + + + Shortcuts + + + + + GameSelector + + + + + + + + + + + Error + + + + + Please join the appropriate room first. + + + + + Wrong password. + + + + + Spectators are not allowed in this game. + + + + + The game is already full. + + + + + The game does not exist any more. + + + + + This game is only open to registered users. + + + + + This game is only open to its creator's buddies. + + + + + You are being ignored by the creator of this game. + + + + + Join game + + + + + Password: + + + + + Please join the respective room first. + + + + + Games + + + + + &Filter games + + + + + C&lear filter + + + + + C&reate + + + + + &Join + + + + + J&oin as spectator + + + + + GamesModel + + + Game Created + + + + + Creator + + + + + Description + + + + + <1m ago + + + + + <5m ago + + + + + %1m ago + + + + + 1hr %1m ago + + + + + %1hr ago + + + + + 5+ hrs ago + + + + + password + + + + + buddies only + + + + + reg. users only + + + + + + can chat + + + + + see hands + + + + + can see hands + + + + + not allowed + + + + + Room + + + + + Game Type + + + + + Restrictions + + + + + Players + + + + + Spectators + + + + + GeneralSettingsPage + + + Reset/Clear Downloaded Pictures + + + + + + + + + Choose path + + + + + Success + + + + + Downloaded card pictures have been reset. + + + + + Error + + + + + One or more downloaded card pictures could not be cleared. + + + + + Personal settings + + + + + Language: + + + + + Download card pictures on the fly + + + + + Download card pictures from a custom URL + + + + + Custom Card Download URL: + + + + + Linking FAQ + + + + + Paths + + + + + Decks directory: + + + + + Replays directory: + + + + + Pictures directory: + + + + + Card database: + + + + + Token database: + + + + + Picture cache size: + + + + + + English + + + + + MainWindow + + + There are too many concurrent connections from your address. + + + + + Scheduled server shutdown. + + + + + Banned by moderator + + + + + Expected end time: %1 + + + + + This ban lasts indefinitely. + + + + + Connection closed + + + + + The server has terminated your connection. +Reason: %1 + + + + + Scheduled server shutdown + + + + + The server is going to be restarted in %n minute(s). +All running games will be lost. +Reason for shutdown: %1 + + + + + Number of players + + + + + Please enter the number of players. + + + + + + Player %1 + + + + + Load replay + + + + + About Cockatrice + + + + + Version %1 + + + + + Translators: + + + + + Project Manager: + + + + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + + Past Project Managers: + + + + + Developers: + + + + + Our Developers + + + + + Help Develop! + + + + + Recognition Page + + + + + Help Translate! + + + + + Support: + + + + + Report an Issue + + + + + + + + + + + + + + + + + + + + + + + Error + + + + + Server timeout + + + + + Incorrect username or password. Please check your authentication information and try again. + + + + + There is already an active session using this user name. +Please close that session first and re-login. + + + + + + You are banned until %1. + + + + + + You are banned indefinitely. + + + + + This server requires user registration. Do you want to register now? + + + + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + + Unknown login error: %1 + + + + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + + Socket error: %1 + + + + + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. +Local version is %1, remote version is %2. + + + + + Your Cockatrice client is obsolete. Please update your Cockatrice version. +Local version is %1, remote version is %2. + + + + + Connecting to %1... + + + + + Registering to %1 as %2... + + + + + Disconnected + + + + + Connected, logging in at %1 + + + + + &Connect... + + + + + &Disconnect + + + + + Start &local game... + + + + + &Watch replay... + + + + + &Deck editor + + + + + &Full screen + + + + + &Register to server... + + + + + &Settings... + + + + + + &Exit + + + + + A&ctions + + + + + &Cockatrice + + + + + &About Cockatrice + + + + + &Help + + + + + Check for card updates... + + + + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + + Information + + + + + Troubleshooting + + + + + F.A.Q. + + + + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + + failed to start. + + + + + crashed. + + + + + timed out. + + + + + write error. + + + + + read error. + + + + + unknown error. + + + + + MessageLogWidget + + + The game has been closed. + + + + + %1 is now watching the game. + + + + + %1 is not watching the game any more. + + + + %1 draws %n card(s). + + %1 draws a card. + %1 draws %n cards. + + + + + You have joined game #%1. + female + + + + + You have joined game #%1. + male + + + + + You are watching a replay of game #%1. + female + + + + + You are watching a replay of game #%1. + male + + + + + %1 has joined the game. + female + + + + + %1 has joined the game. + male + + + + + %1 has left the game. + female + + + + + %1 has left the game. + male + + + + + You have been kicked out of the game. + + + + + %1 has loaded a deck (%2). + + + + + %1 has loaded a deck with %2 sideboard cards (%3). + + + + + %1 is ready to start the game. + female + + + + + %1 is ready to start the game. + male + + + + + %1 is not ready to start the game any more. + female + + + + + %1 is not ready to start the game any more. + male + + + + + %1 has locked her sideboard. + female + + + + + %1 has locked his sideboard. + male + + + + + %1 has unlocked her sideboard. + female + + + + + %1 has unlocked his sideboard. + male + + + + + %1 has conceded the game. + female + + + + + %1 has conceded the game. + male + + + + + %1 has restored connection to the game. + female + + + + + %1 has restored connection to the game. + male + + + + + %1 has lost connection to the game. + female + + + + + %1 has lost connection to the game. + male + + + + + %1 shuffles %2. + female + + + + + %1 shuffles %2. + male + + + + + %1 rolls a %2 with a %3-sided die. + female + + + + + %1 rolls a %2 with a %3-sided die. + male + + + + %1 draws %n card(s). + female + + %1 draws a card. + %1 draws %n cards. + + + + %1 draws %n card(s). + male + + %1 draws a card. + %1 draws %n cards. + + + + + %1 undoes his last draw. + + + + + %1 undoes her last draw. + + + + + %1 undoes his last draw (%2). + + + + + %1 undoes her last draw (%2). + + + + + from exile + + + + + the bottom card of %1's library + + + + + the bottom card of his library + + + + + the bottom card of her library + + + + + from the bottom of %1's library + + + + + from the bottom of his library + + + + + from the bottom of her library + + + + + the top card of %1's library + + + + + the top card of his library + + + + + the top card of her library + + + + + from the top of %1's library + + + + + from the top of his library + + + + + from the top of her library + + + + + from %1's library + + + + + from library + + + + + from sideboard + + + + + from the stack + + + + + %1 gives %2 control over %3. + + + + + %1 puts %2 into play tapped%3. + + + + + %1 puts %2 into play%3. + + + + + %1 exiles %2%3. + + + + + %1 puts %2%3 into his library. + + + + + %1 puts %2%3 into her library. + + + + + %1 puts %2%3 on bottom of his library. + + + + + %1 puts %2%3 on bottom of her library. + + + + + %1 puts %2%3 on top of his library. + + + + + %1 puts %2%3 on top of her library. + + + + + %1 puts %2%3 into his library at position %4. + + + + + %1 puts %2%3 into her library at position %4. + + + + + %1 moves %2%3 to sideboard. + + + + + %1 plays %2%3. + + + + + %1 takes a mulligan to %n. + female + + + + + %1 takes a mulligan to %n. + male + + + + + %1 flips %2 face-down. + female + + + + + %1 flips %2 face-down. + male + + + + + %1 flips %2 face-up. + female + + + + + %1 flips %2 face-up. + male + + + + + %1 destroys %2. + female + + + + + %1 destroys %2. + male + + + + + %1 unattaches %2. + female + + + + + %1 unattaches %2. + male + + + + + %1 creates token: %2%3. + female + + + + + %1 creates token: %2%3. + male + + + + + %1 points from her %2 to herself. + female + + + + + %1 points from his %2 to himself. + male + + + + + %1 points from her %2 to %3. + p1 female, p2 female + + + + + %1 points from her %2 to %3. + p1 female, p2 male + + + + + %1 points from his %2 to %3. + p1 male, p2 female + + + + + %1 points from his %2 to %3. + p1 male, p2 male + + + + + %1 points from %2's %3 to herself. + card owner female, target female + + + + + %1 points from %2's %3 to herself. + card owner male, target female + + + + + %1 points from %2's %3 to himself. + card owner female, target male + + + + + %1 points from %2's %3 to himself. + card owner male, target male + + + + + %1 points from %2's %3 to %4. + p1 female, p2 female, p3 female + + + + + %1 points from %2's %3 to %4. + p1 female, p2 female, p3 male + + + + + %1 points from %2's %3 to %4. + p1 female, p2 male, p3 female + + + + + %1 points from %2's %3 to %4. + p1 female, p2 male, p3 male + + + + + %1 points from %2's %3 to %4. + p1 male, p2 female, p3 female + + + + + %1 points from %2's %3 to %4. + p1 male, p2 female, p3 male + + + + + %1 points from %2's %3 to %4. + p1 male, p2 male, p3 female + + + + + %1 points from %2's %3 to %4. + p1 male, p2 male, p3 male + + + + + %1 points from her %2 to her %3. + female + + + + + %1 points from his %2 to his %3. + male + + + + + %1 points from her %2 to %3's %4. + p1 female, p2 female + + + + + %1 points from her %2 to %3's %4. + p1 female, p2 male + + + + + %1 points from his %2 to %3's %4. + p1 male, p2 female + + + + + %1 points from his %2 to %3's %4. + p1 male, p2 male + + + + + %1 points from %2's %3 to her own %4. + card owner female, target female + + + + + %1 points from %2's %3 to her own %4. + card owner male, target female + + + + + %1 points from %2's %3 to his own %4. + card owner female, target male + + + + + %1 points from %2's %3 to his own %4. + card owner male, target male + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 female, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 female, p3 male + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 male, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 male, p3 male + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 female, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 female, p3 male + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 male, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 male, p3 male + + + + %1 places %n %2 counter(s) on %3 (now %4). + female + + + + %1 places %n %2 counter(s) on %3 (now %4). + male + + + + %1 removes %n %2 counter(s) from %3 (now %4). + female + + + + %1 removes %n %2 counter(s) from %3 (now %4). + male + + + + + %1 taps her permanents. + female + + + + + %1 untaps her permanents. + female + + + + + %1 taps his permanents. + male + + + + + %1 untaps his permanents. + male + + + + + %1 taps %2. + female + + + + + %1 untaps %2. + female + + + + + %1 taps %2. + male + + + + + %1 untaps %2. + male + + + + + %1 sets counter %2 to %3 (%4%5). + female + + + + + %1 sets counter %2 to %3 (%4%5). + male + + + + + %1 sets %2 to not untap normally. + female + + + + + %1 sets %2 to not untap normally. + male + + + + + %1 sets %2 to untap normally. + female + + + + + %1 sets %2 to untap normally. + male + + + + + %1 sets PT of %2 to %3. + female + + + + + %1 sets PT of %2 to %3. + male + + + + + %1 sets annotation of %2 to %3. + female + + + + + %1 sets annotation of %2 to %3. + male + + + + + %1 is looking at %2. + female + + + + + %1 is looking at %2. + male + + + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + + + + %1 stops looking at %2. + female + + + + + %1 stops looking at %2. + male + + + + + %1 reveals %2 to %3. + p1 female, p2 female + + + + + %1 reveals %2 to %3. + p1 female, p2 male + + + + + %1 reveals %2 to %3. + p1 male, p2 female + + + + + %1 reveals %2 to %3. + p1 male, p2 male + + + + + %1 reveals %2. + female + + + + + %1 reveals %2. + male + + + + + %1 randomly reveals %2%3 to %4. + p1 female, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 female, p2 male + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 randomly reveals %2%3. + female + + + + + %1 randomly reveals %2%3. + male + + + + + %1 peeks at face down card #%2. + female + + + + + %1 peeks at face down card #%2. + male + + + + + %1 peeks at face down card #%2: %3. + female + + + + + %1 peeks at face down card #%2: %3. + male + + + + + %1 reveals %2%3 to %4. + p1 female, p2 female + + + + + %1 reveals %2%3 to %4. + p1 female, p2 male + + + + + %1 reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 reveals %2%3. + female + + + + + %1 reveals %2%3. + male + + + + + %1 is now keeping the top card %2 revealed. + + + + + %1 is not revealing the top card %2 any longer. + + + + + It is now %1's turn. + female + + + + + It is now %1's turn. + male + + + + + + a card + + + + %1 places %n counter(s) (%2) on %3 (now %4). + + %1 places a counter (%2) on %3 (now %4). + %1 places %n counters (%2) on %3 (now %4). + + + + %1 removes %n counter(s) (%2) from %3 (now %4). + + %1 removes a counter (%2) from %3 (now %4). + %1 removes %n counters (%2) from %3 (now %4). + + + + + red + + + + + yellow + + + + + green + + + + + The game has started. + + + + + %1 draws his initial hand. + + + + + %1 draws her initial hand. + + + + %1 places %n %2 counter(s) on %3 (now %4). + + %1 places a %2 counter on %3 (now %4). + %1 places %n %2 counters on %3 (now %4). + + + + %1 removes %n %2 counter(s) from %3 (now %4). + + %1 removes a %2 counter from %3 (now %4). + %1 removes %n %2 counters from %3 (now %4). + + + + + ending phase + + + + + untap step + + + + + %1 draws %2 card(s). + female + + + + + %1 draws %2 card(s). + male + + + + + from play + + + + + from her graveyard + + + + + from his graveyard + + + + + from her hand + + + + + from his hand + + + + + %1 puts %2%3 into her graveyard. + + + + + %1 puts %2%3 into his graveyard. + + + + + %1 moves %2%3 to her hand. + + + + + %1 moves %2%3 to his hand. + + + + + %1 attaches %2 to %3's %4. + p1 female, p2 female + + + + + %1 attaches %2 to %3's %4. + p1 female, p2 male + + + + + %1 attaches %2 to %3's %4. + p1 male, p2 female + + + + + %1 attaches %2 to %3's %4. + p1 male, p2 male + + + + + %1 places %2 %3 counter(s) on %4 (now %5). + female + + + + + %1 places %2 %3 counter(s) on %4 (now %5). + male + + + + + %1 removes %2 %3 counter(s) from %4 (now %5). + female + + + + + %1 removes %2 %3 counter(s) from %4 (now %5). + male + + + + + %1 is looking at the top %2 card(s) %3. + female + + + + + %1 is looking at the top %2 card(s) %3. + male + + + + + upkeep step + + + + + draw step + + + + + first main phase + + + + + beginning of combat step + + + + + declare attackers step + + + + + declare blockers step + + + + + combat damage step + + + + + end of combat step + + + + + second main phase + + + + + It is now the %1. + + + + + MessagesSettingsPage + + + Chat settings + + + + + Custom alert words + + + + + Enable chat mentions + + + + + Enable mention completer + + + + + In-game message macros + + + + + Ignore chat room messages sent by unregistered users + + + + + Ignore private messages sent by unregistered users + + + + + Enable desktop notifications for private messages + + + + + Separate words with a space, alphanumeric characters only + + + + + + Invert text color + + + + + Enable desktop notification for mentions. + + + + + + (Color is hexadecimal) + + + + + Add message + + + + + Message: + + + + + PhasesToolbar + + + Untap step + + + + + Upkeep step + + + + + Draw step + + + + + First main phase + + + + + Beginning of combat step + + + + + Declare attackers step + + + + + Declare blockers step + + + + + Combat damage step + + + + + End of combat step + + + + + Second main phase + + + + + End of turn step + + + + + Player + + + &View library + + + + + Move top cards to &graveyard... + + + + + View &top cards of library... + + + + + &View graveyard + + + + + &View sideboard + + + + + Player "%1" + + + + + + + + &Hand + + + + + &Reveal hand to... + + + + + Reveal r&andom card to... + + + + + &Library + + + + + + + + &Graveyard + + + + + &Sideboard + + + + + Red + + + + + Yellow + + + + + Green + + + + + View top cards of library + + + + + Number of cards: + + + + + &Draw card + + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + + + + + + + &Bottom of library + + + + + &Move graveyard to... + + + + + &Move exile to... + + + + + Reveal &library to... + + + + + &Always reveal top card + + + + + O&pen deck in deck editor + + + + + &Undo last draw + + + + + Play top card &face down + + + + + Move top cards to &exile... + + + + + Put top card on &bottom + + + + + Put bottom card &in graveyard + + + + + Cr&eate predefined token + + + + + C&ard + + + + + &All players + + + + + &Play + + + + + &Hide + + + + + Play &Face Down + + + + + &Tap + + + + + &Untap + + + + + Toggle &normal untapping + + + + + &Flip + + + + + &Peek at card face + + + + + &Clone + + + + + Attac&h to card... + + + + + Unattac&h + + + + + &Draw arrow... + + + + + &Increase power + + + + + &Decrease power + + + + + I&ncrease toughness + + + + + D&ecrease toughness + + + + + In&crease power and toughness + + + + + Dec&rease power and toughness + + + + + Set &power and toughness... + + + + + &Set annotation... + + + + + &Add counter (%1) + + + + + &Remove counter (%1) + + + + + &Set counters (%1)... + + + + + Draw cards + + + + + + + + Number: + + + + + Move top cards to grave + + + + + Move top cards to exile + + + + + Roll die + + + + + Number of sides: + + + + + Set power/toughness + + + + + Please enter the new PT: + + + + + Set annotation + + + + + Please enter the new annotation: + + + + + Set counters + + + + + Cr&eate related card + + + + + QMenuBar + + + Services + + + + + Hide %1 + + + + + Hide Others + + + + + Show All + + + + + Preferences... + + + + + Quit %1 + + + + + About %1 + + + + + QObject + + + Cockatrice replays (*.cor) + + + + + Common deck formats (*.cod *.dec *.mwDeck) + + + + + All files (*.*) + + + + + RemoteDeckList_TreeModel + + + Name + + + + + ID + + + + + Upload time + + + + + RemoteReplayList_TreeModel + + + ID + + + + + Name + + + + + Players + + + + + Keep + + + + + Time started + + + + + Duration (sec) + + + + + RoomSelector + + + Rooms + + + + + Joi&n + + + + + Room + + + + + Description + + + + + Permissions + + + + + Players + + + + + Games + + + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + + + + SetsModel + + + Enabled + + + + + Set type + + + + + Set code + + + + + Long name + + + + + Release date + + + + + ShutdownDialog + + + &Reason for shutdown: + + + + + &Time until shutdown (minutes): + + + + + Shut down server + + + + + SoundSettingsPage + + + Choose path + + + + + Enable &sounds + + + + + Path to sounds directory: + + + + + Test system sound engine + + + + + Sound settings + + + + + Master volume requires QT5 + + + + + Master volume + + + + + TabAdmin + + + Update server &message + + + + + &Shut down server + + + + + &Reload configuration + + + + + Server administration functions + + + + + &Unlock functions + + + + + &Lock functions + + + + + Unlock administration functions + + + + + Do you really want to unlock the administration functions? + + + + + Administration + + + + + TabDeckEditor + + + &Print deck... + + + + + &Close + + + + + &Edit sets... + + + + + Filters + + + + + &Clear all filters + + + + + Delete selected + + + + + Deck &name: + + + + + &Comments: + + + + + Hash: + + + + + &New deck + + + + + &Load deck... + + + + + &Save deck + + + + + Save deck &as... + + + + + Load deck from cl&ipboard... + + + + + Save deck to clip&board + + + + + &Analyze deck on deckstats.net + + + + + Open custom image folder + + + + + Open custom sets folder + + + + + Add card to &maindeck + + + + + Add card to &sideboard + + + + + &Deck Editor + + + + + C&ard Database + + + + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + + Welcome + + + + + Hi! It seems like you're running this version of Cockatrice for the first time. +All the sets in the card database have been enabled. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + + + + + &Remove row + + + + + &Increment number + + + + + &Decrement number + + + + + Edit &tokens... + + + + + Deck: %1 + + + + + Are you sure? + + + + + The decklist has been modified. +Do you want to save the changes? + + + + + Load deck + + + + + + + Error + + + + + The deck could not be saved. + + + + + + The deck could not be saved. +Please check that the directory is writable and try again. + + + + + Save deck + + + + + TabDeckStorage + + + Local file system + + + + + Server deck storage + + + + + + Open in deck editor + + + + + Upload deck + + + + + Download deck + + + + + + New folder + + + + + + Delete + + + + + Enter deck name + + + + + This decklist does not have a name. +Please enter a name: + + + + + Unnamed deck + + + + + Delete local file + + + + + + + Are you sure you want to delete "%1"? + + + + + Name of new folder: + + + + + Delete remote folder + + + + + Delete remote deck + + + + + Deck storage + + + + + TabGame + + + &Phases + + + + + &Game + + + + + Next &phase + + + + + Next &turn + + + + + &Remove all local arrows + + + + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + + Game &information + + + + + &Concede + + + + + &Leave game + + + + + C&lose replay + + + + + &Say: + + + + + Concede + + + + + Are you sure you want to concede this game? + + + + + Leave game + + + + + Are you sure you want to leave this game? + + + + + You are flooding the game. Please wait a couple of seconds. + + + + + You have been kicked out of the game. + + + + + REPLAY + + + + + TabMessage + + + Private &chat + + + + + &Leave + + + + + %1 - Private chat + + + + + This user is ignoring you. + + + + + Private message from + + + + + %1 has left the server. + + + + + %1 has joined the server. + + + + + TabReplays + + + Local file system + + + + + Server replay storage + + + + + + Watch replay + + + + + + Delete + + + + + Download replay + + + + + Toggle expiration lock + + + + + Delete local file + + + + + Are you sure you want to delete "%1"? + + + + + Delete remote replay + + + + + Are you sure you want to delete the replay of game %1? + + + + + Game replays + + + + + TabRoom + + + &Say: + + + + + Chat + + + + + &Room + + + + + &Leave room + + + + + &Clear chat + + + + + Chat Settings... + + + + + mentioned you. + + + + + Click to view + + + + + You are flooding the chat. Please wait a couple of seconds. + + + + + TabServer + + + Server + + + + + TabSupervisor + + + Are you sure? + + + + + There are still open games. Are you sure you want to quit? + + + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + + + + TabUserLists + + + Add to Buddy List + + + + + Add to Ignore List + + + + + Account + + + + + UserContextMenu + + + User &details + + + + + Private &chat + + + + + Show this user's &games + + + + + Add to &buddy list + + + + + Remove from &buddy list + + + + + Add to &ignore list + + + + + Remove from &ignore list + + + + + Kick from &game + + + + + Ban from &server + + + + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + + %1's games + + + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + + + + UserInfoBox + + + User information + + + + + Real name: + + + + + Pronouns: + + + + + Location: + + + + + User level: + + + + + Account Age: + + + + + Edit + + + + + Change password + + + + + Change avatar + + + + + Administrator + + + + + Moderator + + + + + Registered user + + + + + + Unregistered user + + + + + Unknown + + + + + Year + + + + + Years + + + + + Day + + + + + Days + + + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + + + + UserInterfaceSettingsPage + + + General interface settings + + + + + Enable notifications in taskbar + + + + + Notify in the taskbar for game events while you are spectating + + + + + &Double-click cards to play them (instead of single-click) + + + + + &Play all nonlands onto the stack (not the battlefield) by default + + + + + Annotate card text on tokens + + + + + Animation settings + + + + + &Tap/untap animation + + + + + UserList + + + Users connected to server: %1 + + + + + Users in this room: %1 + + + + + Buddies online: %1 / %2 + + + + + Ignored users online: %1 / %2 + + + + + WndSets + + + Enable all sets + + + + + Disable all sets + + + + + Move selected set up + + + + + Move selected set to the top + + + + + Move selected set down + + + + + Move selected set to the bottom + + + + + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed + + + + + Edit sets + + + + + Success + + + + + The sets database has been saved successfully. + + + + + ZoneViewWidget + + + sort by name + + + + + sort by type + + + + + shuffle when closing + + + + + pile view + + + + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_zh-Hans.ts b/cockatrice/translations/cockatrice_zh-Hans.ts index 87d7668c..66216c59 100644 --- a/cockatrice/translations/cockatrice_zh-Hans.ts +++ b/cockatrice/translations/cockatrice_zh-Hans.ts @@ -2,17 +2,17 @@ AbstractCounter - + &Set counter... 设置指示物为... - + Set counter 设置指示物 - + New value for counter '%1': 新的指示物价值'%1': @@ -20,86 +20,86 @@ AppearanceSettingsPage - + Zone background pictures 区域背景图片 - + Hand background: 手牌区域背景: - + Stack background: 堆叠背景: - + Table background: 桌面背景: - + Player info background: 牌手信息背景: - + Card back: 牌背: - + Card rendering 牌面 - + Display card names on cards having a picture 显示有图卡牌的名称 - + Scale cards on mouse over - + Hand layout 手牌区域布局 - + Display hand horizontally (wastes space) 水平显示手牌区域 (浪费空间) - + Enable left justification - + Table grid layout 表格布局 - + Invert vertical coordinate 反转垂直坐标 - + Minimum player count for multi-column layout: 界面布局之内能够容纳的牌手栏数量: - - - - - + + + + + Choose path 选择路径 @@ -107,97 +107,102 @@ BanDialog - + ban &user name 禁止该用户 - + ban &IP address 禁止该IP地址 - + + ban client I&D + + + + Ban type 禁止类型 - + &permanent ban 永久禁止 - + &temporary ban 暂时禁止 - + &Days: 天: - + &Hours: 小时: - + &Minutes: 分钟: - + Duration of the ban 持续时长 - + Please enter the reason for the ban. This is only saved for moderators and cannot be seen by the banned person. 请输入禁止原因. 此项为管理缘由而保存并且对于这位被禁止的用户不可见. - + Please enter the reason for the ban that will be visible to the banned person. 请输入对于被禁止用户可见的禁止原因 - + &OK 确认 - + &Cancel 取消 - + Ban user from server 禁止此用户 - + Error 错误 - - You have to select a name-based or IP-based ban, or both. - 请选择一个名字或者IP地址禁止 任一或者两者 + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + CardDatabase - + New sets found - + %1 new set(s) have been found in the card database. Do you want to enable them? @@ -230,30 +235,53 @@ This is only saved for moderators and cannot be seen by the banned person.力量/防御力 + + CardFrame + + + Image + + + + + Description + + + + + Both + + + CardInfoText - + Name: 名称: - + Mana cost: 法术力费用: - + + Color(s): + + + + Card type: 卡牌类别: - + P / T: 力量/防御力: - + Loyalty: 忠诚值: @@ -276,27 +304,32 @@ This is only saved for moderators and cannot be seen by the banned person.显示卡牌以及描述 - + Name: 名称: - + Mana cost: 法术力费用: - + + Color(s): + + + + Card type: 卡牌类别: - + P / T: 力量/防御力: - + Loyalty: 忠诚值: @@ -560,12 +593,12 @@ This is only saved for moderators and cannot be seen by the banned person. DeckEditorSettingsPage - + Nothing is here... yet - + General 综合 @@ -573,17 +606,17 @@ This is only saved for moderators and cannot be seen by the banned person. DeckListModel - + Number 数量 - + Card 卡牌 - + Price 价格 @@ -605,42 +638,42 @@ This is only saved for moderators and cannot be seen by the banned person. DeckViewContainer - - Load &local deck - 从本地载入套牌 + + Load local deck + - - Load d&eck from server - 从服务器载入套牌 + + Load deck from server + - + Ready to s&tart 准备 - + S&ideboard unlocked 解锁备牌 - + S&ideboard locked 上锁备牌 - + Load deck 载入套牌 - + Error 错误 - + The selected file could not be loaded. 此文件无法被载入 @@ -686,37 +719,52 @@ This is only saved for moderators and cannot be seen by the banned person. DlgConnect - + + Previous Host + + + + + New Host + + + + &Host: 主机: - + + Enter host name + + + + &Port: 端口: - + Player &name: 玩家名字: - + P&assword: 密码: - + &Save password 记住密码 - + A&uto connect at start 启动时自动连接 - + Connect to server 连接服务器 @@ -724,82 +772,92 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateGame - + &Description: 描述: - + &Password: 密码: - + P&layers: 玩家: - + + Re&member settings + + + + Game type 房间类型 - + Only &buddies can join 只有好友可以加入 - + Only &registered users can join 只有注册用户可以加入 - + Joining restrictions 加入条件 - + &Spectators can watch 允许观看者 - + Spectators &need a password to watch 观看者需要密码 - + Spectators can see &hands 观看者可见手牌 - + Spectators can &chat 观看者可以聊天 - + Spectators 观看者 - + + &Clear + + + + Create game 创建一个房间 - + Game information 房间信息 - + Error 错误 - + Server error. 服务器错误. @@ -807,96 +865,169 @@ This is only saved for moderators and cannot be seen by the banned person. DlgCreateToken - + &Name: 名称: - + Token 衍生物 - + C&olor: 颜色: - + white 白色 - + blue - + black 黑色 - + red - + green 绿 - + multicolor 多色 - + colorless 无色 - + &P/T: 力量/防御力: - + &Annotation: 注释: - + &Destroy token when it leaves the table 此衍生物离场时会被摧毁 - + Token data 衍生物数据资料 - + Show &all tokens 显示所有衍生物 - + Show tokens from this &deck 显示此套牌所使用的衍生物 - + Choose token from list 从列表中选择衍生物 - + Create token 创造一个衍生物 + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + DlgEditTokens @@ -992,6 +1123,54 @@ Make sure to enable the 'token set' in the 'Edit sets...' di + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + DlgFilterGames @@ -1043,7 +1222,7 @@ Make sure to enable the 'token set' in the 'Edit sets...' di DlgLoadDeckFromClipboard - + &Refresh 刷新 @@ -1053,12 +1232,14 @@ Make sure to enable the 'token set' in the 'Edit sets...' di 从剪贴板读取套牌 - + + Error 错误 - + + Invalid deck list. 无效的牌表. @@ -1071,22 +1252,116 @@ Make sure to enable the 'token set' in the 'Edit sets...' di 读取套牌 + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + DlgSettings - - - + + + Error 错误 - + Unknown Error loading card database 读取卡牌数据库时出现未知错误 - + Your card database is invalid. Cockatrice may not function correctly with an invalid database @@ -1103,7 +1378,7 @@ Would you like to change your database location setting? 您想要重新设置卡牌数据库路径么? - + Your card database version is too old. This can cause problems loading card information or images @@ -1120,7 +1395,7 @@ Would you like to change your database location setting? 您想要重新设置卡牌数据库路径么? - + File Error loading your card database. Would you like to change your database location setting? @@ -1129,7 +1404,7 @@ Would you like to change your database location setting? 您想要重新设置卡牌数据库路径么? - + Your card database was loaded but contains no cards. Would you like to change your database location setting? @@ -1138,7 +1413,7 @@ Would you like to change your database location setting? 您想要重新设置卡牌数据库路径么? - + Your card database did not finish loading Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached @@ -1147,7 +1422,7 @@ Would you like to change your database location setting? - + Unknown card database load status Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues @@ -1156,63 +1431,58 @@ Would you like to change your database location setting? - + The path to your deck directory is invalid. Would you like to go back and set the correct path? 您想要重新设置卡牌数据库路径么? - + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? 您想要重新设置卡牌数据库路径么? - + Settings 设置 - + General 常规 - + Appearance 外观 - + User Interface 用户界面 - + Deck Editor 卡组编辑 - + Chat 聊天 - + Sound + + + Shortcuts + + GameSelector - - - C&reate - 创建 - - - - &Join - 加入 - @@ -1222,7 +1492,7 @@ Would you like to change your database location setting? - + Error 错误 @@ -1267,37 +1537,47 @@ Would you like to change your database location setting? 你屏蔽了这个与游戏的创造者. - + Join game 加入游戏 - + Password: 密码: - + Please join the respective room first. 请各自加入一个房间. - + Games 游戏 - + &Filter games - 过滤房间 + - + C&lear filter - 清除筛选项目 + - + + C&reate + + + + + &Join + + + + J&oin as spectator 观看 @@ -1414,97 +1694,107 @@ Would you like to change your database location setting? GeneralSettingsPage - + Reset/Clear Downloaded Pictures - - - - - + + + + + Choose path 选择路径 - + Success - + Downloaded card pictures have been reset. - + Error 错误 - + One or more downloaded card pictures could not be cleared. - + Personal settings 个人设置 - + Language: 语言: - + Download card pictures on the fly 下载卡牌上的图片 - - Download high-quality card pictures + + Download card pictures from a custom URL - + + Custom Card Download URL: + + + + + Linking FAQ + + + + Paths 路径 - + Decks directory: 卡组路径: - + Replays directory: 游戏录像目录: - + Pictures directory: 图片目录: - + Card database: 卡牌数据库: - + Token database: - + Picture cache size: - - + + English 简体中文 (Chinese Simplified) @@ -1512,56 +1802,49 @@ Would you like to change your database location setting? MainWindow - + There are too many concurrent connections from your address. 你的地址有太多连接. - + Scheduled server shutdown. 预定服务器关闭. - + Banned by moderator 禁止版主 - + Expected end time: %1 预计结束时间: %1 - + This ban lasts indefinitely. 这个封禁是永久的. - - - Invalid username. -You may only use A-Z, a-z, 0-9, _, ., and - in your username. - - - - + Connection closed 连接关闭 - + The server has terminated your connection. Reason: %1 服务器中断连接. 原因: %1 - + Scheduled server shutdown 预定服务器关闭 - + The server is going to be restarted in %n minute(s). All running games will be lost. Reason for shutdown: %1 @@ -1570,305 +1853,484 @@ Reason for shutdown: %1 关闭原因: %1 - + Number of players 玩家人数 - + Please enter the number of players. 请输入玩家的数量. - - + + Player %1 玩家 %1 - + Load replay 载入游戏录像 - + About Cockatrice 关于Cockatrice鸡蛇 - + Version %1 版本 %1 - + Translators: - + Project Manager: - + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + Past Project Managers: - + Developers: - + Our Developers - + Help Develop! - + Recognition Page - + Help Translate! - + Support: - + Report an Issue - - Suggest an Improvement - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + Error 错误 - + Server timeout 服务器超时 - + Incorrect username or password. Please check your authentication information and try again. - + There is already an active session using this user name. Please close that session first and re-login. 已经有一个在线用户正在使用这个用户名. 首先请关闭这个对话框然后重新登陆. - + + You are banned until %1. 你被禁止直到: %1. - + + You are banned indefinitely. 你被永久封禁. - - This server requires user registration. + + This server requires user registration. Do you want to register now? - + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + Unknown login error: %1 未知的登陆错误: %1 - + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + Socket error: %1 接口错误: %1 - + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. Local version is %1, remote version is %2. 你正在连接到一个过时的服务器,请下调你的版本或者连接到一个匹配的服务器. 你当前的版本 %1, 服务器版本 %2. - + Your Cockatrice client is obsolete. Please update your Cockatrice version. Local version is %1, remote version is %2. 你使用的客户端意见过时,请使用更新你版本. 你当前的版本 %1, 服务器版本 %2. - + Connecting to %1... 连接到 %1... - + + Registering to %1 as %2... + + + + Disconnected 断开连接 - + Connected, logging in at %1 连接登陆到 %1 - - Logged in as %1 at %2 - - - - + &Connect... 连接... - + &Disconnect 断开连接 - + Start &local game... 开始本地游戏... - + &Watch replay... 观看录像... - + &Deck editor 卡组编辑 - + &Full screen 全屏 - + + &Register to server... + + + + &Settings... 设置... - - + + &Exit 退出 - + A&ctions - + &Cockatrice Cockatrice鸡蛇 - + &About Cockatrice 关于Cockatrice鸡蛇 - + &Help 帮助 - - Check card updates... + + Check for card updates... - - + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + Information - - A card update is already ongoing. + + Troubleshooting - - Unable to run the card updater: + + F.A.Q. - + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + failed to start. - + crashed. - + timed out. - + write error. - + read error. - + unknown error. - - - The card updater exited with an error: %1 - - - - - Card update completed successfully. Will now reload card database. - - MessageLogWidget @@ -2107,62 +2569,62 @@ Local version is %1, remote version is %2. - + the bottom card of %1's library - + the bottom card of his library - + the bottom card of her library - + from the bottom of %1's library - + from the bottom of his library - + from the bottom of her library - + the top card of %1's library - + the top card of his library - + the top card of her library - + from the top of %1's library - + from the top of his library - + from the top of her library @@ -2565,304 +3027,304 @@ Local version is %1, remote version is %2. - + %1 taps her permanents. female - + %1 untaps her permanents. female - + %1 taps his permanents. male - + %1 untaps his permanents. male - + %1 taps %2. female - + %1 untaps %2. female - + %1 taps %2. male - + %1 untaps %2. male - + %1 sets counter %2 to %3 (%4%5). female - + %1 sets counter %2 to %3 (%4%5). male - + %1 sets %2 to not untap normally. female - + %1 sets %2 to not untap normally. male - + %1 sets %2 to untap normally. female - + %1 sets %2 to untap normally. male - + %1 sets PT of %2 to %3. female - + %1 sets PT of %2 to %3. male - + %1 sets annotation of %2 to %3. female - + %1 sets annotation of %2 to %3. male + + + %1 is looking at %2. + female + + %1 is looking at %2. - female + male + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + - - %1 is looking at %2. - male - - - - %1 is looking at the top %n card(s) %2. + + %1 stops looking at %2. female - - - - %1 is looking at the top %n card(s) %2. - male - + %1 stops looking at %2. - female - - - - - %1 stops looking at %2. male - + %1 reveals %2 to %3. p1 female, p2 female - + %1 reveals %2 to %3. p1 female, p2 male - + %1 reveals %2 to %3. p1 male, p2 female - + %1 reveals %2 to %3. p1 male, p2 male + + + %1 reveals %2. + female + + %1 reveals %2. - female - - - - - %1 reveals %2. male - + %1 randomly reveals %2%3 to %4. p1 female, p2 female - + %1 randomly reveals %2%3 to %4. p1 female, p2 male - + %1 randomly reveals %2%3 to %4. p1 male, p2 female - + %1 randomly reveals %2%3 to %4. p1 male, p2 male - + %1 randomly reveals %2%3. female - + %1 randomly reveals %2%3. male - + %1 peeks at face down card #%2. female - + %1 peeks at face down card #%2. male - + %1 peeks at face down card #%2: %3. female - + %1 peeks at face down card #%2: %3. male - + %1 reveals %2%3 to %4. p1 female, p2 female - + %1 reveals %2%3 to %4. p1 female, p2 male - + %1 reveals %2%3 to %4. p1 male, p2 female - + %1 reveals %2%3 to %4. p1 male, p2 male - + %1 reveals %2%3. female - + %1 reveals %2%3. male - + %1 is now keeping the top card %2 revealed. - + %1 is not revealing the top card %2 any longer. - + It is now %1's turn. female - + It is now %1's turn. male - + a card @@ -2925,12 +3387,12 @@ Local version is %1, remote version is %2. - + ending phase - + untap step 重置阶段 @@ -3040,64 +3502,64 @@ Local version is %1, remote version is %2. - + %1 is looking at the top %2 card(s) %3. female - + %1 is looking at the top %2 card(s) %3. male - + upkeep step 维持阶段 - + draw step 抓卡阶段 - + first main phase 主要阶段 - + beginning of combat step 战斗开始阶段 - + declare attackers step 宣告进攻阶段 - + declare blockers step 宣告阻挡阶段 - + combat damage step 战斗伤害阶段 - + end of combat step 战斗结束阶段 - + second main phase 第二个主要阶段 - + It is now the %1. @@ -3105,62 +3567,74 @@ Local version is %1, remote version is %2. MessagesSettingsPage - + Chat settings - + + Custom alert words + + + + Enable chat mentions - + + Enable mention completer + + + + In-game message macros - - Ignore unregistered users in main chat + + Ignore chat room messages sent by unregistered users - - Ignore chat room messages sent by unregistered users. + + Ignore private messages sent by unregistered users - - Ignore private messages sent by unregistered users. + + Enable desktop notifications for private messages - + + Separate words with a space, alphanumeric characters only + + + + + Invert text color - - Enable desktop notifications for private messages. - - - - + Enable desktop notification for mentions. - + + (Color is hexadecimal) - + Add message 添加信息 - + Message: 信息: @@ -3226,336 +3700,337 @@ Local version is %1, remote version is %2. Player - + &View library 显示卡组 - + Move top cards to &graveyard... 将卡组置入墓地... - + View &top cards of library... - + &View graveyard 显示墓地 - + &View sideboard 显示备牌 - + Player "%1" 玩家 "%1" - - - + + + + &Hand 手牌 - + &Reveal hand to... - + Reveal r&andom card to... 展示随机卡牌到 - + &Library 卡组 - - - + + + &Graveyard 墓地 - + &Sideboard 备牌 - + Red - + Yellow - + Green - + View top cards of library - + Number of cards: 卡牌数量: - + &Draw card 抓卡 - + Reveal top cards of library - + Number of cards: (max. %1) - + &View exile 显示放逐区域 - - - + + + &Exile 放逐 - + Reveal t&op cards to... - + D&raw cards... 抓多张卡... - + Take &mulligan 起手抓牌 - + &Shuffle 切洗卡组 - + &Counters 数值 - + &Untap all permanents 重置所有永久物 - + R&oll die... 抛骰子... - + &Create token... 创造一个衍生物... - + C&reate another token 将另一个衍生物放置进场 - + S&ay - + &Move hand to... - - - - + + + + &Top of library - - - - + + + + &Bottom of library - + &Move graveyard to... - + &Move exile to... - + Reveal &library to... - + &Always reveal top card 一直展示卡牌顶端卡牌 - + O&pen deck in deck editor 打开卡组编辑 - + &Undo last draw 撤销最后抓卡 - + Play top card &face down - + Move top cards to &exile... 移动顶端卡牌到放逐区域... - + Put top card on &bottom 放顶端卡牌到牌库底 - + Put bottom card &in graveyard - + Cr&eate predefined token 将预设衍生物放置进场 - + C&ard 卡牌 - + &All players 全部玩家 - + &Play 开始 - + &Hide 隐藏 - + Play &Face Down - + &Tap 横置 - + &Untap 重置 - + Toggle &normal untapping 锁定通常重置 - + &Flip 翻面 - + &Peek at card face 查看卡牌背面 - + &Clone 复制 - + Attac&h to card... 结附卡牌... - + Unattac&h 取消结附 - + &Draw arrow... 划箭头... - + &Increase power 增加力量 - + &Decrease power 降低防御 - + I&ncrease toughness 降低防御 - + D&ecrease toughness 降低防御 @@ -3565,22 +4040,22 @@ Local version is %1, remote version is %2. 增加力量和防御 - + Dec&rease power and toughness 降低力量和防御 - + Set &power and toughness... 设置力量和防御... - + &Set annotation... 设置注释... - + &Add counter (%1) 增加一点%1色指示物 @@ -3590,103 +4065,108 @@ Local version is %1, remote version is %2. 移除一点%1色指示物 - + &Set counters (%1)... 设置数值 (%1)... - + Draw cards 抓卡 - - - - + + + + Number: 数值: - + Move top cards to grave 顶端卡牌置入墓地 - + Move top cards to exile 移动顶端卡牌到放逐区域 - + Roll die 抛骰子 - + Number of sides: 面数: - + Set power/toughness 设置力量和防御 - + Please enter the new PT: 请输入力量和防御值: - + Set annotation 设置注释 - + Please enter the new annotation: 请输入注释: - + Set counters 设置数值 + + + Cr&eate related card + + QMenuBar - + Services - + Hide %1 隐藏 %1 - + Hide Others - + Show All - + Preferences... - + Quit %1 - + About %1 关于 %1 @@ -3694,7 +4174,7 @@ Local version is %1, remote version is %2. QObject - + Cockatrice replays (*.cor) 鸡蛇录像文件 (*.cor) @@ -3784,14 +4264,43 @@ Local version is %1, remote version is %2. + Permissions + + + + Players 玩家 - + Games 游戏 + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + SetsModel @@ -3834,7 +4343,7 @@ Local version is %1, remote version is %2. 将要关闭的时间 (分钟): - + Shut down server 关闭服务器 @@ -3842,37 +4351,37 @@ Local version is %1, remote version is %2. SoundSettingsPage - + Choose path - + Enable &sounds - + Path to sounds directory: - + Test system sound engine - + Sound settings - + Master volume requires QT5 - + Master volume @@ -3880,42 +4389,47 @@ Local version is %1, remote version is %2. TabAdmin - + Update server &message 更新服务器信息 - + &Shut down server 关闭服务器 - + + &Reload configuration + + + + Server administration functions 服务器管理功能 - + &Unlock functions 解锁功能 - + &Lock functions 锁定功能 - + Unlock administration functions 解锁管理员功能 - + Do you really want to unlock the administration functions? 你真的想要解锁管理员功能? - + Administration 管理员 @@ -3923,181 +4437,221 @@ Local version is %1, remote version is %2. TabDeckEditor - + &Print deck... 打印卡组... - + &Close 关闭 - + &Edit sets... 编辑环境... - - &Clear search - 清除搜索结果 + + Filters + - + + &Clear all filters + + + + + Delete selected + + + + Deck &name: 卡组名称: - + &Comments: 评论: - + Hash: - + &New deck 创建新卡组 - + &Load deck... 读取套牌... - + &Save deck 保存卡组 - + Save deck &as... 保存卡组... - + Load deck from cl&ipboard... 从剪贴板读取套牌... - + Save deck to clip&board 保存卡组到剪切板 - + &Analyze deck on deckstats.net 在deckstats.net对卡组进行分析 - + Open custom image folder - + + Open custom sets folder + + + + Add card to &maindeck 添加卡牌到主要牌组 - + Add card to &sideboard 添加卡牌到备牌 - + &Deck Editor 卡组编辑 - + C&ard Database - + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + Welcome - - Hi! Its seems like it's the first time you run this version of Cockatrice. + + Hi! It seems like you're running this version of Cockatrice for the first time. All the sets in the card database have been enabled. -Read more about changing the set order or disabling specific sets in the the "Edit Sets" window. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. - - Show card text only - - - - + &Remove row 移除卡牌 - + &Increment number 添加卡牌张数 - + &Decrement number 减少卡牌张数 - + Edit &tokens... 编辑源生物... - + Deck: %1 卡组: %1 - + Are you sure? 你确定? - + The decklist has been modified. Do you want to save the changes? 卡组列表已被修改。 你想要保存更改吗? - + Load deck 读取套牌 - - - + + + Error 错误 - + The deck could not be saved. 不能保存卡组到服务器. - - + + The deck could not be saved. Please check that the directory is writable and try again. 卡组不能保存到服务器. 请检查目录是否可用后再重试. - + Save deck 保存卡组 @@ -4195,94 +4749,99 @@ Please enter a name: TabGame - + &Phases 阶段 - + &Game 游戏 - + Next &phase 下个阶段 - + Next &turn 下个回合 - + &Remove all local arrows 重置所有箭头 - + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + Game &information 游戏信息 - + &Concede 放弃游戏 - + &Leave game 离开游戏 - + C&lose replay 关闭游戏录像 - + &Say: 说: - + Concede 放弃游戏 - + Are you sure you want to concede this game? 你确定放弃这个游戏? - + Leave game 离开游戏 - + Are you sure you want to leave this game? 你确定离开这这个游戏? - + You are flooding the game. Please wait a couple of seconds. - + You have been kicked out of the game. - - Replay %1: %2 - 游戏录像 %1: %2 - - - - Game %1: %2 - 游戏 %1: %2 + + REPLAY + @@ -4326,54 +4885,54 @@ Please enter a name: TabReplays - + Local file system 本地文件系统 - + Server replay storage 服务器录像仓库 - + Watch replay 观看录像 - - + + Delete 删除 - + Download replay 下载游戏录像 - + Toggle expiration lock 过期锁定 - + Delete local file 删除本地文件 - + Are you sure you want to delete "%1"? 你确定想要删除"%1"? - + Delete remote replay 删除服务器上的录像 - + Are you sure you want to delete the replay of game %1? 你确定要删除这这个游戏的录像吗 %1? @@ -4386,47 +4945,47 @@ Please enter a name: TabRoom - + &Say: 说: - + Chat 聊天 - + &Room 房间 - + &Leave room 离开房间 - + &Clear chat - + Chat Settings... - + mentioned you. - + Click to view - + You are flooding the chat. Please wait a couple of seconds. 你正在灌水,请等待几秒再发言. @@ -4442,15 +5001,25 @@ Please enter a name: TabSupervisor - + Are you sure? 你确定? - + There are still open games. Are you sure you want to quit? 游戏还在继续, 你确定要退出吗? + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + TabUserLists @@ -4473,169 +5042,301 @@ Please enter a name: UserContextMenu - + User &details 用户详细信息 - + Private &chat - + Show this user's &games 显示这个用户的游戏 - + Add to &buddy list 加入到好友列表 - + Remove from &buddy list - + Add to &ignore list 加入到屏蔽列表 - + Remove from &ignore list - + Kick from &game 从游戏中踢出 - + Ban from &server 在服务器中禁止 - + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + %1's games %1的游戏 + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + UserInfoBox - + User information 用户信息 - + Real name: 真实姓名: - - Gender: - 性别: + + Pronouns: + - + Location: 位置: - + User level: 用户等级: - + Account Age: - + + Edit + + + + + Change password + + + + + Change avatar + + + + Administrator 管理员 - + Moderator 版主 - + Registered user 注册用户 - - + + Unregistered user 未注册用户 - + Unknown - + Year - + Years - + Day - + Days + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + UserInterfaceSettingsPage - + General interface settings 通用接口设置 - + Enable notifications in taskbar - + Notify in the taskbar for game events while you are spectating - + &Double-click cards to play them (instead of single-click) 双击卡牌开始 (而不是单击开始) - + &Play all nonlands onto the stack (not the battlefield) by default - + + Annotate card text on tokens + + + + Animation settings 动画设置 - + &Tap/untap animation 横置/重置 动画 @@ -4643,22 +5344,22 @@ Please enter a name: UserList - + Users connected to server: %1 - + Users in this room: %1 房间人数: %1 - + Buddies online: %1 / %2 好友在线: %1 / %2 - + Ignored users online: %1 / %2 屏蔽用户在线: %1 / %2 @@ -4680,27 +5381,44 @@ Please enter a name: Move selected set up + + + Move selected set to the top + + Move selected set down - - - Move selected set to top - - - Move selected set to bottom + Move selected set to the bottom - Enable the sets that you want to have available in the deck editor. -Move sets around to change their order, or click on a column header to sort sets on that field. -Sets order decides the source that will be used when loading images for a specific card. -Disabled sets will still be used for loading images. + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed @@ -4709,12 +5427,12 @@ Disabled sets will still be used for loading images. 编辑版本 - + Success - + The sets database has been saved successfully. @@ -4742,4 +5460,561 @@ Disabled sets will still be used for loading images. + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + \ No newline at end of file diff --git a/cockatrice/translations/cockatrice_zh-Hant.ts b/cockatrice/translations/cockatrice_zh-Hant.ts new file mode 100644 index 00000000..001019c0 --- /dev/null +++ b/cockatrice/translations/cockatrice_zh-Hant.ts @@ -0,0 +1,5994 @@ + + + AbstractCounter + + + &Set counter... + + + + + Set counter + + + + + New value for counter '%1': + + + + + AppearanceSettingsPage + + + Zone background pictures + + + + + Hand background: + + + + + Stack background: + + + + + Table background: + + + + + Player info background: + + + + + Card back: + + + + + Card rendering + + + + + Display card names on cards having a picture + + + + + Scale cards on mouse over + + + + + Hand layout + + + + + Display hand horizontally (wastes space) + + + + + Enable left justification + + + + + Table grid layout + + + + + Invert vertical coordinate + + + + + Minimum player count for multi-column layout: + + + + + + + + + Choose path + + + + + BanDialog + + + ban &user name + + + + + ban &IP address + + + + + ban client I&D + + + + + Ban type + + + + + &permanent ban + + + + + &temporary ban + + + + + &Days: + + + + + &Hours: + + + + + &Minutes: + + + + + Duration of the ban + + + + + Please enter the reason for the ban. +This is only saved for moderators and cannot be seen by the banned person. + + + + + Please enter the reason for the ban that will be visible to the banned person. + + + + + &OK + + + + + &Cancel + + + + + Ban user from server + + + + + Error + + + + + You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban. + + + + + CardDatabase + + + New sets found + + + + + %1 new set(s) have been found in the card database. Do you want to enable them? + + + + + CardDatabaseModel + + + Name + + + + + Sets + + + + + Mana cost + + + + + Card type + + + + + P/T + + + + + CardFrame + + + Image + + + + + Description + + + + + Both + + + + + CardInfoText + + + Name: + + + + + Mana cost: + + + + + Color(s): + + + + + Card type: + + + + + P / T: + + + + + Loyalty: + + + + + CardInfoWidget + + + Show card only + + + + + Show text only + + + + + Show full info + + + + + Name: + + + + + Mana cost: + + + + + Color(s): + + + + + Card type: + + + + + P / T: + + + + + Loyalty: + + + + + CardItem + + + &Power / toughness + + + + + &Move to + + + + + CardZone + + + her hand + nominative, female owner + + + + + %1's hand + nominative, female owner + + + + + his hand + nominative, male owner + + + + + %1's hand + nominative, male owner + + + + + her library + look at zone, female owner + + + + + %1's library + look at zone, female owner + + + + + his library + look at zone, male owner + + + + + %1's library + look at zone, male owner + + + + + of her library + top cards of zone, female owner + + + + + of %1's library + top cards of zone, female owner + + + + + of his library + top cards of zone, male owner + + + + + of %1's library + top cards of zone, male owner + + + + + her library + reveal zone, female owner + + + + + %1's library + reveal zone, female owner + + + + + his library + reveal zone, male owner + + + + + %1's library + reveal zone, male owner + + + + + her library + shuffle, female owner + + + + + %1's library + shuffle, female owner + + + + + his library + shuffle, male owner + + + + + %1's library + shuffle, male owner + + + + + her sideboard + look at zone, female owner + + + + + %1's sideboard + look at zone, female owner + + + + + his sideboard + look at zone, male owner + + + + + %1's sideboard + look at zone, male owner + + + + + her library + nominative, female owner + + + + + %1's library + nominative, female owner + + + + + his library + nominative, male owner + + + + + %1's library + nominative, male owner + + + + + her graveyard + nominative, female owner + + + + + %1's graveyard + nominative, female owner + + + + + his graveyard + nominative, male owner + + + + + %1's graveyard + nominative, male owner + + + + + her exile + nominative, female owner + + + + + %1's exile + nominative, female owner + + + + + his exile + nominative, male owner + + + + + %1's exile + nominative, male owner + + + + + her sideboard + nominative, female owner + + + + + %1's sideboard + nominative, female owner + + + + + his sideboard + nominative, male owner + + + + + %1's sideboard + nominative, male owner + + + + + DeckEditorSettingsPage + + + Nothing is here... yet + + + + + General + + + + + DeckListModel + + + Number + + + + + Card + + + + + Price + + + + + DeckStatsInterface + + + + Error + + + + + The reply from the server could not be parsed. + + + + + DeckViewContainer + + + Load local deck + + + + + Load deck from server + + + + + Ready to s&tart + + + + + S&ideboard unlocked + + + + + S&ideboard locked + + + + + Load deck + + + + + Error + + + + + The selected file could not be loaded. + + + + + DlgCardSearch + + + Card name: + + + + + Card text: + + + + + Card type (OR): + + + + + Color (OR): + + + + + O&K + + + + + &Cancel + + + + + Card search + + + + + DlgConnect + + + Previous Host + + + + + New Host + + + + + &Host: + + + + + Enter host name + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + &Save password + + + + + A&uto connect at start + + + + + Connect to server + + + + + DlgCreateGame + + + &Description: + + + + + &Password: + + + + + P&layers: + + + + + Re&member settings + + + + + Game type + + + + + Only &buddies can join + + + + + Only &registered users can join + + + + + Joining restrictions + + + + + &Spectators can watch + + + + + Spectators &need a password to watch + + + + + Spectators can see &hands + + + + + Spectators can &chat + + + + + Spectators + + + + + &Clear + + + + + Create game + + + + + Game information + + + + + Error + + + + + Server error. + + + + + DlgCreateToken + + + &Name: + + + + + Token + + + + + C&olor: + + + + + white + + + + + blue + + + + + black + + + + + red + + + + + green + + + + + multicolor + + + + + colorless + + + + + &P/T: + + + + + &Annotation: + + + + + &Destroy token when it leaves the table + + + + + Token data + + + + + Show &all tokens + + + + + Show tokens from this &deck + + + + + Choose token from list + + + + + Create token + + + + + DlgEditAvatar + + + + No image chosen. + + + + + To change your avatar, choose a new image. +To remove your current avatar, confirm without choosing a new image. + + + + + Browse... + + + + + Change avatar + + + + + Open Image + + + + + Image Files (*.png *.jpg *.bmp) + + + + + Invalid image chosen. + + + + + DlgEditPassword + + + Old password: + + + + + New password: + + + + + Confirm new password: + + + + + Change password + + + + + Error + + + + + The new passwords don't match. + + + + + DlgEditTokens + + + &Name: + + + + + C&olor: + + + + + white + + + + + blue + + + + + black + + + + + red + + + + + green + + + + + multicolor + + + + + colorless + + + + + &P/T: + + + + + &Annotation: + + + + + Token data + + + + + + Add token + + + + + Remove token + + + + + Edit tokens + + + + + Please enter the name of the token: + + + + + Error + + + + + The chosen name conflicts with an existing card or token. +Make sure to enable the 'token set' in the 'Edit sets...' dialog to display them correctly. + + + + + DlgEditUser + + + Email: + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Country: + + + + + Undefined + + + + + Real name: + + + + + Edit user profile + + + + + DlgFilterGames + + + Show &unavailable games + + + + + Show &password protected games + + + + + Game &description: + + + + + &Creator name: + + + + + &Game types + + + + + at &least: + + + + + at &most: + + + + + Maximum player count + + + + + Filter games + + + + + DlgLoadDeckFromClipboard + + + &Refresh + + + + + Load deck from clipboard + + + + + + Error + + + + + + Invalid deck list. + + + + + DlgLoadRemoteDeck + + + Load deck + + + + + DlgRegister + + + &Host: + + + + + &Port: + + + + + Player &name: + + + + + P&assword: + + + + + Password (again): + + + + + Email: + + + + + Email (again): + + + + + Pronouns: + + + + + Neutral + + + + + Masculine + + + + + Feminine + + + + + Undefined + + + + + + Registration Warning + + + + + Your passwords do not match, please try again. + + + + + Your email addresses do not match, please try again. + + + + + Country: + + + + + Real name: + + + + + Register to server + + + + + DlgSettings + + + + + Error + + + + + Unknown Error loading card database + + + + + Your card database is invalid. + +Cockatrice may not function correctly with an invalid database + +You may need to rerun oracle to update your card database. + +Would you like to change your database location setting? + + + + + Your card database version is too old. + +This can cause problems loading card information or images + +Usually this can be fixed by rerunning oracle to to update your card database. + +Would you like to change your database location setting? + + + + + File Error loading your card database. + +Would you like to change your database location setting? + + + + + Your card database was loaded but contains no cards. + +Would you like to change your database location setting? + + + + + Your card database did not finish loading + +Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues with your cards.xml attached + +Would you like to change your database location setting? + + + + + Unknown card database load status + +Please file a ticket at http://github.com/Cockatrice/Cockatrice/issues + +Would you like to change your database location setting? + + + + + The path to your deck directory is invalid. Would you like to go back and set the correct path? + + + + + The path to your card pictures directory is invalid. Would you like to go back and set the correct path? + + + + + Settings + + + + + General + + + + + Appearance + + + + + User Interface + + + + + Deck Editor + + + + + Chat + + + + + Sound + + + + + Shortcuts + + + + + GameSelector + + + + + + + + + + + Error + + + + + Please join the appropriate room first. + + + + + Wrong password. + + + + + Spectators are not allowed in this game. + + + + + The game is already full. + + + + + The game does not exist any more. + + + + + This game is only open to registered users. + + + + + This game is only open to its creator's buddies. + + + + + You are being ignored by the creator of this game. + + + + + Join game + + + + + Password: + + + + + Please join the respective room first. + + + + + Games + + + + + &Filter games + + + + + C&lear filter + + + + + C&reate + + + + + &Join + + + + + J&oin as spectator + + + + + GamesModel + + + Game Created + + + + + Creator + + + + + Description + + + + + <1m ago + + + + + <5m ago + + + + + %1m ago + + + + + 1hr %1m ago + + + + + %1hr ago + + + + + 5+ hrs ago + + + + + password + + + + + buddies only + + + + + reg. users only + + + + + + can chat + + + + + see hands + + + + + can see hands + + + + + not allowed + + + + + Room + + + + + Game Type + + + + + Restrictions + + + + + Players + + + + + Spectators + + + + + GeneralSettingsPage + + + Reset/Clear Downloaded Pictures + + + + + + + + + Choose path + + + + + Success + + + + + Downloaded card pictures have been reset. + + + + + Error + + + + + One or more downloaded card pictures could not be cleared. + + + + + Personal settings + + + + + Language: + + + + + Download card pictures on the fly + + + + + Download card pictures from a custom URL + + + + + Custom Card Download URL: + + + + + Linking FAQ + + + + + Paths + + + + + Decks directory: + + + + + Replays directory: + + + + + Pictures directory: + + + + + Card database: + + + + + Token database: + + + + + Picture cache size: + + + + + + English + 繁體中文 (Chinese Traditional) + + + + MainWindow + + + There are too many concurrent connections from your address. + + + + + Scheduled server shutdown. + + + + + Banned by moderator + + + + + Expected end time: %1 + + + + + This ban lasts indefinitely. + + + + + Connection closed + + + + + The server has terminated your connection. +Reason: %1 + + + + + Scheduled server shutdown + + + + + The server is going to be restarted in %n minute(s). +All running games will be lost. +Reason for shutdown: %1 + + + + + Number of players + + + + + Please enter the number of players. + + + + + + Player %1 + + + + + Load replay + + + + + About Cockatrice + + + + + Version %1 + + + + + Translators: + + + + + Project Manager: + + + + + The server has reached its maximum user capacity, please check back later. + + + + + + Invalid username. + + + + + You have been logged out due to logging in at another location. + + + + + + Success + + + + + Registration accepted. +Will now login. + + + + + Account activation accepted. +Will now login. + + + + + Past Project Managers: + + + + + Developers: + + + + + Our Developers + + + + + Help Develop! + + + + + Recognition Page + + + + + Help Translate! + + + + + Support: + + + + + Report an Issue + + + + + + + + + + + + + + + + + + + + + + + Error + + + + + Server timeout + + + + + Incorrect username or password. Please check your authentication information and try again. + + + + + There is already an active session using this user name. +Please close that session first and re-login. + + + + + + You are banned until %1. + + + + + + You are banned indefinitely. + + + + + This server requires user registration. Do you want to register now? + + + + + This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client. +Please close and reopen your client to try again. + + + + + An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider. + + + + + Account activation + + + + + Unknown login error: %1 + + + + + + +This usually means that your client version is out of date, and the server sent a reply your client doesn't understand. + + + + + Your username must respect these rules: + + + + + is %1 - %2 characters long + + + + + can %1 contain lowercase characters + + + + + + + + NOT + + + + + can %1 contain uppercase characters + + + + + can %1 contain numeric characters + + + + + can contain the following punctuation: %1 + + + + + first character can %1 be a punctuation mark + + + + + You may only use A-Z, a-z, 0-9, _, ., and - in your username. + + + + + + + + + Registration denied + + + + + Registration is currently disabled on this server + + + + + There is already an existing account with the same user name. + + + + + It's mandatory to specify a valid email address when registering. + + + + + Too many registration attempts from your IP address. + + + + + Password too short. + + + + + Registration failed for a technical problem on the server. + + + + + Unknown registration error: %1 + + + + + Account activation failed + + + + + Socket error: %1 + + + + + You are trying to connect to an obsolete server. Please downgrade your Cockatrice version or connect to a suitable server. +Local version is %1, remote version is %2. + + + + + Your Cockatrice client is obsolete. Please update your Cockatrice version. +Local version is %1, remote version is %2. + + + + + Connecting to %1... + + + + + Registering to %1 as %2... + + + + + Disconnected + + + + + Connected, logging in at %1 + + + + + &Connect... + + + + + &Disconnect + + + + + Start &local game... + + + + + &Watch replay... + + + + + &Deck editor + + + + + &Full screen + + + + + &Register to server... + + + + + &Settings... + + + + + + &Exit + + + + + A&ctions + + + + + &Cockatrice + + + + + &About Cockatrice + + + + + &Help + + + + + Check for card updates... + + + + + A card database update is already running. + + + + + Unable to run the card database updater: + + + + + The card database updater exited with an error: %1 + + + + + Update completed successfully. Cockatrice will now reload the card database. + + + + + + Information + + + + + Troubleshooting + + + + + F.A.Q. + + + + + Your account has not been activated yet. +You need to provide the activation token received in the activation email + + + + + failed to start. + + + + + crashed. + + + + + timed out. + + + + + write error. + + + + + read error. + + + + + unknown error. + + + + + MessageLogWidget + + + The game has been closed. + + + + + %1 is now watching the game. + + + + + %1 is not watching the game any more. + + + + %1 draws %n card(s). + + %1 draws a card. + %1 draws %n cards. + + + + + You have joined game #%1. + female + + + + + You have joined game #%1. + male + + + + + You are watching a replay of game #%1. + female + + + + + You are watching a replay of game #%1. + male + + + + + %1 has joined the game. + female + + + + + %1 has joined the game. + male + + + + + %1 has left the game. + female + + + + + %1 has left the game. + male + + + + + You have been kicked out of the game. + + + + + %1 has loaded a deck (%2). + + + + + %1 has loaded a deck with %2 sideboard cards (%3). + + + + + %1 is ready to start the game. + female + + + + + %1 is ready to start the game. + male + + + + + %1 is not ready to start the game any more. + female + + + + + %1 is not ready to start the game any more. + male + + + + + %1 has locked her sideboard. + female + + + + + %1 has locked his sideboard. + male + + + + + %1 has unlocked her sideboard. + female + + + + + %1 has unlocked his sideboard. + male + + + + + %1 has conceded the game. + female + + + + + %1 has conceded the game. + male + + + + + %1 has restored connection to the game. + female + + + + + %1 has restored connection to the game. + male + + + + + %1 has lost connection to the game. + female + + + + + %1 has lost connection to the game. + male + + + + + %1 shuffles %2. + female + + + + + %1 shuffles %2. + male + + + + + %1 rolls a %2 with a %3-sided die. + female + + + + + %1 rolls a %2 with a %3-sided die. + male + + + + %1 draws %n card(s). + female + + %1 draws a card. + %1 draws %n cards. + + + + %1 draws %n card(s). + male + + %1 draws a card. + %1 draws %n cards. + + + + + %1 undoes his last draw. + + + + + %1 undoes her last draw. + + + + + %1 undoes his last draw (%2). + + + + + %1 undoes her last draw (%2). + + + + + from exile + + + + + the bottom card of %1's library + + + + + the bottom card of his library + + + + + the bottom card of her library + + + + + from the bottom of %1's library + + + + + from the bottom of his library + + + + + from the bottom of her library + + + + + the top card of %1's library + + + + + the top card of his library + + + + + the top card of her library + + + + + from the top of %1's library + + + + + from the top of his library + + + + + from the top of her library + + + + + from %1's library + + + + + from library + + + + + from sideboard + + + + + from the stack + + + + + %1 gives %2 control over %3. + + + + + %1 puts %2 into play tapped%3. + + + + + %1 puts %2 into play%3. + + + + + %1 exiles %2%3. + + + + + %1 puts %2%3 into his library. + + + + + %1 puts %2%3 into her library. + + + + + %1 puts %2%3 on bottom of his library. + + + + + %1 puts %2%3 on bottom of her library. + + + + + %1 puts %2%3 on top of his library. + + + + + %1 puts %2%3 on top of her library. + + + + + %1 puts %2%3 into his library at position %4. + + + + + %1 puts %2%3 into her library at position %4. + + + + + %1 moves %2%3 to sideboard. + + + + + %1 plays %2%3. + + + + + %1 takes a mulligan to %n. + female + + + + + %1 takes a mulligan to %n. + male + + + + + %1 flips %2 face-down. + female + + + + + %1 flips %2 face-down. + male + + + + + %1 flips %2 face-up. + female + + + + + %1 flips %2 face-up. + male + + + + + %1 destroys %2. + female + + + + + %1 destroys %2. + male + + + + + %1 unattaches %2. + female + + + + + %1 unattaches %2. + male + + + + + %1 creates token: %2%3. + female + + + + + %1 creates token: %2%3. + male + + + + + %1 points from her %2 to herself. + female + + + + + %1 points from his %2 to himself. + male + + + + + %1 points from her %2 to %3. + p1 female, p2 female + + + + + %1 points from her %2 to %3. + p1 female, p2 male + + + + + %1 points from his %2 to %3. + p1 male, p2 female + + + + + %1 points from his %2 to %3. + p1 male, p2 male + + + + + %1 points from %2's %3 to herself. + card owner female, target female + + + + + %1 points from %2's %3 to herself. + card owner male, target female + + + + + %1 points from %2's %3 to himself. + card owner female, target male + + + + + %1 points from %2's %3 to himself. + card owner male, target male + + + + + %1 points from %2's %3 to %4. + p1 female, p2 female, p3 female + + + + + %1 points from %2's %3 to %4. + p1 female, p2 female, p3 male + + + + + %1 points from %2's %3 to %4. + p1 female, p2 male, p3 female + + + + + %1 points from %2's %3 to %4. + p1 female, p2 male, p3 male + + + + + %1 points from %2's %3 to %4. + p1 male, p2 female, p3 female + + + + + %1 points from %2's %3 to %4. + p1 male, p2 female, p3 male + + + + + %1 points from %2's %3 to %4. + p1 male, p2 male, p3 female + + + + + %1 points from %2's %3 to %4. + p1 male, p2 male, p3 male + + + + + %1 points from her %2 to her %3. + female + + + + + %1 points from his %2 to his %3. + male + + + + + %1 points from her %2 to %3's %4. + p1 female, p2 female + + + + + %1 points from her %2 to %3's %4. + p1 female, p2 male + + + + + %1 points from his %2 to %3's %4. + p1 male, p2 female + + + + + %1 points from his %2 to %3's %4. + p1 male, p2 male + + + + + %1 points from %2's %3 to her own %4. + card owner female, target female + + + + + %1 points from %2's %3 to her own %4. + card owner male, target female + + + + + %1 points from %2's %3 to his own %4. + card owner female, target male + + + + + %1 points from %2's %3 to his own %4. + card owner male, target male + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 female, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 female, p3 male + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 male, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 female, p2 male, p3 male + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 female, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 female, p3 male + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 male, p3 female + + + + + %1 points from %2's %3 to %4's %5. + p1 male, p2 male, p3 male + + + + %1 places %n %2 counter(s) on %3 (now %4). + female + + + + %1 places %n %2 counter(s) on %3 (now %4). + male + + + + %1 removes %n %2 counter(s) from %3 (now %4). + female + + + + %1 removes %n %2 counter(s) from %3 (now %4). + male + + + + + %1 taps her permanents. + female + + + + + %1 untaps her permanents. + female + + + + + %1 taps his permanents. + male + + + + + %1 untaps his permanents. + male + + + + + %1 taps %2. + female + + + + + %1 untaps %2. + female + + + + + %1 taps %2. + male + + + + + %1 untaps %2. + male + + + + + %1 sets counter %2 to %3 (%4%5). + female + + + + + %1 sets counter %2 to %3 (%4%5). + male + + + + + %1 sets %2 to not untap normally. + female + + + + + %1 sets %2 to not untap normally. + male + + + + + %1 sets %2 to untap normally. + female + + + + + %1 sets %2 to untap normally. + male + + + + + %1 sets PT of %2 to %3. + female + + + + + %1 sets PT of %2 to %3. + male + + + + + %1 sets annotation of %2 to %3. + female + + + + + %1 sets annotation of %2 to %3. + male + + + + + %1 is looking at %2. + female + + + + + %1 is looking at %2. + male + + + + %1 is looking at the top %n card(s) %2. + female + + + + %1 is looking at the top %n card(s) %2. + male + + + + + %1 stops looking at %2. + female + + + + + %1 stops looking at %2. + male + + + + + %1 reveals %2 to %3. + p1 female, p2 female + + + + + %1 reveals %2 to %3. + p1 female, p2 male + + + + + %1 reveals %2 to %3. + p1 male, p2 female + + + + + %1 reveals %2 to %3. + p1 male, p2 male + + + + + %1 reveals %2. + female + + + + + %1 reveals %2. + male + + + + + %1 randomly reveals %2%3 to %4. + p1 female, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 female, p2 male + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 randomly reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 randomly reveals %2%3. + female + + + + + %1 randomly reveals %2%3. + male + + + + + %1 peeks at face down card #%2. + female + + + + + %1 peeks at face down card #%2. + male + + + + + %1 peeks at face down card #%2: %3. + female + + + + + %1 peeks at face down card #%2: %3. + male + + + + + %1 reveals %2%3 to %4. + p1 female, p2 female + + + + + %1 reveals %2%3 to %4. + p1 female, p2 male + + + + + %1 reveals %2%3 to %4. + p1 male, p2 female + + + + + %1 reveals %2%3 to %4. + p1 male, p2 male + + + + + %1 reveals %2%3. + female + + + + + %1 reveals %2%3. + male + + + + + %1 is now keeping the top card %2 revealed. + + + + + %1 is not revealing the top card %2 any longer. + + + + + It is now %1's turn. + female + + + + + It is now %1's turn. + male + + + + + + a card + + + + %1 places %n counter(s) (%2) on %3 (now %4). + + %1 places a counter (%2) on %3 (now %4). + %1 places %n counters (%2) on %3 (now %4). + + + + %1 removes %n counter(s) (%2) from %3 (now %4). + + %1 removes a counter (%2) from %3 (now %4). + %1 removes %n counters (%2) from %3 (now %4). + + + + + red + + + + + yellow + + + + + green + + + + + The game has started. + + + + + %1 draws his initial hand. + + + + + %1 draws her initial hand. + + + + %1 places %n %2 counter(s) on %3 (now %4). + + %1 places a %2 counter on %3 (now %4). + %1 places %n %2 counters on %3 (now %4). + + + + %1 removes %n %2 counter(s) from %3 (now %4). + + %1 removes a %2 counter from %3 (now %4). + %1 removes %n %2 counters from %3 (now %4). + + + + + ending phase + + + + + untap step + + + + + %1 draws %2 card(s). + female + + + + + %1 draws %2 card(s). + male + + + + + from play + + + + + from her graveyard + + + + + from his graveyard + + + + + from her hand + + + + + from his hand + + + + + %1 puts %2%3 into her graveyard. + + + + + %1 puts %2%3 into his graveyard. + + + + + %1 moves %2%3 to her hand. + + + + + %1 moves %2%3 to his hand. + + + + + %1 attaches %2 to %3's %4. + p1 female, p2 female + + + + + %1 attaches %2 to %3's %4. + p1 female, p2 male + + + + + %1 attaches %2 to %3's %4. + p1 male, p2 female + + + + + %1 attaches %2 to %3's %4. + p1 male, p2 male + + + + + %1 places %2 %3 counter(s) on %4 (now %5). + female + + + + + %1 places %2 %3 counter(s) on %4 (now %5). + male + + + + + %1 removes %2 %3 counter(s) from %4 (now %5). + female + + + + + %1 removes %2 %3 counter(s) from %4 (now %5). + male + + + + + %1 is looking at the top %2 card(s) %3. + female + + + + + %1 is looking at the top %2 card(s) %3. + male + + + + + upkeep step + + + + + draw step + + + + + first main phase + + + + + beginning of combat step + + + + + declare attackers step + + + + + declare blockers step + + + + + combat damage step + + + + + end of combat step + + + + + second main phase + + + + + It is now the %1. + + + + + MessagesSettingsPage + + + Chat settings + + + + + Custom alert words + + + + + Enable chat mentions + + + + + Enable mention completer + + + + + In-game message macros + + + + + Ignore chat room messages sent by unregistered users + + + + + Ignore private messages sent by unregistered users + + + + + Enable desktop notifications for private messages + + + + + Separate words with a space, alphanumeric characters only + + + + + + Invert text color + + + + + Enable desktop notification for mentions. + + + + + + (Color is hexadecimal) + + + + + Add message + + + + + Message: + + + + + PhasesToolbar + + + Untap step + + + + + Upkeep step + + + + + Draw step + + + + + First main phase + + + + + Beginning of combat step + + + + + Declare attackers step + + + + + Declare blockers step + + + + + Combat damage step + + + + + End of combat step + + + + + Second main phase + + + + + End of turn step + + + + + Player + + + &View library + + + + + Move top cards to &graveyard... + + + + + View &top cards of library... + + + + + &View graveyard + + + + + &View sideboard + + + + + Player "%1" + + + + + + + + &Hand + + + + + &Reveal hand to... + + + + + Reveal r&andom card to... + + + + + &Library + + + + + + + + &Graveyard + + + + + &Sideboard + + + + + Red + + + + + Yellow + + + + + Green + + + + + View top cards of library + + + + + Number of cards: + + + + + &Draw card + + + + + Reveal top cards of library + + + + + Number of cards: (max. %1) + + + + + &View exile + + + + + + + + &Exile + + + + + Reveal t&op cards to... + + + + + D&raw cards... + + + + + Take &mulligan + + + + + &Shuffle + + + + + &Counters + + + + + &Untap all permanents + + + + + R&oll die... + + + + + &Create token... + + + + + C&reate another token + + + + + S&ay + + + + + &Move hand to... + + + + + + + + &Top of library + + + + + + + + &Bottom of library + + + + + &Move graveyard to... + + + + + &Move exile to... + + + + + Reveal &library to... + + + + + &Always reveal top card + + + + + O&pen deck in deck editor + + + + + &Undo last draw + + + + + Play top card &face down + + + + + Move top cards to &exile... + + + + + Put top card on &bottom + + + + + Put bottom card &in graveyard + + + + + Cr&eate predefined token + + + + + C&ard + + + + + &All players + + + + + &Play + + + + + &Hide + + + + + Play &Face Down + + + + + &Tap + + + + + &Untap + + + + + Toggle &normal untapping + + + + + &Flip + + + + + &Peek at card face + + + + + &Clone + + + + + Attac&h to card... + + + + + Unattac&h + + + + + &Draw arrow... + + + + + &Increase power + + + + + &Decrease power + + + + + I&ncrease toughness + + + + + D&ecrease toughness + + + + + In&crease power and toughness + + + + + Dec&rease power and toughness + + + + + Set &power and toughness... + + + + + &Set annotation... + + + + + &Add counter (%1) + + + + + &Remove counter (%1) + + + + + &Set counters (%1)... + + + + + Draw cards + + + + + + + + Number: + + + + + Move top cards to grave + + + + + Move top cards to exile + + + + + Roll die + + + + + Number of sides: + + + + + Set power/toughness + + + + + Please enter the new PT: + + + + + Set annotation + + + + + Please enter the new annotation: + + + + + Set counters + + + + + Cr&eate related card + + + + + QMenuBar + + + Services + + + + + Hide %1 + + + + + Hide Others + + + + + Show All + + + + + Preferences... + + + + + Quit %1 + + + + + About %1 + + + + + QObject + + + Cockatrice replays (*.cor) + + + + + Common deck formats (*.cod *.dec *.mwDeck) + + + + + All files (*.*) + + + + + RemoteDeckList_TreeModel + + + Name + + + + + ID + + + + + Upload time + + + + + RemoteReplayList_TreeModel + + + ID + + + + + Name + + + + + Players + + + + + Keep + + + + + Time started + + + + + Duration (sec) + + + + + RoomSelector + + + Rooms + + + + + Joi&n + + + + + Room + + + + + Description + + + + + Permissions + + + + + Players + + + + + Games + + + + + + Error + + + + + You do not have the proper permission to join this room. + + + + + Failed to join the room due to an unknown error. + + + + + SequenceEdit + + + Shortcut already in use + + + + + SetsModel + + + Enabled + + + + + Set type + + + + + Set code + + + + + Long name + + + + + Release date + + + + + ShutdownDialog + + + &Reason for shutdown: + + + + + &Time until shutdown (minutes): + + + + + Shut down server + + + + + SoundSettingsPage + + + Choose path + + + + + Enable &sounds + + + + + Path to sounds directory: + + + + + Test system sound engine + + + + + Sound settings + + + + + Master volume requires QT5 + + + + + Master volume + + + + + TabAdmin + + + Update server &message + + + + + &Shut down server + + + + + &Reload configuration + + + + + Server administration functions + + + + + &Unlock functions + + + + + &Lock functions + + + + + Unlock administration functions + + + + + Do you really want to unlock the administration functions? + + + + + Administration + + + + + TabDeckEditor + + + &Print deck... + + + + + &Close + + + + + &Edit sets... + + + + + Filters + + + + + &Clear all filters + + + + + Delete selected + + + + + Deck &name: + + + + + &Comments: + + + + + Hash: + + + + + &New deck + + + + + &Load deck... + + + + + &Save deck + + + + + Save deck &as... + + + + + Load deck from cl&ipboard... + + + + + Save deck to clip&board + + + + + &Analyze deck on deckstats.net + + + + + Open custom image folder + + + + + Open custom sets folder + + + + + Add card to &maindeck + + + + + Add card to &sideboard + + + + + &Deck Editor + + + + + C&ard Database + + + + + Show/Hide card information + + + + + Show/Hide deck + + + + + Show/Hide filters + + + + + Reset layout + + + + + Card Info + + + + + Deck + + + + + Welcome + + + + + Hi! It seems like you're running this version of Cockatrice for the first time. +All the sets in the card database have been enabled. +Read more about changing the set order or disabling specific sets and consequent effects in the "Edit Sets" window. + + + + + &Remove row + + + + + &Increment number + + + + + &Decrement number + + + + + Edit &tokens... + + + + + Deck: %1 + + + + + Are you sure? + + + + + The decklist has been modified. +Do you want to save the changes? + + + + + Load deck + + + + + + + Error + + + + + The deck could not be saved. + + + + + + The deck could not be saved. +Please check that the directory is writable and try again. + + + + + Save deck + + + + + TabDeckStorage + + + Local file system + + + + + Server deck storage + + + + + + Open in deck editor + + + + + Upload deck + + + + + Download deck + + + + + + New folder + + + + + + Delete + + + + + Enter deck name + + + + + This decklist does not have a name. +Please enter a name: + + + + + Unnamed deck + + + + + Delete local file + + + + + + + Are you sure you want to delete "%1"? + + + + + Name of new folder: + + + + + Delete remote folder + + + + + Delete remote deck + + + + + Deck storage + + + + + TabGame + + + &Phases + + + + + &Game + + + + + Next &phase + + + + + Next &turn + + + + + &Remove all local arrows + + + + + Rotate View Cl&ockwise + + + + + Rotate View Co&unterclockwise + + + + + Game &information + + + + + &Concede + + + + + &Leave game + + + + + C&lose replay + + + + + &Say: + + + + + Concede + + + + + Are you sure you want to concede this game? + + + + + Leave game + + + + + Are you sure you want to leave this game? + + + + + You are flooding the game. Please wait a couple of seconds. + + + + + You have been kicked out of the game. + + + + + REPLAY + + + + + TabMessage + + + Private &chat + + + + + &Leave + + + + + %1 - Private chat + + + + + This user is ignoring you. + + + + + Private message from + + + + + %1 has left the server. + + + + + %1 has joined the server. + + + + + TabReplays + + + Local file system + + + + + Server replay storage + + + + + + Watch replay + + + + + + Delete + + + + + Download replay + + + + + Toggle expiration lock + + + + + Delete local file + + + + + Are you sure you want to delete "%1"? + + + + + Delete remote replay + + + + + Are you sure you want to delete the replay of game %1? + + + + + Game replays + + + + + TabRoom + + + &Say: + + + + + Chat + + + + + &Room + + + + + &Leave room + + + + + &Clear chat + + + + + Chat Settings... + + + + + mentioned you. + + + + + Click to view + + + + + You are flooding the chat. Please wait a couple of seconds. + + + + + TabServer + + + Server + + + + + TabSupervisor + + + Are you sure? + + + + + There are still open games. Are you sure you want to quit? + + + + + Promotion + + + + + You have been promoted to moderator. Please log out and back in for changes to take effect. + + + + + TabUserLists + + + Add to Buddy List + + + + + Add to Ignore List + + + + + Account + + + + + UserContextMenu + + + User &details + + + + + Private &chat + + + + + Show this user's &games + + + + + Add to &buddy list + + + + + Remove from &buddy list + + + + + Add to &ignore list + + + + + Remove from &ignore list + + + + + Kick from &game + + + + + Ban from &server + + + + + &Promote user to moderator + + + + + Dem&ote user from moderator + + + + + %1's games + + + + + + Success + + + + + Successfully promoted user. + + + + + Successfully demoted user. + + + + + + Failed + + + + + Failed to promote user. + + + + + Failed to demote user. + + + + + UserInfoBox + + + User information + + + + + Real name: + + + + + Pronouns: + + + + + Location: + + + + + User level: + + + + + Account Age: + + + + + Edit + + + + + Change password + + + + + Change avatar + + + + + Administrator + + + + + Moderator + + + + + Registered user + + + + + + Unregistered user + + + + + Unknown + + + + + Year + + + + + Years + + + + + Day + + + + + Days + + + + + + + Information + + + + + User information updated. + + + + + + + + + + + + Error + + + + + This server does not permit you to update your user informations. + + + + + + An error occured while trying to update your user informations. + + + + + Password changed. + + + + + This server does not permit you to change your password. + + + + + The new password is too short. + + + + + The old password is incorrect. + + + + + Avatar updated. + + + + + This server does not permit you to update your avatar. + + + + + An error occured while trying to updater your avatar. + + + + + UserInterfaceSettingsPage + + + General interface settings + + + + + Enable notifications in taskbar + + + + + Notify in the taskbar for game events while you are spectating + + + + + &Double-click cards to play them (instead of single-click) + + + + + &Play all nonlands onto the stack (not the battlefield) by default + + + + + Annotate card text on tokens + + + + + Animation settings + + + + + &Tap/untap animation + + + + + UserList + + + Users connected to server: %1 + + + + + Users in this room: %1 + + + + + Buddies online: %1 / %2 + + + + + Ignored users online: %1 / %2 + + + + + WndSets + + + Enable all sets + + + + + Disable all sets + + + + + Move selected set up + + + + + Move selected set to the top + + + + + Move selected set down + + + + + Move selected set to the bottom + + + + + hints: + + + + + Enable the sets that you want to have available in the deck editor + + + + + Move sets around to change their order, or click on a column header to sort sets on that field + + + + + Sets order decides the source that will be used when loading images for a specific card + + + + + Disabled sets will be used for loading images only if all the enabled sets failed + + + + + Edit sets + + + + + Success + + + + + The sets database has been saved successfully. + + + + + ZoneViewWidget + + + sort by name + + + + + sort by type + + + + + shuffle when closing + + + + + pile view + + + + + shortcutsTab + + + Form + + + + + Main Window + + + + + Deck editor + + + + + Local gameplay + + + + + Watch replay + + + + + Connect + + + + + Register + + + + + Full screen + + + + + Settings + + + + + Check for card updates + + + + + Exit + + + + + Deck Editor + + + + + Analyze deck + + + + + Load deck (clipboard) + + + + + Clerar all filters + + + + + New deck + + + + + Clear one filter + + + + + Open custom folder + + + + + Close + + + + + Print deck + + + + + Edit sets + + + + + Delete card + + + + + Edit tokens + + + + + Reset layout + + + + + Add card + + + + + Save deck + + + + + Remove card + + + + + Save deck as + + + + + Load deck + + + + + Save deck (clipboard) + + + + + + Counters + + + + + Life + + + + + + + + + Set + + + + + + + + Add + + + + + + + + Remove + + + + + Red + + + + + Green + + + + + Yellow + + + + + Mainwindow / Deck editor + + + + + Power / toughness + + + + + Power and toughness + + + + + Add (+1/+1) + + + + + Remove (-1/-1) + + + + + Toughness + + + + + Remove (-0/-1) + + + + + Add (+0/+1) + + + + + Power + + + + + Remove (-1/-0) + + + + + Add (+1/+0) + + + + + Game Phases + + + + + Untap + + + + + Disconnect + + + + + Upkeep + + + + + + Draw + + + + + Main 1 + + + + + Start combat + + + + + Attack + + + + + Block + + + + + Damage + + + + + End combat + + + + + Main 2 + + + + + End + + + + + Next phase + + + + + Next turn + + + + + Player + + + + + Tap Card + + + + + Untap Card + + + + + Untap all + + + + + Toogle untap + + + + + Flip card + + + + + Peek card + + + + + Play card + + + + + Attach card + + + + + Unattach card + + + + + Clone card + + + + + Create token + + + + + Create another token + + + + + Set annotation + + + + + Phases / P/T / Player + + + + + Move card to + + + + + Bottom library + + + + + Top library + + + + + + Graveyard + + + + + + Exile + + + + + Hand + + + + + View + + + + + Library + + + + + Tops card of library + + + + + Sideboard + + + + + Close recent view + + + + + Pre-play + + + + + Load remote deck + + + + + Load local deck + + + + + Game play + + + + + Draw arrow + + + + + Leave game + + + + + Remove local arrows + + + + + Concede + + + + + Roll dice + + + + + Rotate view CW + + + + + Shuffle library + + + + + Rotate view CCW + + + + + Mulligan + + + + + Draw card + + + + + Draw cards + + + + + Undo draw + + + + + Always reveal top card + + + + + Draw / Move / View / Game play + + + + \ No newline at end of file diff --git a/common/decklist.cpp b/common/decklist.cpp index 3bf61927..478ffb28 100644 --- a/common/decklist.cpp +++ b/common/decklist.cpp @@ -726,12 +726,27 @@ bool DeckList::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNod void DeckList::updateDeckHash() { QStringList cardList; - for (int i = 0; i < root->size(); i++) { + bool isValidDeckList = true; + QSet hashZones, optionalZones; + + hashZones << "main" << "side"; // Zones in deck to be included in hashing process + optionalZones << "tokens"; // Optional zones in deck not included in hashing process + + for (int i = 0; i < root->size(); i++) + { InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - for (int k = 0; k < card->getNumber(); ++k) - cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); + for (int j = 0; j < node->size(); j++) + { + if (hashZones.contains(node->getName())) // Mainboard or Sideboard + { + DecklistCardNode *card = dynamic_cast(node->at(j)); + for (int k = 0; k < card->getNumber(); ++k) + cardList.append((node->getName() == "side" ? "SB:" : "") + card->getName().toLower()); + } + else if (!optionalZones.contains(node->getName())) // Not a valid zone -> cheater? + { + isValidDeckList = false; // Deck is invalid + } } } cardList.sort(); @@ -741,7 +756,7 @@ void DeckList::updateDeckHash() + (((quint64) (unsigned char) deckHashArray[2] << 16)) + (((quint64) (unsigned char) deckHashArray[3]) << 8) + (quint64) (unsigned char) deckHashArray[4]; - deckHash = QString::number(number, 32).rightJustified(8, '0'); + deckHash = (isValidDeckList) ? QString::number(number, 32).rightJustified(8, '0') : "INVALID"; emit deckHashChanged(); } diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index db5cd130..d58846c6 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -106,6 +106,7 @@ SET(PROTO_FILES event_user_joined.proto event_user_left.proto event_user_message.proto + event_notify_user.proto game_commands.proto game_event_container.proto game_event_context.proto @@ -127,6 +128,7 @@ SET(PROTO_FILES response_register.proto response_replay_download.proto response_replay_list.proto + response_adjust_mod.proto response.proto room_commands.proto room_event.proto diff --git a/common/pb/admin_commands.proto b/common/pb/admin_commands.proto index 233ec532..9d5e5414 100644 --- a/common/pb/admin_commands.proto +++ b/common/pb/admin_commands.proto @@ -1,21 +1,39 @@ +syntax = "proto2"; message AdminCommand { - enum AdminCommandType { - UPDATE_SERVER_MESSAGE = 1000; - SHUTDOWN_SERVER = 1001; - } - extensions 100 to max; + enum AdminCommandType { + UPDATE_SERVER_MESSAGE = 1000; + SHUTDOWN_SERVER = 1001; + RELOAD_CONFIG = 1002; + ADJUST_MOD = 1003; + } + extensions 100 to max; } message Command_UpdateServerMessage { - extend AdminCommand { - optional Command_UpdateServerMessage ext = 1000; - } + extend AdminCommand { + optional Command_UpdateServerMessage ext = 1000; + } } message Command_ShutdownServer { - extend AdminCommand { - optional Command_ShutdownServer ext = 1001; - } - optional string reason = 1; - optional uint32 minutes = 2; + extend AdminCommand { + optional Command_ShutdownServer ext = 1001; + } + optional string reason = 1; + optional uint32 minutes = 2; } + +message Command_ReloadConfig { + extend AdminCommand { + optional Command_ReloadConfig ext = 1002; + } +} + +message Command_AdjustMod { + extend AdminCommand { + optional Command_AdjustMod ext = 1003; + } + required string user_name = 1; + required bool should_be_mod = 2; +} + diff --git a/common/pb/card_attributes.proto b/common/pb/card_attributes.proto index d7a9d1e0..ac23ca0d 100644 --- a/common/pb/card_attributes.proto +++ b/common/pb/card_attributes.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; enum CardAttribute { - AttrTapped = 1; - AttrAttacking = 2; - AttrFaceDown = 3; - AttrColor = 4; - AttrPT = 5; - AttrAnnotation = 6; - AttrDoesntUntap = 7; + AttrTapped = 1; + AttrAttacking = 2; + AttrFaceDown = 3; + AttrColor = 4; + AttrPT = 5; + AttrAnnotation = 6; + AttrDoesntUntap = 7; } diff --git a/common/pb/color.proto b/common/pb/color.proto index 90fdb781..d932ba76 100644 --- a/common/pb/color.proto +++ b/common/pb/color.proto @@ -1,6 +1,7 @@ +syntax = "proto2"; message color { - optional uint32 r = 1; - optional uint32 g = 2; - optional uint32 b = 3; - optional uint32 a = 4; + optional uint32 r = 1; + optional uint32 g = 2; + optional uint32 b = 3; + optional uint32 a = 4; } \ No newline at end of file diff --git a/common/pb/command_attach_card.proto b/common/pb/command_attach_card.proto index 02fbb9ce..654e57c5 100644 --- a/common/pb/command_attach_card.proto +++ b/common/pb/command_attach_card.proto @@ -1,13 +1,14 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_AttachCard { - extend GameCommand { - optional Command_AttachCard ext = 1009; - } - optional string start_zone = 1; - optional sint32 card_id = 2 [default = -1]; - optional sint32 target_player_id = 3 [default = -1]; - optional string target_zone = 4; - optional sint32 target_card_id = 5 [default = -1]; + extend GameCommand { + optional Command_AttachCard ext = 1009; + } + optional string start_zone = 1; + optional sint32 card_id = 2 [default = -1]; + optional sint32 target_player_id = 3 [default = -1]; + optional string target_zone = 4; + optional sint32 target_card_id = 5 [default = -1]; } diff --git a/common/pb/command_change_zone_properties.proto b/common/pb/command_change_zone_properties.proto index 5bbc2900..f89e36aa 100644 --- a/common/pb/command_change_zone_properties.proto +++ b/common/pb/command_change_zone_properties.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_ChangeZoneProperties { - extend GameCommand { - optional Command_ChangeZoneProperties ext = 1031; - } - optional string zone_name = 1; - - optional bool always_reveal_top_card = 10; + extend GameCommand { + optional Command_ChangeZoneProperties ext = 1031; + } + optional string zone_name = 1; + + optional bool always_reveal_top_card = 10; } diff --git a/common/pb/command_concede.proto b/common/pb/command_concede.proto index 22028af2..5ac74235 100644 --- a/common/pb/command_concede.proto +++ b/common/pb/command_concede.proto @@ -1,6 +1,7 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_Concede { - extend GameCommand { - optional Command_Concede ext = 1017; - } + extend GameCommand { + optional Command_Concede ext = 1017; + } } diff --git a/common/pb/command_create_arrow.proto b/common/pb/command_create_arrow.proto index 31c29c5b..55048031 100644 --- a/common/pb/command_create_arrow.proto +++ b/common/pb/command_create_arrow.proto @@ -1,15 +1,16 @@ +syntax = "proto2"; import "game_commands.proto"; import "color.proto"; message Command_CreateArrow { - extend GameCommand { - optional Command_CreateArrow ext = 1011; - } - optional sint32 start_player_id = 1 [default = -1]; - optional string start_zone = 2; - optional sint32 start_card_id = 3 [default = -1]; - optional sint32 target_player_id = 4 [default = -1]; - optional string target_zone = 5; - optional sint32 target_card_id = 6 [default = -1]; - optional color arrow_color = 7; + extend GameCommand { + optional Command_CreateArrow ext = 1011; + } + optional sint32 start_player_id = 1 [default = -1]; + optional string start_zone = 2; + optional sint32 start_card_id = 3 [default = -1]; + optional sint32 target_player_id = 4 [default = -1]; + optional string target_zone = 5; + optional sint32 target_card_id = 6 [default = -1]; + optional color arrow_color = 7; } diff --git a/common/pb/command_create_counter.proto b/common/pb/command_create_counter.proto index 047b6551..d60fbdc2 100644 --- a/common/pb/command_create_counter.proto +++ b/common/pb/command_create_counter.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "game_commands.proto"; import "color.proto"; message Command_CreateCounter { - extend GameCommand { - optional Command_CreateCounter ext = 1019; - } - optional string counter_name = 1; - optional color counter_color = 2; - optional uint32 radius = 3; - optional sint32 value = 4; + extend GameCommand { + optional Command_CreateCounter ext = 1019; + } + optional string counter_name = 1; + optional color counter_color = 2; + optional uint32 radius = 3; + optional sint32 value = 4; } diff --git a/common/pb/command_create_token.proto b/common/pb/command_create_token.proto index 574fcb21..9fc61b70 100644 --- a/common/pb/command_create_token.proto +++ b/common/pb/command_create_token.proto @@ -1,16 +1,19 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_CreateToken { - extend GameCommand { - optional Command_CreateToken ext = 1010; - } - optional string zone = 1; - optional string card_name = 2; - optional string color = 3; - optional string pt = 4; - optional string annotation = 5; - optional bool destroy_on_zone_change = 6; - optional sint32 x = 7; - optional sint32 y = 8; + extend GameCommand { + optional Command_CreateToken ext = 1010; + } + optional string zone = 1; + optional string card_name = 2; + optional string color = 3; + optional string pt = 4; + optional string annotation = 5; + optional bool destroy_on_zone_change = 6; + optional sint32 x = 7; + optional sint32 y = 8; + optional string target_zone = 9; + optional sint32 target_card_id = 10 [default = -1]; } diff --git a/common/pb/command_deck_del.proto b/common/pb/command_deck_del.proto index 261ad630..a231ca08 100644 --- a/common/pb/command_deck_del.proto +++ b/common/pb/command_deck_del.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_DeckDel { - extend SessionCommand { - optional Command_DeckDel ext = 1011; - } - optional sint32 deck_id = 1 [default = -1]; + extend SessionCommand { + optional Command_DeckDel ext = 1011; + } + optional sint32 deck_id = 1 [default = -1]; } diff --git a/common/pb/command_deck_del_dir.proto b/common/pb/command_deck_del_dir.proto index 2eb80f7c..3364cac4 100644 --- a/common/pb/command_deck_del_dir.proto +++ b/common/pb/command_deck_del_dir.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_DeckDelDir { - extend SessionCommand { - optional Command_DeckDelDir ext = 1010; - } - optional string path = 1; + extend SessionCommand { + optional Command_DeckDelDir ext = 1010; + } + optional string path = 1; } diff --git a/common/pb/command_deck_download.proto b/common/pb/command_deck_download.proto index 822754ca..19dda152 100644 --- a/common/pb/command_deck_download.proto +++ b/common/pb/command_deck_download.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_DeckDownload { - extend SessionCommand { - optional Command_DeckDownload ext = 1012; - } - optional sint32 deck_id = 1 [default = -1]; + extend SessionCommand { + optional Command_DeckDownload ext = 1012; + } + optional sint32 deck_id = 1 [default = -1]; } diff --git a/common/pb/command_deck_list.proto b/common/pb/command_deck_list.proto index a1e99208..43f1b0e0 100644 --- a/common/pb/command_deck_list.proto +++ b/common/pb/command_deck_list.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_DeckList { - extend SessionCommand { - optional Command_DeckList ext = 1008; - } + extend SessionCommand { + optional Command_DeckList ext = 1008; + } } diff --git a/common/pb/command_deck_new_dir.proto b/common/pb/command_deck_new_dir.proto index a88d1513..ecb3b106 100644 --- a/common/pb/command_deck_new_dir.proto +++ b/common/pb/command_deck_new_dir.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_DeckNewDir { - extend SessionCommand { - optional Command_DeckNewDir ext = 1009; - } - optional string path = 1; - optional string dir_name = 2; + extend SessionCommand { + optional Command_DeckNewDir ext = 1009; + } + optional string path = 1; + optional string dir_name = 2; } \ No newline at end of file diff --git a/common/pb/command_deck_select.proto b/common/pb/command_deck_select.proto index e696dae0..d79bf37e 100644 --- a/common/pb/command_deck_select.proto +++ b/common/pb/command_deck_select.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_DeckSelect { - extend GameCommand { - optional Command_DeckSelect ext = 1029; - } - optional string deck = 1; - optional sint32 deck_id = 2 [default = -1]; + extend GameCommand { + optional Command_DeckSelect ext = 1029; + } + optional string deck = 1; + optional sint32 deck_id = 2 [default = -1]; } diff --git a/common/pb/command_deck_upload.proto b/common/pb/command_deck_upload.proto index 41f43ab4..0f250c5f 100644 --- a/common/pb/command_deck_upload.proto +++ b/common/pb/command_deck_upload.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_DeckUpload { - extend SessionCommand { - optional Command_DeckUpload ext = 1013; - } - optional string path = 1; // to upload a new deck - optional uint32 deck_id = 2; // to replace an existing deck - optional string deck_list = 3; + extend SessionCommand { + optional Command_DeckUpload ext = 1013; + } + optional string path = 1; // to upload a new deck + optional uint32 deck_id = 2; // to replace an existing deck + optional string deck_list = 3; } diff --git a/common/pb/command_del_counter.proto b/common/pb/command_del_counter.proto index 1e59b336..8d2689a2 100644 --- a/common/pb/command_del_counter.proto +++ b/common/pb/command_del_counter.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_DelCounter { - extend GameCommand { - optional Command_DelCounter ext = 1021; - } - optional sint32 counter_id = 1 [default = -1]; + extend GameCommand { + optional Command_DelCounter ext = 1021; + } + optional sint32 counter_id = 1 [default = -1]; } diff --git a/common/pb/command_delete_arrow.proto b/common/pb/command_delete_arrow.proto index ffccacf0..ba204989 100644 --- a/common/pb/command_delete_arrow.proto +++ b/common/pb/command_delete_arrow.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_DeleteArrow { - extend GameCommand { - optional Command_DeleteArrow ext = 1012; - } - optional sint32 arrow_id = 1 [default = -1]; + extend GameCommand { + optional Command_DeleteArrow ext = 1012; + } + optional sint32 arrow_id = 1 [default = -1]; } diff --git a/common/pb/command_draw_cards.proto b/common/pb/command_draw_cards.proto index 2873d915..6851ac00 100644 --- a/common/pb/command_draw_cards.proto +++ b/common/pb/command_draw_cards.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_DrawCards { - extend GameCommand { - optional Command_DrawCards ext = 1006; - } - optional uint32 number = 1; + extend GameCommand { + optional Command_DrawCards ext = 1006; + } + optional uint32 number = 1; } diff --git a/common/pb/command_dump_zone.proto b/common/pb/command_dump_zone.proto index 15fbe3c0..07bfe195 100644 --- a/common/pb/command_dump_zone.proto +++ b/common/pb/command_dump_zone.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_DumpZone { - extend GameCommand { - optional Command_DumpZone ext = 1024; - } - optional sint32 player_id = 1 [default = -1]; - optional string zone_name = 2; - optional sint32 number_cards = 3; + extend GameCommand { + optional Command_DumpZone ext = 1024; + } + optional sint32 player_id = 1 [default = -1]; + optional string zone_name = 2; + optional sint32 number_cards = 3; } diff --git a/common/pb/command_flip_card.proto b/common/pb/command_flip_card.proto index 25692f9c..d825c64f 100644 --- a/common/pb/command_flip_card.proto +++ b/common/pb/command_flip_card.proto @@ -1,11 +1,12 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_FlipCard { - extend GameCommand { - optional Command_FlipCard ext = 1008; - } - optional string zone = 1; - optional sint32 card_id = 2 [default = -1]; - optional bool face_down = 3; + extend GameCommand { + optional Command_FlipCard ext = 1008; + } + optional string zone = 1; + optional sint32 card_id = 2 [default = -1]; + optional bool face_down = 3; } diff --git a/common/pb/command_game_say.proto b/common/pb/command_game_say.proto index 906bd6f1..6aa47e0e 100644 --- a/common/pb/command_game_say.proto +++ b/common/pb/command_game_say.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_GameSay { - extend GameCommand { - optional Command_GameSay ext = 1002; - } - optional string message = 1; + extend GameCommand { + optional Command_GameSay ext = 1002; + } + optional string message = 1; } diff --git a/common/pb/command_inc_card_counter.proto b/common/pb/command_inc_card_counter.proto index e8b5f1ca..8676fc89 100644 --- a/common/pb/command_inc_card_counter.proto +++ b/common/pb/command_inc_card_counter.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_IncCardCounter { - extend GameCommand { - optional Command_IncCardCounter ext = 1015; - } - optional string zone = 1; - optional sint32 card_id = 2 [default = -1]; - optional sint32 counter_id = 3 [default = -1]; - optional sint32 counter_delta = 4; + extend GameCommand { + optional Command_IncCardCounter ext = 1015; + } + optional string zone = 1; + optional sint32 card_id = 2 [default = -1]; + optional sint32 counter_id = 3 [default = -1]; + optional sint32 counter_delta = 4; } diff --git a/common/pb/command_inc_counter.proto b/common/pb/command_inc_counter.proto index a14a2801..d99521b7 100644 --- a/common/pb/command_inc_counter.proto +++ b/common/pb/command_inc_counter.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_IncCounter { - extend GameCommand { - optional Command_IncCounter ext = 1018; - } - optional sint32 counter_id = 1 [default = -1]; - optional sint32 delta = 2; + extend GameCommand { + optional Command_IncCounter ext = 1018; + } + optional sint32 counter_id = 1 [default = -1]; + optional sint32 delta = 2; } diff --git a/common/pb/command_kick_from_game.proto b/common/pb/command_kick_from_game.proto index b340cb07..e95037c7 100644 --- a/common/pb/command_kick_from_game.proto +++ b/common/pb/command_kick_from_game.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_KickFromGame { - extend GameCommand { - optional Command_KickFromGame ext = 1000; + extend GameCommand { + optional Command_KickFromGame ext = 1000; } - optional sint32 player_id = 1 [default = -1]; + optional sint32 player_id = 1 [default = -1]; } diff --git a/common/pb/command_leave_game.proto b/common/pb/command_leave_game.proto index cdae43af..afa1e6c4 100644 --- a/common/pb/command_leave_game.proto +++ b/common/pb/command_leave_game.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_LeaveGame { - extend GameCommand { - optional Command_LeaveGame ext = 1001; - } + extend GameCommand { + optional Command_LeaveGame ext = 1001; + } } diff --git a/common/pb/command_move_card.proto b/common/pb/command_move_card.proto index 315d6423..ad3f7c57 100644 --- a/common/pb/command_move_card.proto +++ b/common/pb/command_move_card.proto @@ -1,25 +1,26 @@ +syntax = "proto2"; import "game_commands.proto"; message CardToMove { - optional sint32 card_id = 1 [default = -1]; - optional bool face_down = 2; - optional string pt = 3; - optional bool tapped = 4; + optional sint32 card_id = 1 [default = -1]; + optional bool face_down = 2; + optional string pt = 3; + optional bool tapped = 4; } message ListOfCardsToMove { - repeated CardToMove card = 1; + repeated CardToMove card = 1; } message Command_MoveCard { - extend GameCommand { - optional Command_MoveCard ext = 1027; - } - optional sint32 start_player_id = 1 [default = -1]; - optional string start_zone = 2; - optional ListOfCardsToMove cards_to_move = 3; - optional sint32 target_player_id = 4 [default = -1]; - optional string target_zone = 5; - optional sint32 x = 6 [default = -1]; - optional sint32 y = 7 [default = -1]; + extend GameCommand { + optional Command_MoveCard ext = 1027; + } + optional sint32 start_player_id = 1 [default = -1]; + optional string start_zone = 2; + optional ListOfCardsToMove cards_to_move = 3; + optional sint32 target_player_id = 4 [default = -1]; + optional string target_zone = 5; + optional sint32 x = 6 [default = -1]; + optional sint32 y = 7 [default = -1]; } diff --git a/common/pb/command_mulligan.proto b/common/pb/command_mulligan.proto index 79c3fde2..f0ffb87c 100644 --- a/common/pb/command_mulligan.proto +++ b/common/pb/command_mulligan.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_Mulligan { - extend GameCommand { - optional Command_Mulligan ext = 1004; - } + extend GameCommand { + optional Command_Mulligan ext = 1004; + } } diff --git a/common/pb/command_next_turn.proto b/common/pb/command_next_turn.proto index d3012df8..802d63cf 100644 --- a/common/pb/command_next_turn.proto +++ b/common/pb/command_next_turn.proto @@ -1,6 +1,7 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_NextTurn { - extend GameCommand { - optional Command_NextTurn ext = 1022; - } + extend GameCommand { + optional Command_NextTurn ext = 1022; + } } diff --git a/common/pb/command_ready_start.proto b/common/pb/command_ready_start.proto index 5f797a0f..1d7d203c 100644 --- a/common/pb/command_ready_start.proto +++ b/common/pb/command_ready_start.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_ReadyStart { - extend GameCommand { - optional Command_ReadyStart ext = 1016; - } - optional bool ready = 1; + extend GameCommand { + optional Command_ReadyStart ext = 1016; + } + optional bool ready = 1; } diff --git a/common/pb/command_replay_delete_match.proto b/common/pb/command_replay_delete_match.proto index 841e7cfc..33d6d44b 100644 --- a/common/pb/command_replay_delete_match.proto +++ b/common/pb/command_replay_delete_match.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_ReplayDeleteMatch { - extend SessionCommand { - optional Command_ReplayDeleteMatch ext = 1103; - } - optional sint32 game_id = 1 [default = -1]; + extend SessionCommand { + optional Command_ReplayDeleteMatch ext = 1103; + } + optional sint32 game_id = 1 [default = -1]; } diff --git a/common/pb/command_replay_download.proto b/common/pb/command_replay_download.proto index ff7e2b34..17724e5b 100644 --- a/common/pb/command_replay_download.proto +++ b/common/pb/command_replay_download.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_ReplayDownload { - extend SessionCommand { - optional Command_ReplayDownload ext = 1101; - } - optional sint32 replay_id = 1 [default = -1]; + extend SessionCommand { + optional Command_ReplayDownload ext = 1101; + } + optional sint32 replay_id = 1 [default = -1]; } diff --git a/common/pb/command_replay_list.proto b/common/pb/command_replay_list.proto index 2dc0e92e..8fbcdc5a 100644 --- a/common/pb/command_replay_list.proto +++ b/common/pb/command_replay_list.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_ReplayList { - extend SessionCommand { - optional Command_ReplayList ext = 1100; - } + extend SessionCommand { + optional Command_ReplayList ext = 1100; + } } diff --git a/common/pb/command_replay_modify_match.proto b/common/pb/command_replay_modify_match.proto index 28e0fc0a..6b342f44 100644 --- a/common/pb/command_replay_modify_match.proto +++ b/common/pb/command_replay_modify_match.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "session_commands.proto"; message Command_ReplayModifyMatch { - extend SessionCommand { - optional Command_ReplayModifyMatch ext = 1102; - } - optional sint32 game_id = 1 [default = -1]; - optional bool do_not_hide = 2; + extend SessionCommand { + optional Command_ReplayModifyMatch ext = 1102; + } + optional sint32 game_id = 1 [default = -1]; + optional bool do_not_hide = 2; } diff --git a/common/pb/command_reveal_cards.proto b/common/pb/command_reveal_cards.proto index 263fef63..5c4024ba 100644 --- a/common/pb/command_reveal_cards.proto +++ b/common/pb/command_reveal_cards.proto @@ -1,11 +1,12 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_RevealCards { - extend GameCommand { - optional Command_RevealCards ext = 1026; - } - optional string zone_name = 1; - optional sint32 card_id = 2 [default = -1]; - optional sint32 player_id = 3 [default = -1]; - optional bool grant_write_access = 4; + extend GameCommand { + optional Command_RevealCards ext = 1026; + } + optional string zone_name = 1; + optional sint32 card_id = 2 [default = -1]; + optional sint32 player_id = 3 [default = -1]; + optional bool grant_write_access = 4; optional sint32 top_cards = 5 [default = -1]; } diff --git a/common/pb/command_roll_die.proto b/common/pb/command_roll_die.proto index f5d9c72e..bdcc7b51 100644 --- a/common/pb/command_roll_die.proto +++ b/common/pb/command_roll_die.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_RollDie { - extend GameCommand { - optional Command_RollDie ext = 1005; - } - optional uint32 sides = 1; + extend GameCommand { + optional Command_RollDie ext = 1005; + } + optional uint32 sides = 1; } diff --git a/common/pb/command_set_active_phase.proto b/common/pb/command_set_active_phase.proto index 66404cd2..ded7d819 100644 --- a/common/pb/command_set_active_phase.proto +++ b/common/pb/command_set_active_phase.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_SetActivePhase { - extend GameCommand { - optional Command_SetActivePhase ext = 1023; - } - optional uint32 phase = 1; + extend GameCommand { + optional Command_SetActivePhase ext = 1023; + } + optional uint32 phase = 1; } diff --git a/common/pb/command_set_card_attr.proto b/common/pb/command_set_card_attr.proto index b8a8fb60..1e1d8664 100644 --- a/common/pb/command_set_card_attr.proto +++ b/common/pb/command_set_card_attr.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "game_commands.proto"; import "card_attributes.proto"; message Command_SetCardAttr { - extend GameCommand { - optional Command_SetCardAttr ext = 1013; - } - optional string zone = 1; - optional sint32 card_id = 2 [default = -1]; - optional CardAttribute attribute = 3; - optional string attr_value = 4; + extend GameCommand { + optional Command_SetCardAttr ext = 1013; + } + optional string zone = 1; + optional sint32 card_id = 2 [default = -1]; + optional CardAttribute attribute = 3; + optional string attr_value = 4; } diff --git a/common/pb/command_set_card_counter.proto b/common/pb/command_set_card_counter.proto index b258a861..5fa9c3ee 100644 --- a/common/pb/command_set_card_counter.proto +++ b/common/pb/command_set_card_counter.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_SetCardCounter { - extend GameCommand { - optional Command_SetCardCounter ext = 1014; - } - optional string zone = 1; - optional sint32 card_id = 2 [default = -1]; - optional sint32 counter_id = 3 [default = -1]; - optional sint32 counter_value = 4; + extend GameCommand { + optional Command_SetCardCounter ext = 1014; + } + optional string zone = 1; + optional sint32 card_id = 2 [default = -1]; + optional sint32 counter_id = 3 [default = -1]; + optional sint32 counter_value = 4; } diff --git a/common/pb/command_set_counter.proto b/common/pb/command_set_counter.proto index a2f34cc2..33cae56a 100644 --- a/common/pb/command_set_counter.proto +++ b/common/pb/command_set_counter.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_SetCounter { - extend GameCommand { - optional Command_SetCounter ext = 1020; - } - optional sint32 counter_id = 1 [default = -1]; - optional sint32 value = 2; + extend GameCommand { + optional Command_SetCounter ext = 1020; + } + optional sint32 counter_id = 1 [default = -1]; + optional sint32 value = 2; } diff --git a/common/pb/command_set_sideboard_lock.proto b/common/pb/command_set_sideboard_lock.proto index 1f9e8ba1..5aa6a64b 100644 --- a/common/pb/command_set_sideboard_lock.proto +++ b/common/pb/command_set_sideboard_lock.proto @@ -1,7 +1,8 @@ - import "game_commands.proto"; +syntax = "proto2"; +import "game_commands.proto"; message Command_SetSideboardLock { - extend GameCommand { - optional Command_SetSideboardLock ext = 1030; - } - optional bool locked = 1; + extend GameCommand { + optional Command_SetSideboardLock ext = 1030; + } + optional bool locked = 1; } diff --git a/common/pb/command_set_sideboard_plan.proto b/common/pb/command_set_sideboard_plan.proto index 09061c8b..7ed0d10c 100644 --- a/common/pb/command_set_sideboard_plan.proto +++ b/common/pb/command_set_sideboard_plan.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_commands.proto"; import "move_card_to_zone.proto"; message Command_SetSideboardPlan { - extend GameCommand { - optional Command_SetSideboardPlan ext = 1028; - } - repeated MoveCard_ToZone move_list = 1; + extend GameCommand { + optional Command_SetSideboardPlan ext = 1028; + } + repeated MoveCard_ToZone move_list = 1; } diff --git a/common/pb/command_shuffle.proto b/common/pb/command_shuffle.proto index 26ee0422..e2e1d5ec 100644 --- a/common/pb/command_shuffle.proto +++ b/common/pb/command_shuffle.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_Shuffle { - extend GameCommand { - optional Command_Shuffle ext = 1003; - } + extend GameCommand { + optional Command_Shuffle ext = 1003; + } } diff --git a/common/pb/command_stop_dump_zone.proto b/common/pb/command_stop_dump_zone.proto index 1a7539c1..1896c9dc 100644 --- a/common/pb/command_stop_dump_zone.proto +++ b/common/pb/command_stop_dump_zone.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_StopDumpZone { - extend GameCommand { - optional Command_StopDumpZone ext = 1025; - } - optional sint32 player_id = 1; - optional string zone_name = 2; + extend GameCommand { + optional Command_StopDumpZone ext = 1025; + } + optional sint32 player_id = 1; + optional string zone_name = 2; } diff --git a/common/pb/command_undo_draw.proto b/common/pb/command_undo_draw.proto index f51cb46c..09428933 100644 --- a/common/pb/command_undo_draw.proto +++ b/common/pb/command_undo_draw.proto @@ -1,6 +1,7 @@ +syntax = "proto2"; import "game_commands.proto"; message Command_UndoDraw { - extend GameCommand { - optional Command_UndoDraw ext = 1007; - } + extend GameCommand { + optional Command_UndoDraw ext = 1007; + } } diff --git a/common/pb/commands.proto b/common/pb/commands.proto index 3796190d..b417550a 100644 --- a/common/pb/commands.proto +++ b/common/pb/commands.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; import "session_commands.proto"; import "game_commands.proto"; import "room_commands.proto"; @@ -5,14 +6,14 @@ import "moderator_commands.proto"; import "admin_commands.proto"; message CommandContainer { - optional uint64 cmd_id = 1; - - optional uint32 game_id = 10; - optional uint32 room_id = 20; - - repeated SessionCommand session_command = 100; - repeated GameCommand game_command = 101; - repeated RoomCommand room_command = 102; - repeated ModeratorCommand moderator_command = 103; - repeated AdminCommand admin_command = 104; + optional uint64 cmd_id = 1; + + optional uint32 game_id = 10; + optional uint32 room_id = 20; + + repeated SessionCommand session_command = 100; + repeated GameCommand game_command = 101; + repeated RoomCommand room_command = 102; + repeated ModeratorCommand moderator_command = 103; + repeated AdminCommand admin_command = 104; } diff --git a/common/pb/context_concede.proto b/common/pb/context_concede.proto index 2ece178f..7b82aa31 100644 --- a/common/pb/context_concede.proto +++ b/common/pb/context_concede.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_Concede { - extend GameEventContext { - optional Context_Concede ext = 1001; - } + extend GameEventContext { + optional Context_Concede ext = 1001; + } } diff --git a/common/pb/context_connection_state_changed.proto b/common/pb/context_connection_state_changed.proto index d1f592ee..c87f8bed 100644 --- a/common/pb/context_connection_state_changed.proto +++ b/common/pb/context_connection_state_changed.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_ConnectionStateChanged { - extend GameEventContext { - optional Context_ConnectionStateChanged ext = 1007; - } + extend GameEventContext { + optional Context_ConnectionStateChanged ext = 1007; + } } diff --git a/common/pb/context_deck_select.proto b/common/pb/context_deck_select.proto index f36c8b6c..dbd4ce16 100644 --- a/common/pb/context_deck_select.proto +++ b/common/pb/context_deck_select.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_DeckSelect { diff --git a/common/pb/context_move_card.proto b/common/pb/context_move_card.proto index d8f7139d..49bcb77c 100644 --- a/common/pb/context_move_card.proto +++ b/common/pb/context_move_card.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_MoveCard { - extend GameEventContext { - optional Context_MoveCard ext = 1004; - } + extend GameEventContext { + optional Context_MoveCard ext = 1004; + } } diff --git a/common/pb/context_mulligan.proto b/common/pb/context_mulligan.proto index d5ef0bcc..edcaf900 100644 --- a/common/pb/context_mulligan.proto +++ b/common/pb/context_mulligan.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_Mulligan { - extend GameEventContext { - optional Context_Mulligan ext = 1005; - } - optional uint32 number = 1; + extend GameEventContext { + optional Context_Mulligan ext = 1005; + } + optional uint32 number = 1; } diff --git a/common/pb/context_ping_changed.proto b/common/pb/context_ping_changed.proto index 8ea43c5f..e8f1b0bd 100644 --- a/common/pb/context_ping_changed.proto +++ b/common/pb/context_ping_changed.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_PingChanged { - extend GameEventContext { - optional Context_PingChanged ext = 1006; - } + extend GameEventContext { + optional Context_PingChanged ext = 1006; + } } diff --git a/common/pb/context_ready_start.proto b/common/pb/context_ready_start.proto index ac16ef64..7a4e6089 100644 --- a/common/pb/context_ready_start.proto +++ b/common/pb/context_ready_start.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_ReadyStart { - extend GameEventContext { - optional Context_ReadyStart ext = 1000; - } + extend GameEventContext { + optional Context_ReadyStart ext = 1000; + } } diff --git a/common/pb/context_set_sideboard_lock.proto b/common/pb/context_set_sideboard_lock.proto index c62c5bd1..a9a36f8b 100644 --- a/common/pb/context_set_sideboard_lock.proto +++ b/common/pb/context_set_sideboard_lock.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_SetSideboardLock { - extend GameEventContext { - optional Context_SetSideboardLock ext = 1008; - } + extend GameEventContext { + optional Context_SetSideboardLock ext = 1008; + } } diff --git a/common/pb/context_undo_draw.proto b/common/pb/context_undo_draw.proto index 513aa9ad..8b934079 100644 --- a/common/pb/context_undo_draw.proto +++ b/common/pb/context_undo_draw.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event_context.proto"; message Context_UndoDraw { - extend GameEventContext { - optional Context_UndoDraw ext = 1003; - } + extend GameEventContext { + optional Context_UndoDraw ext = 1003; + } } diff --git a/common/pb/event_add_to_list.proto b/common/pb/event_add_to_list.proto index 5f168eea..4a1072e8 100644 --- a/common/pb/event_add_to_list.proto +++ b/common/pb/event_add_to_list.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "session_event.proto"; import "serverinfo_user.proto"; message Event_AddToList { - extend SessionEvent { - optional Event_AddToList ext = 1005; - } - optional string list_name = 1; - optional ServerInfo_User user_info = 2; + extend SessionEvent { + optional Event_AddToList ext = 1005; + } + optional string list_name = 1; + optional ServerInfo_User user_info = 2; } diff --git a/common/pb/event_attach_card.proto b/common/pb/event_attach_card.proto index 56eef093..a71610f9 100644 --- a/common/pb/event_attach_card.proto +++ b/common/pb/event_attach_card.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "game_event.proto"; message Event_AttachCard { - extend GameEvent { - optional Event_AttachCard ext = 2012; - } - optional string start_zone = 1; - optional sint32 card_id = 2; - optional sint32 target_player_id = 3; - optional string target_zone = 4; - optional sint32 target_card_id = 5; + extend GameEvent { + optional Event_AttachCard ext = 2012; + } + optional string start_zone = 1; + optional sint32 card_id = 2; + optional sint32 target_player_id = 3; + optional string target_zone = 4; + optional sint32 target_card_id = 5; } diff --git a/common/pb/event_change_zone_properties.proto b/common/pb/event_change_zone_properties.proto index 7e86fe0a..0f1deb6d 100644 --- a/common/pb/event_change_zone_properties.proto +++ b/common/pb/event_change_zone_properties.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_event.proto"; message Event_ChangeZoneProperties { - extend GameEvent { - optional Event_ChangeZoneProperties ext = 2020; - } - optional string zone_name = 1; - - optional bool always_reveal_top_card = 10; + extend GameEvent { + optional Event_ChangeZoneProperties ext = 2020; + } + optional string zone_name = 1; + + optional bool always_reveal_top_card = 10; } diff --git a/common/pb/event_connection_closed.proto b/common/pb/event_connection_closed.proto index e516f172..03018b80 100644 --- a/common/pb/event_connection_closed.proto +++ b/common/pb/event_connection_closed.proto @@ -1,17 +1,21 @@ +syntax = "proto2"; import "session_event.proto"; message Event_ConnectionClosed { - extend SessionEvent { - optional Event_ConnectionClosed ext = 1002; - } - enum CloseReason { - OTHER = 1; - SERVER_SHUTDOWN = 2; - TOO_MANY_CONNECTIONS = 3; - BANNED = 4; - USERNAMEINVALID = 5; - } - optional CloseReason reason = 1; - optional string reason_str = 2; - optional uint32 end_time = 3; + extend SessionEvent { + optional Event_ConnectionClosed ext = 1002; + } + enum CloseReason { + OTHER = 1; + SERVER_SHUTDOWN = 2; + TOO_MANY_CONNECTIONS = 3; + BANNED = 4; + USERNAMEINVALID = 5; + USER_LIMIT_REACHED = 6; + DEMOTED = 7; + LOGGEDINELSEWERE = 8; + } + optional CloseReason reason = 1; + optional string reason_str = 2; + optional uint32 end_time = 3; } diff --git a/common/pb/event_create_arrow.proto b/common/pb/event_create_arrow.proto index 6cbd04f3..820d3cea 100644 --- a/common/pb/event_create_arrow.proto +++ b/common/pb/event_create_arrow.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_arrow.proto"; message Event_CreateArrow { - extend GameEvent { - optional Event_CreateArrow ext = 2000; - } - optional ServerInfo_Arrow arrow_info = 1; + extend GameEvent { + optional Event_CreateArrow ext = 2000; + } + optional ServerInfo_Arrow arrow_info = 1; } diff --git a/common/pb/event_create_counter.proto b/common/pb/event_create_counter.proto index 09499964..5dfca01e 100644 --- a/common/pb/event_create_counter.proto +++ b/common/pb/event_create_counter.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_counter.proto"; message Event_CreateCounter { - extend GameEvent { - optional Event_CreateCounter ext = 2002; - } - optional ServerInfo_Counter counter_info = 1; + extend GameEvent { + optional Event_CreateCounter ext = 2002; + } + optional ServerInfo_Counter counter_info = 1; } diff --git a/common/pb/event_create_token.proto b/common/pb/event_create_token.proto index 4dde7251..42c32eff 100644 --- a/common/pb/event_create_token.proto +++ b/common/pb/event_create_token.proto @@ -1,16 +1,17 @@ +syntax = "proto2"; import "game_event.proto"; message Event_CreateToken { - extend GameEvent { - optional Event_CreateToken ext = 2013; - } - optional string zone_name = 1; - optional sint32 card_id = 2; - optional string card_name = 3; - optional string color = 4; - optional string pt = 5; - optional string annotation = 6; - optional bool destroy_on_zone_change = 7; - optional sint32 x = 8; - optional sint32 y = 9; + extend GameEvent { + optional Event_CreateToken ext = 2013; + } + optional string zone_name = 1; + optional sint32 card_id = 2; + optional string card_name = 3; + optional string color = 4; + optional string pt = 5; + optional string annotation = 6; + optional bool destroy_on_zone_change = 7; + optional sint32 x = 8; + optional sint32 y = 9; } diff --git a/common/pb/event_del_counter.proto b/common/pb/event_del_counter.proto index 85616e4e..c693fc86 100644 --- a/common/pb/event_del_counter.proto +++ b/common/pb/event_del_counter.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_DelCounter { - extend GameEvent { - optional Event_DelCounter ext = 2004; - } - optional sint32 counter_id = 1; + extend GameEvent { + optional Event_DelCounter ext = 2004; + } + optional sint32 counter_id = 1; } diff --git a/common/pb/event_delete_arrow.proto b/common/pb/event_delete_arrow.proto index f1d40d24..bd165c97 100644 --- a/common/pb/event_delete_arrow.proto +++ b/common/pb/event_delete_arrow.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_DeleteArrow { - extend GameEvent { - optional Event_DeleteArrow ext = 2001; - } - optional sint32 arrow_id = 1; + extend GameEvent { + optional Event_DeleteArrow ext = 2001; + } + optional sint32 arrow_id = 1; } diff --git a/common/pb/event_destroy_card.proto b/common/pb/event_destroy_card.proto index 6bea092c..8bef2242 100644 --- a/common/pb/event_destroy_card.proto +++ b/common/pb/event_destroy_card.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; message Event_DestroyCard { - extend GameEvent { - optional Event_DestroyCard ext = 2011; - } - optional string zone_name = 1; - optional uint32 card_id = 2; + extend GameEvent { + optional Event_DestroyCard ext = 2011; + } + optional string zone_name = 1; + optional uint32 card_id = 2; } diff --git a/common/pb/event_draw_cards.proto b/common/pb/event_draw_cards.proto index a4165d29..cea343a1 100644 --- a/common/pb/event_draw_cards.proto +++ b/common/pb/event_draw_cards.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_card.proto"; message Event_DrawCards { - extend GameEvent { - optional Event_DrawCards ext = 2005; - } - optional sint32 number = 1; - repeated ServerInfo_Card cards = 2; + extend GameEvent { + optional Event_DrawCards ext = 2005; + } + optional sint32 number = 1; + repeated ServerInfo_Card cards = 2; } diff --git a/common/pb/event_dump_zone.proto b/common/pb/event_dump_zone.proto index 933b9c50..f3dc0852 100644 --- a/common/pb/event_dump_zone.proto +++ b/common/pb/event_dump_zone.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "game_event.proto"; message Event_DumpZone { - extend GameEvent { - optional Event_DumpZone ext = 2018; - } - optional sint32 zone_owner_id = 1; - optional string zone_name = 2; - optional sint32 number_cards = 3; + extend GameEvent { + optional Event_DumpZone ext = 2018; + } + optional sint32 zone_owner_id = 1; + optional string zone_name = 2; + optional sint32 number_cards = 3; } diff --git a/common/pb/event_flip_card.proto b/common/pb/event_flip_card.proto index 4d870440..08a5d8c6 100644 --- a/common/pb/event_flip_card.proto +++ b/common/pb/event_flip_card.proto @@ -1,11 +1,12 @@ +syntax = "proto2"; import "game_event.proto"; message Event_FlipCard { - extend GameEvent { - optional Event_FlipCard ext = 2010; - } - optional string zone_name = 1; - optional sint32 card_id = 2; - optional string card_name = 3; - optional bool face_down = 4; + extend GameEvent { + optional Event_FlipCard ext = 2010; + } + optional string zone_name = 1; + optional sint32 card_id = 2; + optional string card_name = 3; + optional bool face_down = 4; } diff --git a/common/pb/event_game_closed.proto b/common/pb/event_game_closed.proto index 723f8263..e400926c 100644 --- a/common/pb/event_game_closed.proto +++ b/common/pb/event_game_closed.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event.proto"; message Event_GameClosed { - extend GameEvent { - optional Event_GameClosed ext = 1002; - } + extend GameEvent { + optional Event_GameClosed ext = 1002; + } } diff --git a/common/pb/event_game_host_changed.proto b/common/pb/event_game_host_changed.proto index c4c42f6d..50e3f968 100644 --- a/common/pb/event_game_host_changed.proto +++ b/common/pb/event_game_host_changed.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_GameHostChanged { - extend GameEvent { - optional Event_GameHostChanged ext = 1003; - } + extend GameEvent { + optional Event_GameHostChanged ext = 1003; + } } diff --git a/common/pb/event_game_joined.proto b/common/pb/event_game_joined.proto index aa344ade..eaf2894c 100644 --- a/common/pb/event_game_joined.proto +++ b/common/pb/event_game_joined.proto @@ -1,15 +1,16 @@ +syntax = "proto2"; import "session_event.proto"; import "serverinfo_game.proto"; import "serverinfo_gametype.proto"; message Event_GameJoined { - extend SessionEvent { - optional Event_GameJoined ext = 1009; - } - optional ServerInfo_Game game_info = 1; - repeated ServerInfo_GameType game_types = 2; - optional sint32 host_id = 3; - optional sint32 player_id = 4; - optional bool spectator = 5; - optional bool resuming = 6; + extend SessionEvent { + optional Event_GameJoined ext = 1009; + } + optional ServerInfo_Game game_info = 1; + repeated ServerInfo_GameType game_types = 2; + optional sint32 host_id = 3; + optional sint32 player_id = 4; + optional bool spectator = 5; + optional bool resuming = 6; } diff --git a/common/pb/event_game_say.proto b/common/pb/event_game_say.proto index 4746b7b5..8aa42eca 100644 --- a/common/pb/event_game_say.proto +++ b/common/pb/event_game_say.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_GameSay { - extend GameEvent { - optional Event_GameSay ext = 1009; - } - optional string message = 1; + extend GameEvent { + optional Event_GameSay ext = 1009; + } + optional string message = 1; } diff --git a/common/pb/event_game_state_changed.proto b/common/pb/event_game_state_changed.proto index a4055c02..092d45de 100644 --- a/common/pb/event_game_state_changed.proto +++ b/common/pb/event_game_state_changed.proto @@ -1,13 +1,14 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_player.proto"; message Event_GameStateChanged { - extend GameEvent { - optional Event_GameStateChanged ext = 1005; - } - repeated ServerInfo_Player player_list = 1; - optional bool game_started = 2; - optional sint32 active_player_id = 3; - optional sint32 active_phase = 4; - optional uint32 seconds_elapsed = 5; + extend GameEvent { + optional Event_GameStateChanged ext = 1005; + } + repeated ServerInfo_Player player_list = 1; + optional bool game_started = 2; + optional sint32 active_player_id = 3; + optional sint32 active_phase = 4; + optional uint32 seconds_elapsed = 5; } diff --git a/common/pb/event_join.proto b/common/pb/event_join.proto index 94757838..1c91dc09 100644 --- a/common/pb/event_join.proto +++ b/common/pb/event_join.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_playerproperties.proto"; message Event_Join { - extend GameEvent { - optional Event_Join ext = 1000; - } - optional ServerInfo_PlayerProperties player_properties = 1; + extend GameEvent { + optional Event_Join ext = 1000; + } + optional ServerInfo_PlayerProperties player_properties = 1; } diff --git a/common/pb/event_join_room.proto b/common/pb/event_join_room.proto index f031eee9..e575edc4 100644 --- a/common/pb/event_join_room.proto +++ b/common/pb/event_join_room.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "room_event.proto"; import "serverinfo_user.proto"; message Event_JoinRoom { - extend RoomEvent { - optional Event_JoinRoom ext = 1001; - } - optional ServerInfo_User user_info = 1; + extend RoomEvent { + optional Event_JoinRoom ext = 1001; + } + optional ServerInfo_User user_info = 1; } diff --git a/common/pb/event_kicked.proto b/common/pb/event_kicked.proto index 0163f5c5..02036cee 100644 --- a/common/pb/event_kicked.proto +++ b/common/pb/event_kicked.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event.proto"; message Event_Kicked { - extend GameEvent { - optional Event_Kicked ext = 1004; - } + extend GameEvent { + optional Event_Kicked ext = 1004; + } } diff --git a/common/pb/event_leave.proto b/common/pb/event_leave.proto index 19b46e25..c4a3740f 100644 --- a/common/pb/event_leave.proto +++ b/common/pb/event_leave.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "game_event.proto"; message Event_Leave { - extend GameEvent { - optional Event_Leave ext = 1001; - } + extend GameEvent { + optional Event_Leave ext = 1001; + } } diff --git a/common/pb/event_leave_room.proto b/common/pb/event_leave_room.proto index a49737a4..98dafe2d 100644 --- a/common/pb/event_leave_room.proto +++ b/common/pb/event_leave_room.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "room_event.proto"; message Event_LeaveRoom { - extend RoomEvent { - optional Event_LeaveRoom ext = 1000; - } - optional string name = 1; + extend RoomEvent { + optional Event_LeaveRoom ext = 1000; + } + optional string name = 1; } diff --git a/common/pb/event_list_games.proto b/common/pb/event_list_games.proto index e3c5b8ac..b69c7b5c 100644 --- a/common/pb/event_list_games.proto +++ b/common/pb/event_list_games.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "room_event.proto"; import "serverinfo_game.proto"; message Event_ListGames { - extend RoomEvent { - optional Event_ListGames ext = 1003; - } - repeated ServerInfo_Game game_list = 1; + extend RoomEvent { + optional Event_ListGames ext = 1003; + } + repeated ServerInfo_Game game_list = 1; } diff --git a/common/pb/event_list_rooms.proto b/common/pb/event_list_rooms.proto index 3969cbd5..e72d2ba1 100644 --- a/common/pb/event_list_rooms.proto +++ b/common/pb/event_list_rooms.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_event.proto"; import "serverinfo_room.proto"; message Event_ListRooms { - extend SessionEvent { - optional Event_ListRooms ext = 1004; - } - repeated ServerInfo_Room room_list = 1; + extend SessionEvent { + optional Event_ListRooms ext = 1004; + } + repeated ServerInfo_Room room_list = 1; } diff --git a/common/pb/event_move_card.proto b/common/pb/event_move_card.proto index 048f3a8d..f6d309f3 100644 --- a/common/pb/event_move_card.proto +++ b/common/pb/event_move_card.proto @@ -1,18 +1,19 @@ +syntax = "proto2"; import "game_event.proto"; message Event_MoveCard { - extend GameEvent { - optional Event_MoveCard ext = 2009; - } - optional sint32 card_id = 1 [default = -1]; - optional string card_name = 2; - optional sint32 start_player_id = 3 [default = -1]; - optional string start_zone = 4; - optional sint32 position = 5 [default = -1]; - optional sint32 target_player_id = 6 [default = -1]; - optional string target_zone = 7; - optional sint32 x = 8 [default = -1]; - optional sint32 y = 9 [default = -1]; - optional sint32 new_card_id = 10 [default = -1]; - optional bool face_down = 11; + extend GameEvent { + optional Event_MoveCard ext = 2009; + } + optional sint32 card_id = 1 [default = -1]; + optional string card_name = 2; + optional sint32 start_player_id = 3 [default = -1]; + optional string start_zone = 4; + optional sint32 position = 5 [default = -1]; + optional sint32 target_player_id = 6 [default = -1]; + optional string target_zone = 7; + optional sint32 x = 8 [default = -1]; + optional sint32 y = 9 [default = -1]; + optional sint32 new_card_id = 10 [default = -1]; + optional bool face_down = 11; } diff --git a/common/pb/event_notify_user.proto b/common/pb/event_notify_user.proto new file mode 100644 index 00000000..dadc5b99 --- /dev/null +++ b/common/pb/event_notify_user.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; +import "session_event.proto"; + +message Event_NotifyUser { + + enum NotificationType { + PROMOTED = 1; + } + + extend SessionEvent { + optional Event_NotifyUser ext = 1010; + } + optional NotificationType type = 1; + +} diff --git a/common/pb/event_player_properties_changed.proto b/common/pb/event_player_properties_changed.proto index 03a12c24..c0feaf53 100644 --- a/common/pb/event_player_properties_changed.proto +++ b/common/pb/event_player_properties_changed.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_playerproperties.proto"; message Event_PlayerPropertiesChanged { - extend GameEvent { - optional Event_PlayerPropertiesChanged ext = 1007; - } - optional ServerInfo_PlayerProperties player_properties = 1; + extend GameEvent { + optional Event_PlayerPropertiesChanged ext = 1007; + } + optional ServerInfo_PlayerProperties player_properties = 1; } diff --git a/common/pb/event_remove_from_list.proto b/common/pb/event_remove_from_list.proto index 4b90b1e7..81bb64ed 100644 --- a/common/pb/event_remove_from_list.proto +++ b/common/pb/event_remove_from_list.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_event.proto"; message Event_RemoveFromList { - extend SessionEvent { - optional Event_RemoveFromList ext = 1006; - } - optional string list_name = 1; - optional string user_name = 2; + extend SessionEvent { + optional Event_RemoveFromList ext = 1006; + } + optional string list_name = 1; + optional string user_name = 2; } diff --git a/common/pb/event_replay_added.proto b/common/pb/event_replay_added.proto index c3de2082..2efb5f37 100644 --- a/common/pb/event_replay_added.proto +++ b/common/pb/event_replay_added.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_event.proto"; import "serverinfo_replay_match.proto"; message Event_ReplayAdded { - extend SessionEvent { - optional Event_ReplayAdded ext = 1100; - } - optional ServerInfo_ReplayMatch match_info = 1; + extend SessionEvent { + optional Event_ReplayAdded ext = 1100; + } + optional ServerInfo_ReplayMatch match_info = 1; } diff --git a/common/pb/event_reveal_cards.proto b/common/pb/event_reveal_cards.proto index 1bb386e2..f6742c69 100644 --- a/common/pb/event_reveal_cards.proto +++ b/common/pb/event_reveal_cards.proto @@ -1,13 +1,14 @@ +syntax = "proto2"; import "game_event.proto"; import "serverinfo_card.proto"; message Event_RevealCards { - extend GameEvent { - optional Event_RevealCards ext = 2006; - } - optional string zone_name = 1; - optional sint32 card_id = 2 [default = -1]; - optional sint32 other_player_id = 3 [default = -1]; - repeated ServerInfo_Card cards = 4; - optional bool grant_write_access = 5; + extend GameEvent { + optional Event_RevealCards ext = 2006; + } + optional string zone_name = 1; + optional sint32 card_id = 2 [default = -1]; + optional sint32 other_player_id = 3 [default = -1]; + repeated ServerInfo_Card cards = 4; + optional bool grant_write_access = 5; } diff --git a/common/pb/event_roll_die.proto b/common/pb/event_roll_die.proto index dcc87bce..4d99f2be 100644 --- a/common/pb/event_roll_die.proto +++ b/common/pb/event_roll_die.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; message Event_RollDie { - extend GameEvent { - optional Event_RollDie ext = 2008; - } - optional uint32 sides = 1; - optional uint32 value = 2; + extend GameEvent { + optional Event_RollDie ext = 2008; + } + optional uint32 sides = 1; + optional uint32 value = 2; } diff --git a/common/pb/event_room_say.proto b/common/pb/event_room_say.proto index e5cc1792..49e70e69 100644 --- a/common/pb/event_room_say.proto +++ b/common/pb/event_room_say.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "room_event.proto"; message Event_RoomSay { - extend RoomEvent { - optional Event_RoomSay ext = 1002; - } - optional string name = 1; - optional string message = 2; + extend RoomEvent { + optional Event_RoomSay ext = 1002; + } + optional string name = 1; + optional string message = 2; } diff --git a/common/pb/event_server_complete_list.proto b/common/pb/event_server_complete_list.proto index 4e402e9b..8adba1fd 100644 --- a/common/pb/event_server_complete_list.proto +++ b/common/pb/event_server_complete_list.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "session_event.proto"; import "serverinfo_user.proto"; import "serverinfo_room.proto"; message Event_ServerCompleteList { - extend SessionEvent { - optional Event_ServerCompleteList ext = 600; - } - optional uint32 server_id = 1; - repeated ServerInfo_User user_list = 2; - repeated ServerInfo_Room room_list = 3; + extend SessionEvent { + optional Event_ServerCompleteList ext = 600; + } + optional uint32 server_id = 1; + repeated ServerInfo_User user_list = 2; + repeated ServerInfo_Room room_list = 3; } diff --git a/common/pb/event_server_identification.proto b/common/pb/event_server_identification.proto index d4417c03..6548043e 100644 --- a/common/pb/event_server_identification.proto +++ b/common/pb/event_server_identification.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "session_event.proto"; message Event_ServerIdentification { - extend SessionEvent { - optional Event_ServerIdentification ext = 500; - } - optional string server_name = 1; - optional string server_version = 2; - optional uint32 protocol_version = 3; + extend SessionEvent { + optional Event_ServerIdentification ext = 500; + } + optional string server_name = 1; + optional string server_version = 2; + optional uint32 protocol_version = 3; } diff --git a/common/pb/event_server_message.proto b/common/pb/event_server_message.proto index a0fc252d..a47d4eb7 100644 --- a/common/pb/event_server_message.proto +++ b/common/pb/event_server_message.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "session_event.proto"; message Event_ServerMessage { - extend SessionEvent { - optional Event_ServerMessage ext = 1000; - } - optional string message = 1; + extend SessionEvent { + optional Event_ServerMessage ext = 1000; + } + optional string message = 1; } diff --git a/common/pb/event_server_shutdown.proto b/common/pb/event_server_shutdown.proto index 9a3439a7..879ec1ea 100644 --- a/common/pb/event_server_shutdown.proto +++ b/common/pb/event_server_shutdown.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_event.proto"; message Event_ServerShutdown { - extend SessionEvent { - optional Event_ServerShutdown ext = 1001; - } - optional string reason = 1; - optional uint32 minutes = 2; + extend SessionEvent { + optional Event_ServerShutdown ext = 1001; + } + optional string reason = 1; + optional uint32 minutes = 2; } diff --git a/common/pb/event_set_active_phase.proto b/common/pb/event_set_active_phase.proto index fe4a8088..a19dd4b9 100644 --- a/common/pb/event_set_active_phase.proto +++ b/common/pb/event_set_active_phase.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_SetActivePhase { - extend GameEvent { - optional Event_SetActivePhase ext = 2017; - } - optional sint32 phase = 1; + extend GameEvent { + optional Event_SetActivePhase ext = 2017; + } + optional sint32 phase = 1; } diff --git a/common/pb/event_set_active_player.proto b/common/pb/event_set_active_player.proto index fe872b26..7962ac15 100644 --- a/common/pb/event_set_active_player.proto +++ b/common/pb/event_set_active_player.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_SetActivePlayer { - extend GameEvent { - optional Event_SetActivePlayer ext = 2016; - } - optional sint32 active_player_id = 1; + extend GameEvent { + optional Event_SetActivePlayer ext = 2016; + } + optional sint32 active_player_id = 1; } diff --git a/common/pb/event_set_card_attr.proto b/common/pb/event_set_card_attr.proto index a41bfc31..aa902fdd 100644 --- a/common/pb/event_set_card_attr.proto +++ b/common/pb/event_set_card_attr.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "game_event.proto"; import "card_attributes.proto"; message Event_SetCardAttr { - extend GameEvent { - optional Event_SetCardAttr ext = 2014; - } - optional string zone_name = 1; - optional sint32 card_id = 2; - optional CardAttribute attribute = 3; - optional string attr_value = 4; + extend GameEvent { + optional Event_SetCardAttr ext = 2014; + } + optional string zone_name = 1; + optional sint32 card_id = 2; + optional CardAttribute attribute = 3; + optional string attr_value = 4; } diff --git a/common/pb/event_set_card_counter.proto b/common/pb/event_set_card_counter.proto index 6072b18f..e0426271 100644 --- a/common/pb/event_set_card_counter.proto +++ b/common/pb/event_set_card_counter.proto @@ -1,11 +1,12 @@ +syntax = "proto2"; import "game_event.proto"; message Event_SetCardCounter { - extend GameEvent { - optional Event_SetCardCounter ext = 2015; - } - optional string zone_name = 1; - optional sint32 card_id = 2; - optional sint32 counter_id = 3; - optional sint32 counter_value = 4; + extend GameEvent { + optional Event_SetCardCounter ext = 2015; + } + optional string zone_name = 1; + optional sint32 card_id = 2; + optional sint32 counter_id = 3; + optional sint32 counter_value = 4; } diff --git a/common/pb/event_set_counter.proto b/common/pb/event_set_counter.proto index 941f7201..55541a02 100644 --- a/common/pb/event_set_counter.proto +++ b/common/pb/event_set_counter.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; message Event_SetCounter { - extend GameEvent { - optional Event_SetCounter ext = 2003; - } - optional sint32 counter_id = 1; - optional sint32 value = 2; + extend GameEvent { + optional Event_SetCounter ext = 2003; + } + optional sint32 counter_id = 1; + optional sint32 value = 2; } diff --git a/common/pb/event_shuffle.proto b/common/pb/event_shuffle.proto index e8a461c5..c1fc1afb 100644 --- a/common/pb/event_shuffle.proto +++ b/common/pb/event_shuffle.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "game_event.proto"; message Event_Shuffle { - extend GameEvent { - optional Event_Shuffle ext = 2007; - } - optional string zone_name = 1; + extend GameEvent { + optional Event_Shuffle ext = 2007; + } + optional string zone_name = 1; } diff --git a/common/pb/event_stop_dump_zone.proto b/common/pb/event_stop_dump_zone.proto index f0cff4dd..3e8f1693 100644 --- a/common/pb/event_stop_dump_zone.proto +++ b/common/pb/event_stop_dump_zone.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; message Event_StopDumpZone { - extend GameEvent { - optional Event_StopDumpZone ext = 2019; - } - optional sint32 zone_owner_id = 1; - optional string zone_name = 2; + extend GameEvent { + optional Event_StopDumpZone ext = 2019; + } + optional sint32 zone_owner_id = 1; + optional string zone_name = 2; } diff --git a/common/pb/event_user_joined.proto b/common/pb/event_user_joined.proto index 87e117e1..15ae93c3 100644 --- a/common/pb/event_user_joined.proto +++ b/common/pb/event_user_joined.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "session_event.proto"; import "serverinfo_user.proto"; message Event_UserJoined { - extend SessionEvent { - optional Event_UserJoined ext = 1007; - } - optional ServerInfo_User user_info = 1; + extend SessionEvent { + optional Event_UserJoined ext = 1007; + } + optional ServerInfo_User user_info = 1; } diff --git a/common/pb/event_user_left.proto b/common/pb/event_user_left.proto index b2bf1976..c857640d 100644 --- a/common/pb/event_user_left.proto +++ b/common/pb/event_user_left.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "session_event.proto"; message Event_UserLeft { - extend SessionEvent { - optional Event_UserLeft ext = 1008; - } - optional string name = 1; + extend SessionEvent { + optional Event_UserLeft ext = 1008; + } + optional string name = 1; } diff --git a/common/pb/event_user_message.proto b/common/pb/event_user_message.proto index b41585c2..9cf6003d 100644 --- a/common/pb/event_user_message.proto +++ b/common/pb/event_user_message.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "session_event.proto"; message Event_UserMessage { - extend SessionEvent { - optional Event_UserMessage ext = 1003; - } - optional string sender_name = 1; - optional string receiver_name = 2; - optional string message = 3; + extend SessionEvent { + optional Event_UserMessage ext = 1003; + } + optional string sender_name = 1; + optional string receiver_name = 2; + optional string message = 3; } diff --git a/common/pb/game_commands.proto b/common/pb/game_commands.proto index a9f28699..47d15441 100644 --- a/common/pb/game_commands.proto +++ b/common/pb/game_commands.proto @@ -1,37 +1,38 @@ +syntax = "proto2"; message GameCommand { - enum GameCommandType { - KICK_FROM_GAME = 1000; - LEAVE_GAME = 1001; - GAME_SAY = 1002; - SHUFFLE = 1003; - MULLIGAN = 1004; - ROLL_DIE = 1005; - DRAW_CARDS = 1006; - UNDO_DRAW = 1007; - FLIP_CARD = 1008; - ATTACH_CARD = 1009; - CREATE_TOKEN = 1010; - CREATE_ARROW = 1011; - DELETE_ARROW = 1012; - SET_CARD_ATTR = 1013; - SET_CARD_COUNTER = 1014; - INC_CARD_COUNTER = 1015; - READY_START = 1016; - CONCEDE = 1017; - INC_COUNTER = 1018; - CREATE_COUNTER = 1019; - SET_COUNTER = 1020; - DEL_COUNTER = 1021; - NEXT_TURN = 1022; - SET_ACTIVE_PHASE = 1023; - DUMP_ZONE = 1024; - STOP_DUMP_ZONE = 1025; - REVEAL_CARDS = 1026; - MOVE_CARD = 1027; - SET_SIDEBOARD_PLAN = 1028; - DECK_SELECT = 1029; - SET_SIDEBOARD_LOCK = 1030; - CHANGE_ZONE_PROPERTIES = 1031; - } - extensions 100 to max; + enum GameCommandType { + KICK_FROM_GAME = 1000; + LEAVE_GAME = 1001; + GAME_SAY = 1002; + SHUFFLE = 1003; + MULLIGAN = 1004; + ROLL_DIE = 1005; + DRAW_CARDS = 1006; + UNDO_DRAW = 1007; + FLIP_CARD = 1008; + ATTACH_CARD = 1009; + CREATE_TOKEN = 1010; + CREATE_ARROW = 1011; + DELETE_ARROW = 1012; + SET_CARD_ATTR = 1013; + SET_CARD_COUNTER = 1014; + INC_CARD_COUNTER = 1015; + READY_START = 1016; + CONCEDE = 1017; + INC_COUNTER = 1018; + CREATE_COUNTER = 1019; + SET_COUNTER = 1020; + DEL_COUNTER = 1021; + NEXT_TURN = 1022; + SET_ACTIVE_PHASE = 1023; + DUMP_ZONE = 1024; + STOP_DUMP_ZONE = 1025; + REVEAL_CARDS = 1026; + MOVE_CARD = 1027; + SET_SIDEBOARD_PLAN = 1028; + DECK_SELECT = 1029; + SET_SIDEBOARD_LOCK = 1030; + CHANGE_ZONE_PROPERTIES = 1031; + } + extensions 100 to max; } diff --git a/common/pb/game_event.proto b/common/pb/game_event.proto index 830c783c..09fbe93b 100644 --- a/common/pb/game_event.proto +++ b/common/pb/game_event.proto @@ -1,35 +1,36 @@ +syntax = "proto2"; message GameEvent { - enum GameEventType { - JOIN = 1000; - LEAVE = 1001; - GAME_CLOSED = 1002; - GAME_HOST_CHANGED = 1003; - KICKED = 1004; - GAME_STATE_CHANGED = 1005; - PLAYER_PROPERTIES_CHANGED = 1007; - GAME_SAY = 1009; - CREATE_ARROW = 2000; - DELETE_ARROW = 2001; - CREATE_COUNTER = 2002; - SET_COUNTER = 2003; - DEL_COUNTER = 2004; - DRAW_CARDS = 2005; - REVEAL_CARDS = 2006; - SHUFFLE = 2007; - ROLL_DIE = 2008; - MOVE_CARD = 2009; - FLIP_CARD = 2010; - DESTROY_CARD = 2011; - ATTACH_CARD = 2012; - CREATE_TOKEN = 2013; - SET_CARD_ATTR = 2014; - SET_CARD_COUNTER = 2015; - SET_ACTIVE_PLAYER = 2016; - SET_ACTIVE_PHASE = 2017; - DUMP_ZONE = 2018; - STOP_DUMP_ZONE = 2019; - CHANGE_ZONE_PROPERTIES = 2020; - } - optional sint32 player_id = 1 [default = -1]; - extensions 100 to max; + enum GameEventType { + JOIN = 1000; + LEAVE = 1001; + GAME_CLOSED = 1002; + GAME_HOST_CHANGED = 1003; + KICKED = 1004; + GAME_STATE_CHANGED = 1005; + PLAYER_PROPERTIES_CHANGED = 1007; + GAME_SAY = 1009; + CREATE_ARROW = 2000; + DELETE_ARROW = 2001; + CREATE_COUNTER = 2002; + SET_COUNTER = 2003; + DEL_COUNTER = 2004; + DRAW_CARDS = 2005; + REVEAL_CARDS = 2006; + SHUFFLE = 2007; + ROLL_DIE = 2008; + MOVE_CARD = 2009; + FLIP_CARD = 2010; + DESTROY_CARD = 2011; + ATTACH_CARD = 2012; + CREATE_TOKEN = 2013; + SET_CARD_ATTR = 2014; + SET_CARD_COUNTER = 2015; + SET_ACTIVE_PLAYER = 2016; + SET_ACTIVE_PHASE = 2017; + DUMP_ZONE = 2018; + STOP_DUMP_ZONE = 2019; + CHANGE_ZONE_PROPERTIES = 2020; + } + optional sint32 player_id = 1 [default = -1]; + extensions 100 to max; } diff --git a/common/pb/game_event_container.proto b/common/pb/game_event_container.proto index 69b92d23..fd4ca430 100644 --- a/common/pb/game_event_container.proto +++ b/common/pb/game_event_container.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "game_event.proto"; import "game_event_context.proto"; message GameEventContainer { - optional uint32 game_id = 1; - repeated GameEvent event_list = 2; - optional GameEventContext context = 3; - optional uint32 seconds_elapsed = 4; + optional uint32 game_id = 1; + repeated GameEvent event_list = 2; + optional GameEventContext context = 3; + optional uint32 seconds_elapsed = 4; } diff --git a/common/pb/game_event_context.proto b/common/pb/game_event_context.proto index 6abd35db..98d5047b 100644 --- a/common/pb/game_event_context.proto +++ b/common/pb/game_event_context.proto @@ -1,14 +1,15 @@ +syntax = "proto2"; message GameEventContext { - enum ContextType { - READY_START = 1000; - CONCEDE = 1001; - DECK_SELECT = 1002; - UNDO_DRAW = 1003; - MOVE_CARD = 1004; - MULLIGAN = 1005; - PING_CHANGED = 1006; - CONNECTION_STATE_CHANGED = 1007; - SET_SIDEBOARD_LOCK = 1008; - } - extensions 100 to max; + enum ContextType { + READY_START = 1000; + CONCEDE = 1001; + DECK_SELECT = 1002; + UNDO_DRAW = 1003; + MOVE_CARD = 1004; + MULLIGAN = 1005; + PING_CHANGED = 1006; + CONNECTION_STATE_CHANGED = 1007; + SET_SIDEBOARD_LOCK = 1008; + } + extensions 100 to max; } diff --git a/common/pb/game_replay.proto b/common/pb/game_replay.proto index 05bc67d0..e8a0aa0b 100644 --- a/common/pb/game_replay.proto +++ b/common/pb/game_replay.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "serverinfo_game.proto"; import "game_event_container.proto"; message GameReplay { - optional uint64 replay_id = 1; - optional ServerInfo_Game game_info = 2; - repeated GameEventContainer event_list = 3; - optional uint32 duration_seconds = 4; + optional uint64 replay_id = 1; + optional ServerInfo_Game game_info = 2; + repeated GameEventContainer event_list = 3; + optional uint32 duration_seconds = 4; } diff --git a/common/pb/isl_message.proto b/common/pb/isl_message.proto index 20dec29d..12520533 100644 --- a/common/pb/isl_message.proto +++ b/common/pb/isl_message.proto @@ -1,3 +1,4 @@ +syntax = "proto2"; import "response.proto"; import "session_event.proto"; import "commands.proto"; @@ -5,25 +6,25 @@ import "game_event_container.proto"; import "room_event.proto"; message IslMessage { - enum MessageType { - GAME_COMMAND_CONTAINER = 0; - ROOM_COMMAND_CONTAINER = 1; - - RESPONSE = 10; - SESSION_EVENT = 11; - GAME_EVENT_CONTAINER = 12; - ROOM_EVENT = 13; - } - optional MessageType message_type = 1; - - optional uint64 session_id = 9; - optional sint32 player_id = 10 [default = -1]; - - optional CommandContainer game_command = 100; - optional CommandContainer room_command = 101; + enum MessageType { + GAME_COMMAND_CONTAINER = 0; + ROOM_COMMAND_CONTAINER = 1; + + RESPONSE = 10; + SESSION_EVENT = 11; + GAME_EVENT_CONTAINER = 12; + ROOM_EVENT = 13; + } + optional MessageType message_type = 1; + + optional uint64 session_id = 9; + optional sint32 player_id = 10 [default = -1]; + + optional CommandContainer game_command = 100; + optional CommandContainer room_command = 101; - optional Response response = 200; - optional SessionEvent session_event = 201; - optional GameEventContainer game_event_container = 202; - optional RoomEvent room_event = 203; + optional Response response = 200; + optional SessionEvent session_event = 201; + optional GameEventContainer game_event_container = 202; + optional RoomEvent room_event = 203; } diff --git a/common/pb/moderator_commands.proto b/common/pb/moderator_commands.proto index 18324ef1..89672776 100644 --- a/common/pb/moderator_commands.proto +++ b/common/pb/moderator_commands.proto @@ -1,17 +1,19 @@ +syntax = "proto2"; message ModeratorCommand { - enum ModeratorCommandType { - BAN_FROM_SERVER = 1000; - } - extensions 100 to max; + enum ModeratorCommandType { + BAN_FROM_SERVER = 1000; + } + extensions 100 to max; } message Command_BanFromServer { - extend ModeratorCommand { - optional Command_BanFromServer ext = 1000; - } - optional string user_name = 1; - optional string address = 2; - optional uint32 minutes = 3; - optional string reason = 4; - optional string visible_reason = 5; + extend ModeratorCommand { + optional Command_BanFromServer ext = 1000; + } + optional string user_name = 1; + optional string address = 2; + optional uint32 minutes = 3; + optional string reason = 4; + optional string visible_reason = 5; + optional string clientid = 6; } diff --git a/common/pb/move_card_to_zone.proto b/common/pb/move_card_to_zone.proto index edb03219..10ce87c3 100644 --- a/common/pb/move_card_to_zone.proto +++ b/common/pb/move_card_to_zone.proto @@ -1,5 +1,6 @@ +syntax = "proto2"; message MoveCard_ToZone { - optional string card_name = 1; - optional string start_zone = 2; - optional string target_zone = 3; + optional string card_name = 1; + optional string start_zone = 2; + optional string target_zone = 3; } diff --git a/common/pb/response.proto b/common/pb/response.proto index 4955bed5..ba3ccc6a 100644 --- a/common/pb/response.proto +++ b/common/pb/response.proto @@ -1,58 +1,61 @@ +syntax = "proto2"; message Response { - enum ResponseCode { - RespNotConnected = -1; - RespNothing = 0; - RespOk = 1; - RespNotInRoom = 2; - RespInternalError = 3; - RespInvalidCommand = 4; - RespInvalidData = 5; - RespNameNotFound = 6; - RespLoginNeeded = 7; - RespFunctionNotAllowed = 8; - RespGameNotStarted = 9; - RespGameFull = 10; - RespContextError = 11; - RespWrongPassword = 12; - RespSpectatorsNotAllowed = 13; - RespOnlyBuddies = 14; - RespUserLevelTooLow = 15; - RespInIgnoreList = 16; - RespWouldOverwriteOldSession = 17; - RespChatFlood = 18; - RespUserIsBanned = 19; - RespAccessDenied = 20; - RespUsernameInvalid = 21; - RespRegistrationRequired = 22; - RespRegistrationAccepted = 23; // Server agrees to process client's registration request - RespUserAlreadyExists = 24; // Client attempted to register a name which is already registered - RespEmailRequiredToRegister = 25; // Server requires email to register accounts but client did not provide one - RespTooManyRequests = 26; // Server refused to complete command because client has sent too many too quickly - RespPasswordTooShort = 27; // Server requires a decent password - RespAccountNotActivated = 28; // Client attempted to log into a registered username but the account hasn't been activated - RespRegistrationDisabled = 29; // Server does not allow clients to register - RespRegistrationFailed = 30; // Server accepted a reg request but failed to perform the registration - RespActivationAccepted = 31; // Server accepted a reg user activation token - RespActivationFailed = 32; // Server didn't accept a reg user activation token - RespRegistrationAcceptedNeedsActivation = 33; // Server accepted cient registration, but it will need token activation - } - enum ResponseType { - JOIN_ROOM = 1000; - LIST_USERS = 1001; - GET_GAMES_OF_USER = 1002; - GET_USER_INFO = 1003; - DUMP_ZONE = 1004; - LOGIN = 1005; - DECK_LIST = 1006; - DECK_DOWNLOAD = 1007; - DECK_UPLOAD = 1008; - REGISTER = 1009; - ACTIVATE = 1010; - REPLAY_LIST = 1100; - REPLAY_DOWNLOAD = 1101; - } - required uint64 cmd_id = 1; - optional ResponseCode response_code = 2; - - extensions 100 to max; + enum ResponseCode { + RespNotConnected = -1; + RespNothing = 0; + RespOk = 1; + RespNotInRoom = 2; + RespInternalError = 3; + RespInvalidCommand = 4; + RespInvalidData = 5; + RespNameNotFound = 6; + RespLoginNeeded = 7; + RespFunctionNotAllowed = 8; + RespGameNotStarted = 9; + RespGameFull = 10; + RespContextError = 11; + RespWrongPassword = 12; + RespSpectatorsNotAllowed = 13; + RespOnlyBuddies = 14; + RespUserLevelTooLow = 15; + RespInIgnoreList = 16; + RespWouldOverwriteOldSession = 17; + RespChatFlood = 18; + RespUserIsBanned = 19; + RespAccessDenied = 20; + RespUsernameInvalid = 21; + RespRegistrationRequired = 22; + RespRegistrationAccepted = 23; // Server agrees to process client's registration request + RespUserAlreadyExists = 24; // Client attempted to register a name which is already registered + RespEmailRequiredToRegister = 25; // Server requires email to register accounts but client did not provide one + RespTooManyRequests = 26; // Server refused to complete command because client has sent too many too quickly + RespPasswordTooShort = 27; // Server requires a decent password + RespAccountNotActivated = 28; // Client attempted to log into a registered username but the account hasn't been activated + RespRegistrationDisabled = 29; // Server does not allow clients to register + RespRegistrationFailed = 30; // Server accepted a reg request but failed to perform the registration + RespActivationAccepted = 31; // Server accepted a reg user activation token + RespActivationFailed = 32; // Server didn't accept a reg user activation token + RespRegistrationAcceptedNeedsActivation = 33; // Server accepted cient registration, but it will need token activation + RespClientIdRequired = 34; // Server requires client to generate and send its client id before allowing access + } + enum ResponseType { + JOIN_ROOM = 1000; + LIST_USERS = 1001; + GET_GAMES_OF_USER = 1002; + GET_USER_INFO = 1003; + DUMP_ZONE = 1004; + LOGIN = 1005; + DECK_LIST = 1006; + DECK_DOWNLOAD = 1007; + DECK_UPLOAD = 1008; + REGISTER = 1009; + ACTIVATE = 1010; + ADJUST_MOD = 1011; + REPLAY_LIST = 1100; + REPLAY_DOWNLOAD = 1101; + } + required uint64 cmd_id = 1; + optional ResponseCode response_code = 2; + + extensions 100 to max; } diff --git a/common/pb/response_activate.proto b/common/pb/response_activate.proto index 25667534..db4ddbc6 100644 --- a/common/pb/response_activate.proto +++ b/common/pb/response_activate.proto @@ -1,7 +1,8 @@ +syntax = "proto2"; import "response.proto"; message Response_Activate { - extend Response { - optional Response_Activate ext = 1010; - } + extend Response { + optional Response_Activate ext = 1010; + } } \ No newline at end of file diff --git a/common/pb/response_adjust_mod.proto b/common/pb/response_adjust_mod.proto new file mode 100644 index 00000000..c91493f8 --- /dev/null +++ b/common/pb/response_adjust_mod.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; +import "response.proto"; + +message Response_AdjustMod{ + extend Response { + optional Response_AdjustMod ext = 1011; + } +} diff --git a/common/pb/response_deck_download.proto b/common/pb/response_deck_download.proto index 804ba47f..f1839a9e 100644 --- a/common/pb/response_deck_download.proto +++ b/common/pb/response_deck_download.proto @@ -1,8 +1,9 @@ +syntax = "proto2"; import "response.proto"; message Response_DeckDownload { - extend Response { - optional Response_DeckDownload ext = 1007; - } - optional string deck = 1; + extend Response { + optional Response_DeckDownload ext = 1007; + } + optional string deck = 1; } diff --git a/common/pb/response_deck_list.proto b/common/pb/response_deck_list.proto index 37aa8276..bc516a25 100644 --- a/common/pb/response_deck_list.proto +++ b/common/pb/response_deck_list.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_deckstorage.proto"; message Response_DeckList { - extend Response { - optional Response_DeckList ext = 1006; - } - optional ServerInfo_DeckStorage_Folder root = 1; + extend Response { + optional Response_DeckList ext = 1006; + } + optional ServerInfo_DeckStorage_Folder root = 1; } diff --git a/common/pb/response_deck_upload.proto b/common/pb/response_deck_upload.proto index eb978fe8..3648d877 100644 --- a/common/pb/response_deck_upload.proto +++ b/common/pb/response_deck_upload.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_deckstorage.proto"; message Response_DeckUpload { - extend Response { - optional Response_DeckUpload ext = 1008; - } - optional ServerInfo_DeckStorage_TreeItem new_file = 1; + extend Response { + optional Response_DeckUpload ext = 1008; + } + optional ServerInfo_DeckStorage_TreeItem new_file = 1; } diff --git a/common/pb/response_dump_zone.proto b/common/pb/response_dump_zone.proto index 8df62ea7..e37288df 100644 --- a/common/pb/response_dump_zone.proto +++ b/common/pb/response_dump_zone.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_zone.proto"; message Response_DumpZone { - extend Response { - optional Response_DumpZone ext = 1004; - } - optional ServerInfo_Zone zone_info = 1; + extend Response { + optional Response_DumpZone ext = 1004; + } + optional ServerInfo_Zone zone_info = 1; } diff --git a/common/pb/response_get_games_of_user.proto b/common/pb/response_get_games_of_user.proto index 1c64377f..f179bcc6 100644 --- a/common/pb/response_get_games_of_user.proto +++ b/common/pb/response_get_games_of_user.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_game.proto"; import "serverinfo_room.proto"; message Response_GetGamesOfUser { - extend Response { - optional Response_GetGamesOfUser ext = 1002; - } - repeated ServerInfo_Room room_list = 1; - repeated ServerInfo_Game game_list = 2; + extend Response { + optional Response_GetGamesOfUser ext = 1002; + } + repeated ServerInfo_Room room_list = 1; + repeated ServerInfo_Game game_list = 2; } diff --git a/common/pb/response_get_user_info.proto b/common/pb/response_get_user_info.proto index b1c3b90c..fbfaaf21 100644 --- a/common/pb/response_get_user_info.proto +++ b/common/pb/response_get_user_info.proto @@ -1,11 +1,12 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_user.proto"; message Response_GetUserInfo { - extend Response { - optional Response_GetUserInfo ext = 1003; - } - optional ServerInfo_User user_info = 1; + extend Response { + optional Response_GetUserInfo ext = 1003; + } + optional ServerInfo_User user_info = 1; } diff --git a/common/pb/response_join_room.proto b/common/pb/response_join_room.proto index c9f9fcd6..7023c648 100644 --- a/common/pb/response_join_room.proto +++ b/common/pb/response_join_room.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_room.proto"; message Response_JoinRoom { - extend Response { - optional Response_JoinRoom ext = 1000; - } - optional ServerInfo_Room room_info = 1; + extend Response { + optional Response_JoinRoom ext = 1000; + } + optional ServerInfo_Room room_info = 1; } diff --git a/common/pb/response_list_users.proto b/common/pb/response_list_users.proto index c9fe9847..825ae6f7 100644 --- a/common/pb/response_list_users.proto +++ b/common/pb/response_list_users.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_user.proto"; message Response_ListUsers { - extend Response { - optional Response_ListUsers ext = 1001; - } - repeated ServerInfo_User user_list = 1; + extend Response { + optional Response_ListUsers ext = 1001; + } + repeated ServerInfo_User user_list = 1; } diff --git a/common/pb/response_login.proto b/common/pb/response_login.proto index a554ca09..fecd9d83 100644 --- a/common/pb/response_login.proto +++ b/common/pb/response_login.proto @@ -1,13 +1,14 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_user.proto"; message Response_Login { - extend Response { - optional Response_Login ext = 1005; - } - optional ServerInfo_User user_info = 1; - repeated ServerInfo_User buddy_list = 2; - repeated ServerInfo_User ignore_list = 3; - optional string denied_reason_str = 4; - optional uint64 denied_end_time = 5; + extend Response { + optional Response_Login ext = 1005; + } + optional ServerInfo_User user_info = 1; + repeated ServerInfo_User buddy_list = 2; + repeated ServerInfo_User ignore_list = 3; + optional string denied_reason_str = 4; + optional uint64 denied_end_time = 5; } diff --git a/common/pb/response_register.proto b/common/pb/response_register.proto index 9c6998ef..b5360f14 100644 --- a/common/pb/response_register.proto +++ b/common/pb/response_register.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; message Response_Register { - extend Response { - optional Response_Register ext = 1009; - } - optional string denied_reason_str = 1; - optional uint64 denied_end_time = 2; + extend Response { + optional Response_Register ext = 1009; + } + optional string denied_reason_str = 1; + optional uint64 denied_end_time = 2; } \ No newline at end of file diff --git a/common/pb/response_replay_download.proto b/common/pb/response_replay_download.proto index a3df3d65..d263f8d9 100644 --- a/common/pb/response_replay_download.proto +++ b/common/pb/response_replay_download.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; message Response_ReplayDownload { - extend Response { - optional Response_ReplayDownload ext = 1101; - } - optional bytes replay_data = 1; + extend Response { + optional Response_ReplayDownload ext = 1101; + } + optional bytes replay_data = 1; } diff --git a/common/pb/response_replay_list.proto b/common/pb/response_replay_list.proto index 93ffb8bb..68e7d41b 100644 --- a/common/pb/response_replay_list.proto +++ b/common/pb/response_replay_list.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "response.proto"; import "serverinfo_replay_match.proto"; message Response_ReplayList { - extend Response { - optional Response_ReplayList ext = 1100; - } - repeated ServerInfo_ReplayMatch match_list = 1; + extend Response { + optional Response_ReplayList ext = 1100; + } + repeated ServerInfo_ReplayMatch match_list = 1; } diff --git a/common/pb/room_commands.proto b/common/pb/room_commands.proto index e7411674..d6872810 100644 --- a/common/pb/room_commands.proto +++ b/common/pb/room_commands.proto @@ -1,48 +1,49 @@ +syntax = "proto2"; message RoomCommand { - enum RoomCommandType { - LEAVE_ROOM = 1000; - ROOM_SAY = 1001; - CREATE_GAME = 1002; - JOIN_GAME = 1003; - } - extensions 100 to max; + enum RoomCommandType { + LEAVE_ROOM = 1000; + ROOM_SAY = 1001; + CREATE_GAME = 1002; + JOIN_GAME = 1003; + } + extensions 100 to max; } message Command_LeaveRoom { - extend RoomCommand { - optional Command_LeaveRoom ext = 1000; - } + extend RoomCommand { + optional Command_LeaveRoom ext = 1000; + } } message Command_RoomSay { - extend RoomCommand { - optional Command_RoomSay ext = 1001; - } - optional string message = 1; + extend RoomCommand { + optional Command_RoomSay ext = 1001; + } + optional string message = 1; } message Command_CreateGame { - extend RoomCommand { - optional Command_CreateGame ext = 1002; - } - optional string description = 1; - optional string password = 2; - optional uint32 max_players = 3; - optional bool only_buddies = 4; - optional bool only_registered = 5; - optional bool spectators_allowed = 6; - optional bool spectators_need_password = 7; - optional bool spectators_can_talk = 8; - optional bool spectators_see_everything = 9; - repeated uint32 game_type_ids = 10; + extend RoomCommand { + optional Command_CreateGame ext = 1002; + } + optional string description = 1; + optional string password = 2; + optional uint32 max_players = 3; + optional bool only_buddies = 4; + optional bool only_registered = 5; + optional bool spectators_allowed = 6; + optional bool spectators_need_password = 7; + optional bool spectators_can_talk = 8; + optional bool spectators_see_everything = 9; + repeated uint32 game_type_ids = 10; } message Command_JoinGame { - extend RoomCommand { - optional Command_JoinGame ext = 1003; - } - optional sint32 game_id = 1 [default = -1]; - optional string password = 2; - optional bool spectator = 3; - optional bool override_restrictions = 4; + extend RoomCommand { + optional Command_JoinGame ext = 1003; + } + optional sint32 game_id = 1 [default = -1]; + optional string password = 2; + optional bool spectator = 3; + optional bool override_restrictions = 4; } diff --git a/common/pb/room_event.proto b/common/pb/room_event.proto index 9a34f4c4..b0cb3702 100644 --- a/common/pb/room_event.proto +++ b/common/pb/room_event.proto @@ -1,10 +1,11 @@ +syntax = "proto2"; message RoomEvent { - enum RoomEventType { - LEAVE_ROOM = 1000; - JOIN_ROOM = 1001; - ROOM_SAY = 1002; - LIST_GAMES = 1003; - } - optional sint32 room_id = 1; - extensions 100 to max; + enum RoomEventType { + LEAVE_ROOM = 1000; + JOIN_ROOM = 1001; + ROOM_SAY = 1002; + LIST_GAMES = 1003; + } + optional sint32 room_id = 1; + extensions 100 to max; } diff --git a/common/pb/server_message.proto b/common/pb/server_message.proto index b1905598..a9330ab3 100644 --- a/common/pb/server_message.proto +++ b/common/pb/server_message.proto @@ -1,19 +1,20 @@ +syntax = "proto2"; import "response.proto"; import "session_event.proto"; import "game_event_container.proto"; import "room_event.proto"; message ServerMessage { - enum MessageType { - RESPONSE = 0; - SESSION_EVENT = 1; - GAME_EVENT_CONTAINER = 2; - ROOM_EVENT = 3; - } - optional MessageType message_type = 1; - - optional Response response = 2; - optional SessionEvent session_event = 3; - optional GameEventContainer game_event_container = 4; - optional RoomEvent room_event = 5; + enum MessageType { + RESPONSE = 0; + SESSION_EVENT = 1; + GAME_EVENT_CONTAINER = 2; + ROOM_EVENT = 3; + } + optional MessageType message_type = 1; + + optional Response response = 2; + optional SessionEvent session_event = 3; + optional GameEventContainer game_event_container = 4; + optional RoomEvent room_event = 5; } diff --git a/common/pb/serverinfo_arrow.proto b/common/pb/serverinfo_arrow.proto index 63565913..cd99bd79 100644 --- a/common/pb/serverinfo_arrow.proto +++ b/common/pb/serverinfo_arrow.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "color.proto"; message ServerInfo_Arrow { - optional sint32 id = 1; - optional sint32 start_player_id = 2; - optional string start_zone = 3; - optional sint32 start_card_id = 4; - optional sint32 target_player_id = 5; - optional string target_zone = 6; - optional sint32 target_card_id = 7; - optional color arrow_color = 8; + optional sint32 id = 1; + optional sint32 start_player_id = 2; + optional string start_zone = 3; + optional sint32 start_card_id = 4; + optional sint32 target_player_id = 5; + optional string target_zone = 6; + optional sint32 target_card_id = 7; + optional color arrow_color = 8; } diff --git a/common/pb/serverinfo_card.proto b/common/pb/serverinfo_card.proto index b628a271..4d9a585c 100644 --- a/common/pb/serverinfo_card.proto +++ b/common/pb/serverinfo_card.proto @@ -1,20 +1,21 @@ +syntax = "proto2"; import "serverinfo_cardcounter.proto"; message ServerInfo_Card { - optional sint32 id = 1 [default = -1]; - optional string name = 2; - optional sint32 x = 3 [default = -1]; - optional sint32 y = 4 [default = -1]; - optional bool face_down = 5; - optional bool tapped = 6; - optional bool attacking = 7; - optional string color = 8; - optional string pt = 9; - optional string annotation = 10; - optional bool destroy_on_zone_change = 11; - optional bool doesnt_untap = 12; - repeated ServerInfo_CardCounter counter_list = 13; - optional sint32 attach_player_id = 14 [default = -1]; - optional string attach_zone = 15; - optional sint32 attach_card_id = 16 [default = -1]; + optional sint32 id = 1 [default = -1]; + optional string name = 2; + optional sint32 x = 3 [default = -1]; + optional sint32 y = 4 [default = -1]; + optional bool face_down = 5; + optional bool tapped = 6; + optional bool attacking = 7; + optional string color = 8; + optional string pt = 9; + optional string annotation = 10; + optional bool destroy_on_zone_change = 11; + optional bool doesnt_untap = 12; + repeated ServerInfo_CardCounter counter_list = 13; + optional sint32 attach_player_id = 14 [default = -1]; + optional string attach_zone = 15; + optional sint32 attach_card_id = 16 [default = -1]; } diff --git a/common/pb/serverinfo_cardcounter.proto b/common/pb/serverinfo_cardcounter.proto index d3c6a117..d1799a65 100644 --- a/common/pb/serverinfo_cardcounter.proto +++ b/common/pb/serverinfo_cardcounter.proto @@ -1,4 +1,5 @@ +syntax = "proto2"; message ServerInfo_CardCounter { - optional sint32 id = 1; - optional sint32 value = 2; + optional sint32 id = 1; + optional sint32 value = 2; } diff --git a/common/pb/serverinfo_counter.proto b/common/pb/serverinfo_counter.proto index eaf08200..849e3b4e 100644 --- a/common/pb/serverinfo_counter.proto +++ b/common/pb/serverinfo_counter.proto @@ -1,9 +1,10 @@ +syntax = "proto2"; import "color.proto"; message ServerInfo_Counter { - optional sint32 id = 1; - optional string name = 2; - optional color counter_color = 3; - optional sint32 radius = 4; - optional sint32 count = 5; + optional sint32 id = 1; + optional string name = 2; + optional color counter_color = 3; + optional sint32 radius = 4; + optional sint32 count = 5; } diff --git a/common/pb/serverinfo_deckstorage.proto b/common/pb/serverinfo_deckstorage.proto index 531a613d..16e3f28e 100644 --- a/common/pb/serverinfo_deckstorage.proto +++ b/common/pb/serverinfo_deckstorage.proto @@ -1,14 +1,15 @@ +syntax = "proto2"; message ServerInfo_DeckStorage_File { - optional uint32 creation_time = 1; + optional uint32 creation_time = 1; } message ServerInfo_DeckStorage_Folder { - repeated ServerInfo_DeckStorage_TreeItem items = 1; + repeated ServerInfo_DeckStorage_TreeItem items = 1; } message ServerInfo_DeckStorage_TreeItem { - optional uint32 id = 1; - optional string name = 2; - optional ServerInfo_DeckStorage_File file = 10; - optional ServerInfo_DeckStorage_Folder folder = 11; + optional uint32 id = 1; + optional string name = 2; + optional ServerInfo_DeckStorage_File file = 10; + optional ServerInfo_DeckStorage_Folder folder = 11; } diff --git a/common/pb/serverinfo_game.proto b/common/pb/serverinfo_game.proto index 3a0e10d4..8ba29eee 100644 --- a/common/pb/serverinfo_game.proto +++ b/common/pb/serverinfo_game.proto @@ -1,23 +1,24 @@ +syntax = "proto2"; import "serverinfo_user.proto"; message ServerInfo_Game { - optional sint32 server_id = 1 [default = -1]; - optional sint32 room_id = 2 [default = -1]; - optional sint32 game_id = 3 [default = -1]; - optional string description = 4; - optional bool with_password = 5; - optional uint32 max_players = 6; - repeated sint32 game_types = 7; - optional ServerInfo_User creator_info = 8; - optional bool only_buddies = 9; - optional bool only_registered = 10; - optional bool spectators_allowed = 11; - optional bool spectators_need_password = 12; - optional bool spectators_can_chat = 13; - optional bool spectators_omniscient = 14; - optional uint32 player_count = 30; - optional uint32 spectators_count = 31; - optional bool started = 50; - optional uint32 start_time = 51; - optional bool closed = 52; + optional sint32 server_id = 1 [default = -1]; + optional sint32 room_id = 2 [default = -1]; + optional sint32 game_id = 3 [default = -1]; + optional string description = 4; + optional bool with_password = 5; + optional uint32 max_players = 6; + repeated sint32 game_types = 7; + optional ServerInfo_User creator_info = 8; + optional bool only_buddies = 9; + optional bool only_registered = 10; + optional bool spectators_allowed = 11; + optional bool spectators_need_password = 12; + optional bool spectators_can_chat = 13; + optional bool spectators_omniscient = 14; + optional uint32 player_count = 30; + optional uint32 spectators_count = 31; + optional bool started = 50; + optional uint32 start_time = 51; + optional bool closed = 52; } diff --git a/common/pb/serverinfo_gametype.proto b/common/pb/serverinfo_gametype.proto index 977b9abb..a135b89b 100644 --- a/common/pb/serverinfo_gametype.proto +++ b/common/pb/serverinfo_gametype.proto @@ -1,5 +1,6 @@ +syntax = "proto2"; message ServerInfo_GameType { - optional sint32 game_type_id = 1; - optional string description = 2; + optional sint32 game_type_id = 1; + optional string description = 2; }; diff --git a/common/pb/serverinfo_player.proto b/common/pb/serverinfo_player.proto index 21911f5f..69cd4498 100644 --- a/common/pb/serverinfo_player.proto +++ b/common/pb/serverinfo_player.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "serverinfo_zone.proto"; import "serverinfo_counter.proto"; import "serverinfo_arrow.proto"; import "serverinfo_playerproperties.proto"; message ServerInfo_Player { - optional ServerInfo_PlayerProperties properties = 1; - optional string deck_list = 2; - repeated ServerInfo_Zone zone_list = 3; - repeated ServerInfo_Counter counter_list = 4; - repeated ServerInfo_Arrow arrow_list = 5; + optional ServerInfo_PlayerProperties properties = 1; + optional string deck_list = 2; + repeated ServerInfo_Zone zone_list = 3; + repeated ServerInfo_Counter counter_list = 4; + repeated ServerInfo_Arrow arrow_list = 5; } diff --git a/common/pb/serverinfo_playerping.proto b/common/pb/serverinfo_playerping.proto index 78ef6393..cb27e0e9 100644 --- a/common/pb/serverinfo_playerping.proto +++ b/common/pb/serverinfo_playerping.proto @@ -1,4 +1,5 @@ +syntax = "proto2"; message ServerInfo_PlayerPing { - optional sint32 player_id = 1; - optional sint32 ping_time = 2; + optional sint32 player_id = 1; + optional sint32 ping_time = 2; } diff --git a/common/pb/serverinfo_playerproperties.proto b/common/pb/serverinfo_playerproperties.proto index 87ef5cd9..96f3644a 100644 --- a/common/pb/serverinfo_playerproperties.proto +++ b/common/pb/serverinfo_playerproperties.proto @@ -1,12 +1,13 @@ +syntax = "proto2"; import "serverinfo_user.proto"; message ServerInfo_PlayerProperties { - optional sint32 player_id = 1; - optional ServerInfo_User user_info = 2; - optional bool spectator = 3; - optional bool conceded = 4; - optional bool ready_start = 5; - optional string deck_hash = 6; - optional sint32 ping_seconds = 7; - optional bool sideboard_locked = 8; + optional sint32 player_id = 1; + optional ServerInfo_User user_info = 2; + optional bool spectator = 3; + optional bool conceded = 4; + optional bool ready_start = 5; + optional string deck_hash = 6; + optional sint32 ping_seconds = 7; + optional bool sideboard_locked = 8; } diff --git a/common/pb/serverinfo_replay.proto b/common/pb/serverinfo_replay.proto index 18f68317..4b15db18 100644 --- a/common/pb/serverinfo_replay.proto +++ b/common/pb/serverinfo_replay.proto @@ -1,5 +1,6 @@ +syntax = "proto2"; message ServerInfo_Replay { - optional sint32 replay_id = 1 [default = -1]; - optional string replay_name = 2; - optional uint32 duration = 3; + optional sint32 replay_id = 1 [default = -1]; + optional string replay_name = 2; + optional uint32 duration = 3; } diff --git a/common/pb/serverinfo_replay_match.proto b/common/pb/serverinfo_replay_match.proto index 64576ecc..05abeb94 100644 --- a/common/pb/serverinfo_replay_match.proto +++ b/common/pb/serverinfo_replay_match.proto @@ -1,13 +1,14 @@ +syntax = "proto2"; import "serverinfo_replay.proto"; message ServerInfo_ReplayMatch { - repeated ServerInfo_Replay replay_list = 1; - - optional sint32 game_id = 2 [default = -1]; - optional string room_name = 3; - optional uint32 time_started = 4; - optional uint32 length = 5; - optional string game_name = 6; - repeated string player_names = 7; - optional bool do_not_hide = 8; + repeated ServerInfo_Replay replay_list = 1; + + optional sint32 game_id = 2 [default = -1]; + optional string room_name = 3; + optional uint32 time_started = 4; + optional uint32 length = 5; + optional string game_name = 6; + repeated string player_names = 7; + optional bool do_not_hide = 8; } diff --git a/common/pb/serverinfo_room.proto b/common/pb/serverinfo_room.proto index 6455ad89..571bc6f7 100644 --- a/common/pb/serverinfo_room.proto +++ b/common/pb/serverinfo_room.proto @@ -1,15 +1,17 @@ +syntax = "proto2"; import "serverinfo_game.proto"; import "serverinfo_user.proto"; import "serverinfo_gametype.proto"; message ServerInfo_Room { - optional sint32 room_id = 1; - optional string name = 2; - optional string description = 3; - optional uint32 game_count = 4; - optional uint32 player_count = 5; - optional bool auto_join = 6; - repeated ServerInfo_Game game_list = 7; - repeated ServerInfo_User user_list = 8; - repeated ServerInfo_GameType gametype_list = 9; + optional sint32 room_id = 1; + optional string name = 2; + optional string description = 3; + optional uint32 game_count = 4; + optional uint32 player_count = 5; + optional bool auto_join = 6; + repeated ServerInfo_Game game_list = 7; + repeated ServerInfo_User user_list = 8; + repeated ServerInfo_GameType gametype_list = 9; + optional string permissionlevel = 10; } diff --git a/common/pb/serverinfo_user.proto b/common/pb/serverinfo_user.proto index e7176e00..f935bbfc 100644 --- a/common/pb/serverinfo_user.proto +++ b/common/pb/serverinfo_user.proto @@ -1,25 +1,28 @@ +syntax = "proto2"; message ServerInfo_User { - enum UserLevelFlag { - IsNothing = 0; - IsUser = 1; - IsRegistered = 2; - IsModerator = 4; - IsAdmin = 8; - }; - enum Gender { - GenderUnknown = -1; - Male = 0; - Female = 1; - }; - optional string name = 1; - optional uint32 user_level = 2; - optional string address = 3; - optional string real_name = 4; - optional Gender gender = 5 [default = GenderUnknown]; - optional string country = 6; - optional bytes avatar_bmp = 7; - optional sint32 id = 8 [default = -1]; - optional sint32 server_id = 9 [default = -1]; - optional uint64 session_id = 10; + enum UserLevelFlag { + IsNothing = 0; + IsUser = 1; + IsRegistered = 2; + IsModerator = 4; + IsAdmin = 8; + }; + enum Gender { + GenderUnknown = -1; + Male = 0; + Female = 1; + }; + optional string name = 1; + optional uint32 user_level = 2; + optional string address = 3; + optional string real_name = 4; + optional Gender gender = 5 [default = GenderUnknown]; + optional string country = 6; + optional bytes avatar_bmp = 7; + optional sint32 id = 8 [default = -1]; + optional sint32 server_id = 9 [default = -1]; + optional uint64 session_id = 10; optional uint64 accountage_secs = 11; + optional string email = 12; + optional string clientid = 13; } diff --git a/common/pb/serverinfo_zone.proto b/common/pb/serverinfo_zone.proto index aee914ad..d359613c 100644 --- a/common/pb/serverinfo_zone.proto +++ b/common/pb/serverinfo_zone.proto @@ -1,24 +1,25 @@ +syntax = "proto2"; import "serverinfo_card.proto"; message ServerInfo_Zone { - enum ZoneType { - // PrivateZone: Contents of the zone are always visible to the owner, - // but not to anyone else. - // PublicZone: Contents of the zone are always visible to anyone. - // HiddenZone: Contents of the zone are never visible to anyone. - // However, the owner of the zone can issue a dump_zone command, - // setting beingLookedAt to true. - // Cards in a zone with the type HiddenZone are referenced by their - // list index, whereas cards in any other zone are referenced by their ids. - - PrivateZone = 0; - PublicZone = 1; - HiddenZone = 2; - } - optional string name = 1; - optional ZoneType type = 2; - optional bool with_coords = 3; - optional sint32 card_count = 4; - repeated ServerInfo_Card card_list = 5; - optional bool always_reveal_top_card = 10; + enum ZoneType { + // PrivateZone: Contents of the zone are always visible to the owner, + // but not to anyone else. + // PublicZone: Contents of the zone are always visible to anyone. + // HiddenZone: Contents of the zone are never visible to anyone. + // However, the owner of the zone can issue a dump_zone command, + // setting beingLookedAt to true. + // Cards in a zone with the type HiddenZone are referenced by their + // list index, whereas cards in any other zone are referenced by their ids. + + PrivateZone = 0; + PublicZone = 1; + HiddenZone = 2; + } + optional string name = 1; + optional ZoneType type = 2; + optional bool with_coords = 3; + optional sint32 card_count = 4; + repeated ServerInfo_Card card_list = 5; + optional bool always_reveal_top_card = 10; } diff --git a/common/pb/session_commands.proto b/common/pb/session_commands.proto index 11e5ce6f..0d5b12bd 100644 --- a/common/pb/session_commands.proto +++ b/common/pb/session_commands.proto @@ -1,129 +1,161 @@ +syntax = "proto2"; import "serverinfo_user.proto"; message SessionCommand { - enum SessionCommandType { - PING = 1000; - LOGIN = 1001; - MESSAGE = 1002; - LIST_USERS = 1003; - GET_GAMES_OF_USER = 1004; - GET_USER_INFO = 1005; - ADD_TO_LIST = 1006; - REMOVE_FROM_LIST = 1007; - DECK_LIST = 1008; - DECK_NEW_DIR = 1009; - DECK_DEL_DIR = 1010; - DECK_DEL = 1011; - DECK_DOWNLOAD = 1012; - DECK_UPLOAD = 1013; - LIST_ROOMS = 1014; - JOIN_ROOM = 1015; - REGISTER = 1016; - ACTIVATE = 1017; - REPLAY_LIST = 1100; - REPLAY_DOWNLOAD = 1101; - REPLAY_MODIFY_MATCH = 1102; - REPLAY_DELETE_MATCH = 1103; - } - extensions 100 to max; + enum SessionCommandType { + PING = 1000; + LOGIN = 1001; + MESSAGE = 1002; + LIST_USERS = 1003; + GET_GAMES_OF_USER = 1004; + GET_USER_INFO = 1005; + ADD_TO_LIST = 1006; + REMOVE_FROM_LIST = 1007; + DECK_LIST = 1008; + DECK_NEW_DIR = 1009; + DECK_DEL_DIR = 1010; + DECK_DEL = 1011; + DECK_DOWNLOAD = 1012; + DECK_UPLOAD = 1013; + LIST_ROOMS = 1014; + JOIN_ROOM = 1015; + REGISTER = 1016; + ACTIVATE = 1017; + ACCOUNT_EDIT = 1018; + ACCOUNT_IMAGE = 1019; + ACCOUNT_PASSWORD = 1020; + REPLAY_LIST = 1100; + REPLAY_DOWNLOAD = 1101; + REPLAY_MODIFY_MATCH = 1102; + REPLAY_DELETE_MATCH = 1103; + } + extensions 100 to max; } message Command_Ping { - extend SessionCommand { - optional Command_Ping ext = 1000; - } + extend SessionCommand { + optional Command_Ping ext = 1000; + } } message Command_Login { - extend SessionCommand { - optional Command_Login ext = 1001; - } - optional string user_name = 1; - optional string password = 2; + extend SessionCommand { + optional Command_Login ext = 1001; + } + optional string user_name = 1; + optional string password = 2; + optional string clientid = 3; + optional string clientver = 4; } message Command_Message { - extend SessionCommand { - optional Command_Message ext = 1002; - } - optional string user_name = 1; - optional string message = 2; + extend SessionCommand { + optional Command_Message ext = 1002; + } + optional string user_name = 1; + optional string message = 2; } message Command_ListUsers { - extend SessionCommand { - optional Command_ListUsers ext = 1003; - } + extend SessionCommand { + optional Command_ListUsers ext = 1003; + } } message Command_GetGamesOfUser { - extend SessionCommand { - optional Command_GetGamesOfUser ext = 1004; - } - optional string user_name = 1; + extend SessionCommand { + optional Command_GetGamesOfUser ext = 1004; + } + optional string user_name = 1; } message Command_GetUserInfo { - extend SessionCommand { - optional Command_GetUserInfo ext = 1005; - } - optional string user_name = 1; + extend SessionCommand { + optional Command_GetUserInfo ext = 1005; + } + optional string user_name = 1; } message Command_AddToList { - extend SessionCommand { - optional Command_AddToList ext = 1006; - } - optional string list = 1; - optional string user_name = 2; + extend SessionCommand { + optional Command_AddToList ext = 1006; + } + optional string list = 1; + optional string user_name = 2; } message Command_RemoveFromList { - extend SessionCommand { - optional Command_RemoveFromList ext = 1007; - } - optional string list = 1; - optional string user_name = 2; + extend SessionCommand { + optional Command_RemoveFromList ext = 1007; + } + optional string list = 1; + optional string user_name = 2; } message Command_ListRooms { - extend SessionCommand { - optional Command_ListRooms ext = 1014; - } + extend SessionCommand { + optional Command_ListRooms ext = 1014; + } } message Command_JoinRoom { - extend SessionCommand { - optional Command_JoinRoom ext = 1015; - } - optional uint32 room_id = 1; + extend SessionCommand { + optional Command_JoinRoom ext = 1015; + } + optional uint32 room_id = 1; } // User wants to register a new account message Command_Register { - extend SessionCommand { - optional Command_Register ext = 1016; - } - // User name client wants to register - required string user_name = 1; - // Hashed password to be inserted into database - required string password = 2; - // Email address of the client for user validation - optional string email = 3; - // Gender of the user - optional ServerInfo_User.Gender gender = 4; - // Country code of the user. 2 letter ISO format - optional string country = 5; - optional string real_name = 6; + extend SessionCommand { + optional Command_Register ext = 1016; + } + // User name client wants to register + required string user_name = 1; + // Hashed password to be inserted into database + required string password = 2; + // Email address of the client for user validation + optional string email = 3; + // Gender of the user + optional ServerInfo_User.Gender gender = 4; + // Country code of the user. 2 letter ISO format + optional string country = 5; + optional string real_name = 6; + optional string clientid = 7; } // User wants to activate an account message Command_Activate { - extend SessionCommand { - optional Command_Activate ext = 1017; - } - // User name client wants to activate - required string user_name = 1; - // Activation token - required string token = 2; + extend SessionCommand { + optional Command_Activate ext = 1017; + } + // User name client wants to activate + required string user_name = 1; + // Activation token + required string token = 2; +} + +message Command_AccountEdit { + extend SessionCommand { + optional Command_AccountEdit ext = 1018; + } + optional string real_name = 1; + optional string email = 2; + optional ServerInfo_User.Gender gender = 3; + optional string country = 4; +} + +message Command_AccountImage { + extend SessionCommand { + optional Command_AccountImage ext = 1019; + } + optional bytes image = 1; +} + +message Command_AccountPassword { + extend SessionCommand { + optional Command_AccountPassword ext = 1020; + } + optional string old_password = 1; + optional string new_password = 2; } diff --git a/common/pb/session_event.proto b/common/pb/session_event.proto index a7b14d00..3cce332c 100644 --- a/common/pb/session_event.proto +++ b/common/pb/session_event.proto @@ -1,18 +1,20 @@ +syntax = "proto2"; message SessionEvent { - enum SessionEventType { - SERVER_IDENTIFICATION = 500; - SERVER_COMPLETE_LIST = 600; - SERVER_MESSAGE = 1000; - SERVER_SHUTDOWN = 1001; - CONNECTION_CLOSED = 1002; - USER_MESSAGE = 1003; - LIST_ROOMS = 1004; - ADD_TO_LIST = 1005; - REMOVE_FROM_LIST = 1006; - USER_JOINED = 1007; - USER_LEFT = 1008; - GAME_JOINED = 1009; - REPLAY_ADDED = 1100; - } - extensions 100 to max; + enum SessionEventType { + SERVER_IDENTIFICATION = 500; + SERVER_COMPLETE_LIST = 600; + SERVER_MESSAGE = 1000; + SERVER_SHUTDOWN = 1001; + CONNECTION_CLOSED = 1002; + USER_MESSAGE = 1003; + LIST_ROOMS = 1004; + ADD_TO_LIST = 1005; + REMOVE_FROM_LIST = 1006; + USER_JOINED = 1007; + USER_LEFT = 1008; + GAME_JOINED = 1009; + NOTIFY_USER = 1010; + REPLAY_ADDED = 1100; + } + extensions 100 to max; } diff --git a/common/server.cpp b/common/server.cpp index 3c2c9718..bb339011 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -30,6 +30,7 @@ #include "pb/event_user_left.pb.h" #include "pb/event_list_rooms.pb.h" #include "pb/session_event.pb.h" +#include "pb/event_connection_closed.pb.h" #include "pb/isl_message.pb.h" #include #include @@ -46,7 +47,7 @@ Server::Server(bool _threaded, QObject *parent) qRegisterMetaType("GameEventContainer"); qRegisterMetaType("IslMessage"); qRegisterMetaType("Command_JoinGame"); - + connect(this, SIGNAL(sigSendIslMessage(IslMessage, int)), this, SLOT(doSendIslMessage(IslMessage, int)), Qt::QueuedConnection); } @@ -62,9 +63,9 @@ void Server::prepareDestroy() for (int i = 0; i < clients.size(); ++i) QMetaObject::invokeMethod(clients.at(i), "prepareDestroy", Qt::QueuedConnection); clientsLock.unlock(); - + bool done = false; - + class SleeperThread : public QThread { public: @@ -83,7 +84,7 @@ void Server::prepareDestroy() while (!clients.isEmpty()) clients.first()->prepareDestroy(); } - + roomsLock.lockForWrite(); QMapIterator roomIterator(rooms); while (roomIterator.hasNext()) @@ -103,36 +104,42 @@ Server_DatabaseInterface *Server::getDatabaseInterface() const return databaseInterfaces.value(QThread::currentThread()); } -AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reasonStr, int &secondsLeft) +AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reasonStr, int &secondsLeft, QString &clientid) { if (name.size() > 35) name = name.left(35); - + Server_DatabaseInterface *databaseInterface = getDatabaseInterface(); - - QWriteLocker locker(&clientsLock); - - AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, reasonStr, secondsLeft); + + AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, clientid, reasonStr, secondsLeft); if (authState == NotLoggedIn || authState == UserIsBanned || authState == UsernameInvalid || authState == UserIsInactive) return authState; - + ServerInfo_User data = databaseInterface->getUserData(name, true); data.set_address(session->getAddress().toStdString()); name = QString::fromStdString(data.name()); // Compensate for case indifference - - databaseInterface->lockSessionTables(); - + if (authState == PasswordRight) { if (users.contains(name) || databaseInterface->userSessionExists(name)) { - qDebug("Login denied: would overwrite old session"); - databaseInterface->unlockSessionTables(); - return WouldOverwriteOldSession; + if (users.contains(name)) { + qDebug("Session already logged in, logging old session out"); + Event_ConnectionClosed event; + event.set_reason(Event_ConnectionClosed::LOGGEDINELSEWERE); + event.set_reason_str("You have been logged out due to logging in at another location."); + event.set_end_time(QDateTime::currentDateTime().toTime_t()); + + SessionEvent *se = users.value(name)->prepareSessionEvent(event); + users.value(name)->sendProtocolItem(*se); + delete se; + + users.value(name)->prepareDestroy(); + } } + } else if (authState == UnknownUser) { // Change user name so that no two users have the same names, // don't interfere with registered user names though. - bool requireReg = databaseInterface->getRequireRegistration(); - if (requireReg) { + if (getRegOnlyServer()) { qDebug("Login denied: registration required"); databaseInterface->unlockSessionTables(); return RegistrationRequired; @@ -145,18 +152,20 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString name = tempName; data.set_name(name.toStdString()); } - + + QWriteLocker locker(&clientsLock); + databaseInterface->lockSessionTables(); users.insert(name, session); qDebug() << "Server::loginUser:" << session << "name=" << name; - - data.set_session_id(databaseInterface->startSession(name, session->getAddress())); + + data.set_session_id(databaseInterface->startSession(name, session->getAddress(), clientid)); databaseInterface->unlockSessionTables(); - + usersBySessionId.insert(data.session_id(), session); - + qDebug() << "session id:" << data.session_id(); session->setUserInfo(data); - + Event_UserJoined event; event.mutable_user_info()->CopyFrom(session->copyUserInfo(false)); SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); @@ -164,14 +173,25 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); delete se; - + event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true)); locker.unlock(); - + + if (clientid.isEmpty()){ + // client id is empty, either out dated client or client has been modified + if (getClientIdRequired()) + return ClientIdRequired; + } + else { + // update users database table with client id + databaseInterface->updateUsersClientID(name, clientid); + } + + databaseInterface->updateUsersLastLoginTime(name); se = Server_ProtocolHandler::prepareSessionEvent(event); sendIsl_SessionEvent(*se); delete se; - + return authState; } @@ -196,7 +216,7 @@ QList Server::getPersistentPlayerReferences(const QString &user Server_AbstractUserInterface *Server::findUser(const QString &userName) const { // Call this only with clientsLock set. - + Server_AbstractUserInterface *userHandler = users.value(userName); if (userHandler) return userHandler; @@ -224,10 +244,10 @@ void Server::removeClient(Server_ProtocolHandler *client) clients[i]->sendProtocolItem(*se); sendIsl_SessionEvent(*se); delete se; - + users.remove(QString::fromStdString(data->name())); qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name()); - + if (data->has_session_id()) { const qint64 sessionId = data->session_id(); usersBySessionId.remove(sessionId); @@ -242,21 +262,21 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo) { // This function is always called from the main thread via signal/slot. clientsLock.lockForWrite(); - + Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo)); externalUsers.insert(QString::fromStdString(userInfo.name()), newUser); externalUsersBySessionId.insert(userInfo.session_id(), newUser); - + Event_UserJoined event; event.mutable_user_info()->CopyFrom(userInfo); - + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); delete se; clientsLock.unlock(); - + ResponseContainer rc(-1); newUser->joinPersistentGames(rc); newUser->sendResponseContainer(rc, Response::RespNothing); @@ -265,12 +285,12 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo) void Server::externalUserLeft(const QString &userName) { // This function is always called from the main thread via signal/slot. - + clientsLock.lockForWrite(); Server_AbstractUserInterface *user = externalUsers.take(userName); externalUsersBySessionId.remove(user->getUserInfo()->session_id()); clientsLock.unlock(); - + QMap > userGames(user->getGames()); QMapIterator > userGamesIterator(userGames); roomsLock.lockForRead(); @@ -279,26 +299,26 @@ void Server::externalUserLeft(const QString &userName) Server_Room *room = rooms.value(userGamesIterator.value().first); if (!room) continue; - + QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(userGamesIterator.key()); if (!game) continue; - + QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(userGamesIterator.value().second); if (!player) continue; - + player->disconnectClient(); } roomsLock.unlock(); - + delete user; - + Event_UserLeft event; event.set_name(userName.toStdString()); - + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); clientsLock.lockForRead(); for (int i = 0; i < clients.size(); ++i) @@ -312,7 +332,7 @@ void Server::externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo) { // This function is always called from the main thread via signal/slot. QReadLocker locker(&roomsLock); - + Server_Room *room = rooms.value(roomId); if (!room) { qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found"; @@ -325,7 +345,7 @@ void Server::externalRoomUserLeft(int roomId, const QString &userName) { // This function is always called from the main thread via signal/slot. QReadLocker locker(&roomsLock); - + Server_Room *room = rooms.value(roomId); if (!room) { qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found"; @@ -338,7 +358,7 @@ void Server::externalRoomSay(int roomId, const QString &userName, const QString { // This function is always called from the main thread via signal/slot. QReadLocker locker(&roomsLock); - + Server_Room *room = rooms.value(roomId); if (!room) { qDebug() << "externalRoomSay: room id=" << roomId << "not found"; @@ -353,7 +373,7 @@ void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &game { // This function is always called from the main thread via signal/slot. QReadLocker locker(&roomsLock); - + Server_Room *room = rooms.value(roomId); if (!room) { qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found"; @@ -365,11 +385,11 @@ void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &game void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId) { // This function is always called from the main thread via signal/slot. - + try { QReadLocker roomsLocker(&roomsLock); QReadLocker clientsLocker(&clientsLock); - + Server_Room *room = rooms.value(roomId); if (!room) { qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found"; @@ -380,7 +400,7 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found"; throw Response::RespNotInRoom; } - + ResponseContainer responseContainer(cmdId); Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface); userInterface->sendResponseContainer(responseContainer, responseCode); @@ -388,7 +408,7 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm Response response; response.set_cmd_id(cmdId); response.set_response_code(code); - + sendIsl_Response(response, serverId, sessionId); } } @@ -396,44 +416,44 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm void Server::externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId) { // This function is always called from the main thread via signal/slot. - + try { ResponseContainer responseContainer(cont.cmd_id()); Response::ResponseCode finalResponseCode = Response::RespOk; - + QReadLocker roomsLocker(&roomsLock); Server_Room *room = rooms.value(cont.room_id()); if (!room) { qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found"; throw Response::RespNotInRoom; } - + QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(cont.game_id()); if (!game) { qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found"; throw Response::RespNotInRoom; } - + QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(playerId); if (!player) { qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found"; throw Response::RespNotInRoom; } - + GameEventStorage ges; for (int i = cont.game_command_size() - 1; i >= 0; --i) { const GameCommand &sc = cont.game_command(i); qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString()); - + Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges); - + if (resp != Response::RespOk) finalResponseCode = resp; } ges.sendToGame(game); - + if (finalResponseCode != Response::RespNothing) { player->playerMutex.lock(); player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode); @@ -443,7 +463,7 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont, Response response; response.set_cmd_id(cont.cmd_id()); response.set_response_code(code); - + sendIsl_Response(response, serverId, sessionId); } } @@ -451,9 +471,9 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont, void Server::externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId) { // This function is always called from the main thread via signal/slot. - + QReadLocker usersLocker(&clientsLock); - + Server_ProtocolHandler *client = usersBySessionId.value(sessionId); if (!client) { qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found"; @@ -465,9 +485,9 @@ void Server::externalGameEventContainerReceived(const GameEventContainer &cont, void Server::externalResponseReceived(const Response &resp, qint64 sessionId) { // This function is always called from the main thread via signal/slot. - + QReadLocker usersLocker(&clientsLock); - + Server_ProtocolHandler *client = usersBySessionId.value(sessionId); if (!client) { qDebug() << "externalResponseReceived: session" << sessionId << "not found"; @@ -479,10 +499,10 @@ void Server::externalResponseReceived(const Response &resp, qint64 sessionId) void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl) { // This function is always called from the main thread via signal/slot. - + Event_ListRooms event; event.add_room_list()->CopyFrom(roomInfo); - + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); clientsLock.lockForRead(); @@ -490,10 +510,10 @@ void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl if (clients[i]->getAcceptsRoomListChanges()) clients[i]->sendProtocolItem(*se); clientsLock.unlock(); - + if (sendToIsl) sendIsl_SessionEvent(*se); - + delete se; } @@ -531,7 +551,7 @@ void Server::sendIsl_Response(const Response &item, int serverId, qint64 session if (sessionId != -1) msg.set_session_id(sessionId); msg.mutable_response()->CopyFrom(item); - + emit sigSendIslMessage(msg, serverId); } @@ -542,7 +562,7 @@ void Server::sendIsl_SessionEvent(const SessionEvent &item, int serverId, qint64 if (sessionId != -1) msg.set_session_id(sessionId); msg.mutable_session_event()->CopyFrom(item); - + emit sigSendIslMessage(msg, serverId); } @@ -553,7 +573,7 @@ void Server::sendIsl_GameEventContainer(const GameEventContainer &item, int serv if (sessionId != -1) msg.set_session_id(sessionId); msg.mutable_game_event_container()->CopyFrom(item); - + emit sigSendIslMessage(msg, serverId); } @@ -564,7 +584,7 @@ void Server::sendIsl_RoomEvent(const RoomEvent &item, int serverId, qint64 sessi if (sessionId != -1) msg.set_session_id(sessionId); msg.mutable_room_event()->CopyFrom(item); - + emit sigSendIslMessage(msg, serverId); } @@ -574,11 +594,11 @@ void Server::sendIsl_GameCommand(const CommandContainer &item, int serverId, qin msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER); msg.set_session_id(sessionId); msg.set_player_id(playerId); - + CommandContainer *cont = msg.mutable_game_command(); cont->CopyFrom(item); cont->set_room_id(roomId); - + emit sigSendIslMessage(msg, serverId); } @@ -587,10 +607,10 @@ void Server::sendIsl_RoomCommand(const CommandContainer &item, int serverId, qin IslMessage msg; msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER); msg.set_session_id(sessionId); - + CommandContainer *cont = msg.mutable_room_command(); cont->CopyFrom(item); cont->set_room_id(roomId); - + emit sigSendIslMessage(msg, serverId); } diff --git a/common/server.h b/common/server.h index ad17bc53..494ec534 100644 --- a/common/server.h +++ b/common/server.h @@ -28,7 +28,7 @@ class GameEventContainer; class CommandContainer; class Command_JoinGame; -enum AuthenticationResult { NotLoggedIn, PasswordRight, UnknownUser, WouldOverwriteOldSession, UserIsBanned, UsernameInvalid, RegistrationRequired, UserIsInactive }; +enum AuthenticationResult { NotLoggedIn, PasswordRight, UnknownUser, WouldOverwriteOldSession, UserIsBanned, UsernameInvalid, RegistrationRequired, UserIsInactive, ClientIdRequired }; class Server : public QObject { @@ -44,18 +44,22 @@ public: Server(bool _threaded, QObject *parent = 0); ~Server(); void setThreaded(bool _threaded) { threaded = _threaded; } - AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft); + AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft, QString &clientid); const QMap &getRooms() { return rooms; } - + Server_AbstractUserInterface *findUser(const QString &userName) const; const QMap &getUsers() const { return users; } const QMap &getUsersBySessionId() const { return usersBySessionId; } void addClient(Server_ProtocolHandler *player); void removeClient(Server_ProtocolHandler *player); virtual QString getLoginMessage() const { return QString(); } - + + virtual bool permitUnregisteredUsers() const { return true; } virtual bool getGameShouldPing() const { return false; } + virtual bool getClientIdRequired() const { return false; } + virtual bool getRegOnlyServer() const { return false; } + virtual int getPingClockInterval() const { return 0; } virtual int getMaxGameInactivityTime() const { return 9999999; } virtual int getMaxPlayerInactivityTime() const { return 9999999; } virtual int getMessageCountingInterval() const { return 0; } @@ -69,18 +73,18 @@ public: Server_DatabaseInterface *getDatabaseInterface() const; int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; } - + void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1); void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1); void sendIsl_GameEventContainer(const GameEventContainer &item, int serverId = -1, qint64 sessionId = -1); void sendIsl_RoomEvent(const RoomEvent &item, int serverId = -1, qint64 sessionId = -1); void sendIsl_GameCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId, int playerId); void sendIsl_RoomCommand(const CommandContainer &item, int serverId, qint64 sessionId, int roomId); - + void addExternalUser(const ServerInfo_User &userInfo); void removeExternalUser(const QString &userName); const QMap &getExternalUsers() const { return externalUsers; } - + void addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); void removePersistentPlayer(const QString &userName, int roomId, int gameId, int playerId); QList getPersistentPlayerReferences(const QString &userName) const; @@ -90,7 +94,7 @@ private: mutable QReadWriteLock persistentPlayersLock; int nextLocalGameId; QMutex nextLocalGameIdMutex; -protected slots: +protected slots: void externalUserJoined(const ServerInfo_User &userInfo); void externalUserLeft(const QString &userName); void externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo); @@ -101,7 +105,7 @@ protected slots: void externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId); void externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); void externalResponseReceived(const Response &resp, qint64 sessionId); - + virtual void doSendIslMessage(const IslMessage & /* msg */, int /* serverId */) { } protected: void prepareDestroy(); @@ -113,7 +117,7 @@ protected: QMap externalUsers; QMap rooms; QMap databaseInterfaces; - + int getUsersCount() const; int getGamesCount() const; void addRoom(Server_Room *newRoom); diff --git a/common/server_card.cpp b/common/server_card.cpp index b4f9f005..13289e55 100644 --- a/common/server_card.cpp +++ b/common/server_card.cpp @@ -126,14 +126,17 @@ void Server_Card::getInfo(ServerInfo_Card *info) info->set_name(displayedName.toStdString()); info->set_x(coord_x); info->set_y(coord_y); + QString ptStr = getPT(); if (facedown) + { info->set_face_down(true); + ptStr = getPT(); + } info->set_tapped(tapped); if (attacking) info->set_attacking(true); if (!color.isEmpty()) info->set_color(color.toStdString()); - const QString ptStr = getPT(); if (!ptStr.isEmpty()) info->set_pt(ptStr.toStdString()); if (!annotation.isEmpty()) @@ -156,4 +159,4 @@ void Server_Card::getInfo(ServerInfo_Card *info) info->set_attach_zone(parentCard->getZone()->getName().toStdString()); info->set_attach_card_id(parentCard->getId()); } -} +} \ No newline at end of file diff --git a/common/server_database_interface.h b/common/server_database_interface.h index d809b1ff..224ae2c7 100644 --- a/common/server_database_interface.h +++ b/common/server_database_interface.h @@ -12,8 +12,8 @@ public: Server_DatabaseInterface(QObject *parent = 0) : QObject(parent) { } - virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0; - virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; } + virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft) = 0; + virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, const QString & /* clientId */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; } virtual bool activeUserExists(const QString & /* user */) { return false; } virtual bool userExists(const QString & /* user */) { return false; } virtual QMap getBuddyList(const QString & /* name */) { return QMap(); } @@ -24,13 +24,14 @@ public: virtual void storeGameInformation(const QString & /* roomName */, const QStringList & /* roomGameTypes */, const ServerInfo_Game & /* gameInfo */, const QSet & /* allPlayersEver */, const QSet & /* allSpectatorsEver */, const QList & /* replayList */) { } virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) { return 0; } - virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; } - virtual bool usernameIsValid(const QString & /*userName */) { return true; }; + virtual qint64 startSession(const QString & /* userName */, const QString & /* address */, const QString & /* clientId */) { return 0; } + virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */) { return true; }; public slots: virtual void endSession(qint64 /* sessionId */ ) { } public: virtual int getNextGameId() = 0; virtual int getNextReplayId() = 0; + virtual int getActiveUserCount() = 0; virtual void clearSessionTables() { } virtual void lockSessionTables() { } @@ -40,10 +41,14 @@ public: virtual bool getRequireRegistration() { return false; } virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; } virtual bool activateUser(const QString & /* userName */, const QString & /* token */) { return false; } + virtual void updateUsersClientID(const QString & /* userName */, const QString & /* userClientID */) { } + virtual void updateUsersLastLoginTime(const QString & /* userName */) { } enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom }; virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { }; bool checkUserIsBanned(Server_ProtocolHandler *session, QString &banReason, int &banSecondsRemaining); + virtual bool changeUserPassword(const QString & /* user */, const QString & /* oldPassword */, const QString & /* newPassword */) { return true; }; + virtual QChar getGenderChar(ServerInfo_User_Gender const & /* gender */) { return QChar('u'); }; }; #endif diff --git a/common/server_player.cpp b/common/server_player.cpp index 9f02acb0..d1413147 100644 --- a/common/server_player.cpp +++ b/common/server_player.cpp @@ -1065,7 +1065,7 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm return Response::RespOk; } -Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) +Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer & rc, GameEventStorage &ges) { if (spectator) return Response::RespFunctionNotAllowed; @@ -1109,8 +1109,20 @@ Response::ResponseCode Server_Player::cmdCreateToken(const Command_CreateToken & event.set_x(x); event.set_y(y); ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; + + // chck if the token is a replacement for an existing card + if(cmd.target_card_id() < 0) + return Response::RespOk; + + Command_AttachCard cmd2; + cmd2.set_start_zone(cmd.target_zone()); + cmd2.set_card_id(cmd.target_card_id()); + + cmd2.set_target_player_id(zone->getPlayer()->getPlayerId()); + cmd2.set_target_zone(cmd.zone()); + cmd2.set_target_card_id(card->getId()); + + return cmdAttachCard(cmd2, rc, ges); } Response::ResponseCode Server_Player::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 46e2bb83..c4ed0f51 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -45,18 +45,18 @@ void Server_ProtocolHandler::prepareDestroy() if (deleted) return; deleted = true; - + QMapIterator roomIterator(rooms); while (roomIterator.hasNext()) roomIterator.next().value()->removeClient(this); - + QMap > tempGames(getGames()); - + server->roomsLock.lockForRead(); QMapIterator > gameIterator(tempGames); while (gameIterator.hasNext()) { gameIterator.next(); - + Server_Room *r = server->getRooms().value(gameIterator.value().first); if (!r) continue; @@ -73,16 +73,16 @@ void Server_ProtocolHandler::prepareDestroy() r->gamesLock.unlock(); continue; } - + p->disconnectClient(); - + g->gameMutex.unlock(); r->gamesLock.unlock(); } server->roomsLock.unlock(); - + server->removeClient(this); - + deleteLater(); } @@ -91,7 +91,7 @@ void Server_ProtocolHandler::sendProtocolItem(const Response &item) ServerMessage msg; msg.mutable_response()->CopyFrom(item); msg.set_message_type(ServerMessage::RESPONSE); - + transmitProtocolItem(msg); } @@ -100,7 +100,7 @@ void Server_ProtocolHandler::sendProtocolItem(const SessionEvent &item) ServerMessage msg; msg.mutable_session_event()->CopyFrom(item); msg.set_message_type(ServerMessage::SESSION_EVENT); - + transmitProtocolItem(msg); } @@ -109,7 +109,7 @@ void Server_ProtocolHandler::sendProtocolItem(const GameEventContainer &item) ServerMessage msg; msg.mutable_game_event_container()->CopyFrom(item); msg.set_message_type(ServerMessage::GAME_EVENT_CONTAINER); - + transmitProtocolItem(msg); } @@ -118,7 +118,7 @@ void Server_ProtocolHandler::sendProtocolItem(const RoomEvent &item) ServerMessage msg; msg.mutable_room_event()->CopyFrom(item); msg.set_message_type(ServerMessage::ROOM_EVENT); - + transmitProtocolItem(msg); } @@ -167,7 +167,7 @@ Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const Server_Room *room = rooms.value(cont.room_id(), 0); if (!room) return Response::RespNotInRoom; - + Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.room_command_size() - 1; i >= 0; --i) { Response::ResponseCode resp = Response::RespInvalidCommand; @@ -206,17 +206,17 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + QMap > gameMap = getGames(); if (!gameMap.contains(cont.game_id())) return Response::RespNotInRoom; const QPair roomIdAndPlayerId = gameMap.value(cont.game_id()); - + QReadLocker roomsLocker(&server->roomsLock); Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first); if (!room) return Response::RespNotInRoom; - + QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(cont.game_id()); if (!game) { @@ -231,12 +231,12 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const } return Response::RespNotInRoom; } - + QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(roomIdAndPlayerId.second); if (!player) return Response::RespNotInRoom; - + int commandCountingInterval = server->getCommandCountingInterval(); int maxCommandCountPerInterval = server->getMaxCommandCountPerInterval(); GameEventStorage ges; @@ -255,7 +255,7 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const for (int i = 0; i < commandCountOverTime.size(); ++i) totalCount += commandCountOverTime[i]; - + if (totalCount > maxCommandCountPerInterval) return Response::RespChatFlood; } @@ -266,7 +266,7 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const finalResponseCode = resp; } ges.sendToGame(game); - + return finalResponseCode; } @@ -283,7 +283,7 @@ Response::ResponseCode Server_ProtocolHandler::processModeratorCommandContainer( const ModeratorCommand &sc = cont.moderator_command(i); const int num = getPbExtension(sc); logDebugMessage(QString::fromStdString(sc.ShortDebugString())); - + resp = processExtendedModeratorCommand(num, sc, rc); if (resp != Response::RespOk) finalResponseCode = resp; @@ -304,7 +304,7 @@ Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(cons const AdminCommand &sc = cont.admin_command(i); const int num = getPbExtension(sc); logDebugMessage(QString::fromStdString(sc.ShortDebugString())); - + resp = processExtendedAdminCommand(num, sc, rc); if (resp != Response::RespOk) finalResponseCode = resp; @@ -317,12 +317,12 @@ void Server_ProtocolHandler::processCommandContainer(const CommandContainer &con // Command processing must be disabled after prepareDestroy() has been called. if (deleted) return; - + lastDataReceived = timeRunning; - + ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1); Response::ResponseCode finalResponseCode; - + if (cont.game_command_size()) finalResponseCode = processGameCommandContainer(cont, responseContainer); else if (cont.room_command_size()) @@ -335,28 +335,37 @@ void Server_ProtocolHandler::processCommandContainer(const CommandContainer &con finalResponseCode = processAdminCommandContainer(cont, responseContainer); else finalResponseCode = Response::RespInvalidCommand; - + if ((finalResponseCode != Response::RespNothing)) sendResponseContainer(responseContainer, finalResponseCode); } void Server_ProtocolHandler::pingClockTimeout() { + + int cmdcountinterval = server->getCommandCountingInterval(); + int msgcountinterval = server->getMessageCountingInterval(); + int pingclockinterval = server->getPingClockInterval(); + int interval = server->getMessageCountingInterval(); if (interval > 0) { - messageSizeOverTime.prepend(0); - if (messageSizeOverTime.size() > server->getMessageCountingInterval()) - messageSizeOverTime.removeLast(); - messageCountOverTime.prepend(0); - if (messageCountOverTime.size() > server->getMessageCountingInterval()) - messageCountOverTime.removeLast(); + if(pingclockinterval > 0) { + messageSizeOverTime.prepend(0); + if (messageSizeOverTime.size() > (msgcountinterval / pingclockinterval)) + messageSizeOverTime.removeLast(); + messageCountOverTime.prepend(0); + if (messageCountOverTime.size() > (msgcountinterval / pingclockinterval)) + messageCountOverTime.removeLast(); + } } interval = server->getCommandCountingInterval(); if (interval > 0) { - commandCountOverTime.prepend(0); - if (commandCountOverTime.size() > server->getCommandCountingInterval()) - commandCountOverTime.removeLast(); + if (pingclockinterval > 0) { + commandCountOverTime.prepend(0); + if (commandCountOverTime.size() > (cmdcountinterval / pingclockinterval)) + commandCountOverTime.removeLast(); + } } if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime()) @@ -372,11 +381,14 @@ Response::ResponseCode Server_ProtocolHandler::cmdPing(const Command_Ping & /*cm Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd, ResponseContainer &rc) { QString userName = QString::fromStdString(cmd.user_name()).simplified(); + QString clientId = QString::fromStdString(cmd.clientid()).simplified(); + if (userName.isEmpty() || (userInfo != 0)) return Response::RespContextError; + QString reasonStr; int banSecondsLeft = 0; - AuthenticationResult res = server->loginUser(this, userName, QString::fromStdString(cmd.password()), reasonStr, banSecondsLeft); + AuthenticationResult res = server->loginUser(this, userName, QString::fromStdString(cmd.password()), reasonStr, banSecondsLeft, clientId); switch (res) { case UserIsBanned: { Response_Login *re = new Response_Login; @@ -388,32 +400,38 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd } case NotLoggedIn: return Response::RespWrongPassword; case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession; - case UsernameInvalid: return Response::RespUsernameInvalid; + case UsernameInvalid: { + Response_Login *re = new Response_Login; + re->set_denied_reason_str(reasonStr.toStdString()); + rc.setResponseExtension(re); + return Response::RespUsernameInvalid; + } case RegistrationRequired: return Response::RespRegistrationRequired; + case ClientIdRequired: return Response::RespClientIdRequired; case UserIsInactive: return Response::RespAccountNotActivated; default: authState = res; } - + userName = QString::fromStdString(userInfo->name()); Event_ServerMessage event; event.set_message(server->getLoginMessage().toStdString()); rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); - + Response_Login *re = new Response_Login; re->mutable_user_info()->CopyFrom(copyUserInfo(true)); - + if (authState == PasswordRight) { QMapIterator buddyIterator(databaseInterface->getBuddyList(userName)); while (buddyIterator.hasNext()) re->add_buddy_list()->CopyFrom(buddyIterator.next().value()); - + QMapIterator ignoreIterator(databaseInterface->getIgnoreList(userName)); while (ignoreIterator.hasNext()) re->add_ignore_list()->CopyFrom(ignoreIterator.next().value()); } - + joinPersistentGames(rc); - + rc.setResponseExtension(re); return Response::RespOk; } @@ -422,21 +440,21 @@ Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + QReadLocker locker(&server->clientsLock); - + QString receiver = QString::fromStdString(cmd.user_name()); Server_AbstractUserInterface *userInterface = server->findUser(receiver); if (!userInterface) return Response::RespNameNotFound; if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name()))) return Response::RespInIgnoreList; - + Event_UserMessage event; event.set_sender_name(userInfo->name()); event.set_receiver_name(cmd.user_name()); event.set_message(cmd.message()); - + SessionEvent *se = prepareSessionEvent(event); userInterface->sendProtocolItem(*se); rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, se); @@ -450,10 +468,10 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_G { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + // We don't need to check whether the user is logged in; persistent games should also work. // The client needs to deal with an empty result list. - + Response_GetGamesOfUser *re = new Response_GetGamesOfUser; server->roomsLock.lockForRead(); QMapIterator roomIterator(server->getRooms()); @@ -467,7 +485,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_G room->gamesLock.unlock(); } server->roomsLock.unlock(); - + rc.setResponseExtension(re); return Response::RespOk; } @@ -476,22 +494,22 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + QString userName = QString::fromStdString(cmd.user_name()); Response_GetUserInfo *re = new Response_GetUserInfo; if (userName.isEmpty()) re->mutable_user_info()->CopyFrom(*userInfo); else { - + QReadLocker locker(&server->clientsLock); - + ServerInfo_User_Container *infoSource = server->findUser(userName); if (!infoSource) return Response::RespNameNotFound; - + re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator)); } - + rc.setResponseExtension(re); return Response::RespOk; } @@ -500,13 +518,13 @@ Response::ResponseCode Server_ProtocolHandler::cmdListRooms(const Command_ListRo { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + Event_ListRooms event; QMapIterator roomIterator(server->getRooms()); while (roomIterator.hasNext()) roomIterator.next().value()->getInfo(*event.add_room_list(), false); rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event)); - + acceptsRoomListChanges = true; return Response::RespOk; } @@ -515,25 +533,43 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoo { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + if (rooms.contains(cmd.room_id())) return Response::RespContextError; - + QReadLocker serverLocker(&server->roomsLock); Server_Room *r = server->getRooms().value(cmd.room_id(), 0); if (!r) return Response::RespNameNotFound; - + + QString roomPermission = r->getRoomPermission().toLower(); + if (roomPermission != "none"){ + if (roomPermission == "registered") { + if (!(userInfo->user_level() & ServerInfo_User::IsRegistered)) + return Response::RespUserLevelTooLow; + } + + if (roomPermission == "moderator"){ + if (!(userInfo->user_level() & ServerInfo_User::IsModerator)) + return Response::RespUserLevelTooLow; + } + + if (roomPermission == "administrator"){ + if (!(userInfo->user_level() & ServerInfo_User::IsAdmin)) + return Response::RespUserLevelTooLow; + } + } + r->addClient(this); rooms.insert(r->getId(), r); - + Event_RoomSay joinMessageEvent; joinMessageEvent.set_message(r->getJoinMessage().toStdString()); rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent)); - + Response_JoinRoom *re = new Response_JoinRoom; r->getInfo(*re->mutable_room_info(), true); - + rc.setResponseExtension(re); return Response::RespOk; } @@ -542,7 +578,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUs { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + Response_ListUsers *re = new Response_ListUsers; server->clientsLock.lockForRead(); QMapIterator userIterator = server->getUsers(); @@ -551,10 +587,10 @@ Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUs QMapIterator extIterator = server->getExternalUsers(); while (extIterator.hasNext()) re->add_user_list()->CopyFrom(extIterator.next().value()->copyUserInfo(false)); - + acceptsUserListChanges = true; server->clientsLock.unlock(); - + rc.setResponseExtension(re); return Response::RespOk; } @@ -569,7 +605,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLeaveRoom(const Command_LeaveR Response::ResponseCode Server_ProtocolHandler::cmdRoomSay(const Command_RoomSay &cmd, Server_Room *room, ResponseContainer & /*rc*/) { QString msg = QString::fromStdString(cmd.message()); - + if (server->getMessageCountingInterval() > 0) { int totalSize = 0, totalCount = 0; if (messageSizeOverTime.isEmpty()) @@ -577,18 +613,18 @@ Response::ResponseCode Server_ProtocolHandler::cmdRoomSay(const Command_RoomSay messageSizeOverTime[0] += msg.size(); for (int i = 0; i < messageSizeOverTime.size(); ++i) totalSize += messageSizeOverTime[i]; - + if (messageCountOverTime.isEmpty()) messageCountOverTime.prepend(0); ++messageCountOverTime[0]; for (int i = 0; i < messageCountOverTime.size(); ++i) totalCount += messageCountOverTime[i]; - + if ((totalSize > server->getMaxMessageSizePerInterval()) || (totalCount > server->getMaxMessageCountPerInterval())) return Response::RespChatFlood; } msg.replace(QChar('\n'), QChar(' ')); - + room->say(QString::fromStdString(userInfo->name()), msg); databaseInterface->logMessage(userInfo->id(), QString::fromStdString(userInfo->name()), QString::fromStdString(userInfo->address()), msg, Server_DatabaseInterface::MessageTargetRoom, room->getId(), room->getName()); @@ -603,23 +639,25 @@ Response::ResponseCode Server_ProtocolHandler::cmdCreateGame(const Command_Creat const int gameId = databaseInterface->getNextGameId(); if (gameId == -1) return Response::RespInternalError; - + if (server->getMaxGamesPerUser() > 0) if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser()) return Response::RespContextError; - + QList gameTypes; for (int i = cmd.game_type_ids_size() - 1; i >= 0; --i) gameTypes.append(cmd.game_type_ids(i)); - + QString description = QString::fromStdString(cmd.description()); if (description.size() > 60) description = description.left(60); - - Server_Game *game = new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes, cmd.only_buddies(), cmd.only_registered(), cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(), room); + + // When server doesn't permit registered users to exist, do not honor only-reg setting + bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers()); + Server_Game *game = new Server_Game(copyUserInfo(false), gameId, description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes, cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(), room); game->addPlayer(this, rc, false, false); room->addGame(game); - + return Response::RespOk; } @@ -627,6 +665,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinGame(const Command_JoinGam { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + return room->processJoinGameCommand(cmd, rc, this); } diff --git a/common/server_room.cpp b/common/server_room.cpp index 985fb543..6ce86c8e 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -12,8 +12,8 @@ #include "pb/serverinfo_room.pb.h" #include -Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent) - : QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive) +Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, const QString &_permissionLevel, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent) + : QObject(parent), id(_id), name(_name), description(_description), permissionLevel(_permissionLevel), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive) { connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection); } @@ -46,6 +46,7 @@ const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool comple result.set_name(name.toStdString()); result.set_description(description.toStdString()); result.set_auto_join(autoJoin); + result.set_permissionlevel(permissionLevel.toStdString()); gamesLock.lockForRead(); result.set_game_count(games.size() + externalGames.size()); diff --git a/common/server_room.h b/common/server_room.h index bf91510c..3bb0e05e 100644 --- a/common/server_room.h +++ b/common/server_room.h @@ -32,6 +32,7 @@ private: int id; QString name; QString description; + QString permissionLevel; bool autoJoin; QString joinMessage; QStringList gameTypes; @@ -44,11 +45,12 @@ private slots: public: mutable QReadWriteLock usersLock; mutable QReadWriteLock gamesLock; - Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); + Server_Room(int _id, const QString &_name, const QString &_description, const QString &_permissionLevel, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); ~Server_Room(); int getId() const { return id; } QString getName() const { return name; } QString getDescription() const { return description; } + QString getRoomPermission() const { return permissionLevel; } bool getAutoJoin() const { return autoJoin; } QString getJoinMessage() const { return joinMessage; } const QStringList &getGameTypes() const { return gameTypes; } diff --git a/common/serverinfo_user_container.cpp b/common/serverinfo_user_container.cpp index 14435f75..a0fd05c9 100644 --- a/common/serverinfo_user_container.cpp +++ b/common/serverinfo_user_container.cpp @@ -36,9 +36,13 @@ ServerInfo_User &ServerInfo_User_Container::copyUserInfo(ServerInfo_User &result if (!sessionInfo) { result.clear_session_id(); result.clear_address(); + result.clear_clientid(); } if (!internalInfo) + { result.clear_id(); + result.clear_email(); + } if (!complete) result.clear_avatar_bmp(); } diff --git a/doc/usermanual/Usermanual.md b/doc/usermanual/Usermanual.md deleted file mode 100644 index 92e91a54..00000000 --- a/doc/usermanual/Usermanual.md +++ /dev/null @@ -1,1257 +0,0 @@ -Preface -======= - -This manual is basically a dump from the cockatrice.de dokuwiki. -Cockatrice has some legal problems right now and the page is down. This -document tries to save the documentation about the software, beautify -and extend it in the future. Please contribute to the project, it is -much too precious to be destroyed. - -Getting Started -=============== - -Making A User Profile ---------------------- - -Not available anymore, site is down. If someone runs his own server -where you can register a user profile, read his documentation. - -Downloading and Installing the Cockatrice Program -------------------------------------------------- - -Due to a legal dispute there are currently no official builds left, so -currently you have to build your own binaries. - -### Client compilation - -#### Windows - -There should be two ways to compile Cockatrice: With Visual Studio 2010 -and with MinGW. As the Visual Studio method is more complicated and -might not work, you should do it with MinGW. The following howto has -been tested with Windows 7 64Bit. - -##### Prerequisites - -We need the Cockatrice sourcecode, it’s dependencies and build tools. -Everything is freely available: - -1. MinGW is needed to compile everything, it ships the compiler and - other tools for this task. - -2. git is needed to download the latest Cockatrice source code. - -3. cmake is needed to create Cockatrice’s project files for MinGW. - -4. Qt 4.8 is a dependency for Cockatrice (download the MinGW version!) - -5. protobuf 2.5 is another dependency for Cockatrice (download the zip - file with the sourcecode). - -6. Nullsoft Scriptable Install System (NSIS) which can be used to - create an installer for your own Cockatrice version. - -All downloadlinks together: - -##### Installation of the Prerequisites - -1. Download MinGW (mingw-get-inst), git, cmake, protobuf 2.5 sources - zip and Qt 4.8.4 MinGW (see links above). - -2. Make a standard installation for git, cmake and NSIS. - -3. Install MinGW: - - 1. Select default values everywhere, except: Also check the C++ - Compiler, check the MSYS Basis System and MinGW Developer - Toolkit. - - 2. Append `C:\MinGW\bin` to your PATH variable (google how to do it - for your Windows version, if you don’t know it). This is very - important and overseen many times! You can keep the PATH - configuration dialog open for the next step. - -4. Install Qt, select default values everywhere, ignore the message - regarding win32.h and append `C:\Qt\4.8.4\bin` to your PATH variable - like you did for MinGW. - -5. Unpack protobuf-2.5.0.zip in `C:\MinGW\msys\1.0\home\YOURLOGIN` - - 1. Open MinGW Shell, type the following commands exactly: - - 2. `cd protobuf-2.5.0/protobuf-2.5.0` - - 3. `./configure –prefix=‘cd /mingw; pwd -W‘` (these apostrophs are - backticks and do not forget the dot at the beginning!) - - 4. `make; make install` (this builds and installs protobuf, which - needs some time) - - 5. Close MinGW Shell - -Your system is now able to compile Cockatrice. You do not have to repeat -the process so far ever again. - -##### Cockatrice Compilation - -1. Checkout Cockatrice: - - 1. Start Git Bash and type the following command exactly: - - 2. `git clone https://github.com/Cockatrice/Cockatrice` - - 3. Close Git Bash - -2. Start CMake (cmake-gui) and do the following: - - 1. Where is the source code: Point to the Cockatrice directory - - 2. Where to build the binaries: Point to the Cockatrice/build - directory (it doesn’t matter if it exists; if it doesn’t, cmake - will ask later if it shall create this directory) - - 3. Check Advanced - - 4. Click Configure, choose MinGW, leave the rest default - - 5. An error will occur, set the following: - - - Set PROTOBUF\_INCLUDE\_DIR to - C:/MinGW/include/google/protobuf - - - Set PROTOBUF\_LIBRARY to C:/MinGW/lib/libprotobuf.dll.a - - - Set PROTOBUF\_LITE\_LIBRARY to - C:/MinGW/lib/libprotobuf-lite.dll.a - - - Set PROTOBUF\_PROTOC\_EXECUTABLE to C:/MinGW/bin/protoc.exe - - - Set PROTOBUF\_PROTOC\_LIBRARY to - C:/MinGW/lib/libprotoc.dll.a - - 6. Click Configure again, then Generate - - 7. Close CMake - -3. Start cmd.exe (the Windows Command Prompt) and type the following - commands: - - 1. `cd Cockatrice/build` (this changes to the build directory) - - 2. `make` (this builds everything and might need some time) - -Cockatrice has now been downloaded and built for the first time. You do -not have to repeat the process so far ever again. - -##### Updating your Cockatrice build - -If you just compiled Cockatrice for the first time, you skip this step -obviously. But if you want to update Cockatrice after the source code -changed on github, do it like this: - -1. Start Git Bash and update Cockatrice: - - 1. `git pull origin master` - - 2. `Close Git Bash` - -2. Start cmd.exe, change to Cockatrice/build, type make like you did - previously. - -Cockatrice has now been updated and built. You may repeat this process -every time when the source code changed. - -##### Cockatrice installation - -To install Cockatrice, you have to create an installer with NSIS now. -Change to the directory `nsis` in the Cockatrice root directory, right -click the cockatrice.nsi file and select `Compile NSIS Script`. The NSIS -program then creates a file called -cockatrice\_win32\_YYYYmmdd\_git-xxxxxxx.exe. This is the complete, -redistributable installer for your Cockatrice build. - -Now install Cockatrice by executing the installer. Note: if you -installed MinGW or Qt in other than the default paths, you have to fix -the paths in the cockatrice.nsi file (also if some libraries change); -you can edit this file with a text editor. - -##### Create a card database - -Start the oracle.exe (the installer does this automatically) and let it -generate a current cards.xml file: - -1. File $\to$ Download Sets Information $\to$ OK (if there are no MtG - sets listed) - -2. Check All, Start Download - -Congratulations, you may now use Cockatrice! - -#### Linux and BSD - -The following procedures have been tested with Debian Wheezy, Fedora 18 -and FreeBSD 9.1. If you use Gentoo with KDE you have the needed -prerequisites and may continue with downloading the source. If you use -Bodhi or Arch Linux (AUR) or another distribution that includes -Cockatrice, you might install Cockatrice from the default packages – -though the package might be old, so you probably should continue with -this howto. - -Before you install new software, you should update your system. The -following instructions failed on a fresh installation of Fedora 18 and -FreeBSD 9.1 until the systems were updated. - -1. You need to install the build tools and dependencies. This varies - between the Linux distributions. - - Debian, Ubuntu and spin-offs - : `sudo apt-get install build-essential git libqt4-dev qtmobility-dev libprotobuf-dev protobuf-compiler cmake` - - Fedora - : `sudo yum groupinstall "Development Tools" yum install qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake` - - FreeBSD - : `pkg_add -r qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf` - -2. Download the sources from github via - `git clone https://github.com/Cockatrice/Cockatrice.git` - -3. To compile the sources, change into the newly created directory, - create a build directory and invoke cmake: - - i. `cd Cockatrice` - - ii. `mkdir build` - - iii. `cd build` - - iv. `cmake ..` - - v. `make` - - If you have some issues with pthread\_ add ’pthread’ to the - “target\_link\_libraries” entry in the `CMakeFiles.txt` in - `Cockatrice/common`. - -4. You may install the program into the directory `/usr/local` by - typing `sudo make install` but you should also be able to start - cockatrice and the oracle from the build directory. - -5. Before you start Cockatrice for the first time, run `oracle -dlsets` - and download available cards, denn run `cockatrice`. The default - paths for decks, pics, cards and tokens are located in - `/home//.local/share/data/Cockatrice/Cockatrice`. - -#### MacOS X - -TODO, please contribute this section! See Linux section, then use the -`prepareMacRelease.sh` script from Cockatrice. - -### Server compilation - -You don’t need your own server if you plan to play only. But as -Cockatrice is open source you are free to run your own. The compilation -works like already written above, but instead of invoking `cmake ..`, -you have to do it like this: - -- If you want to build the server, use: - `cmake -DWITH_SERVER=1 ..` - -- If you want to build the server, but not the client, use: - `cmake -DWITH_SERVER=1 -DWITHOUT_CLIENT=1 ..` - -There is more information on compiling and running Servatrice on CentOS -6 in chapter [servatrice] on page . - -Downloading Card Database Using the Oracle ------------------------------------------- - -If you are installing Cockatrice for the first time, changing what sets -are in your database or even adding the newest set to your database, -this tutorial will show you how to do it properly. - -The Oracle will automatically run after the initial setup of Cockatrice. -If you would like to re-install your database or add new sets you can -find it (for Windows) by clicking the start menu, going to all programs, -selecting the Cockatrice folder, and in there you will find the Oracle -tool. - -- When the Oracle importer opens, click on “File” in the top left - corner and select “Download sets information…” - - ![image](pics/fetch554a.jpg) - -- This will bring up a box where you can input the URL of a card - database. The default address is - this was an XML file found - on the Cockatrice website that has the current set listings for - Magic the Gathering. As the page is down, you have to import the - file which is distributed with the Cockatrice sources. This can also - be done from the file menu. - -- Select “OK” to load the set listings. - -- A list of all current sets will be brought up. A default selection - of sets will automatically be checked. From here you can check or - uncheck all sets, or you may only download specific sets that you - wish to play with. - - ![image](pics/fetchfc3d.jpg) - - NOTE: If you are playing against someone who is using a card that is - not in your database, you will not see a card image or oracle text - for that card. Some players like to download all sets to avoid this - issue, but other players who only play specific formats (Like T2, - Standard, or Extended) wish to keep their database small with only - cards they will be using. - -- After you select which sets you wish to download, select “Start - download” at the bottom of the Oracle to download the selected sets - information. - -- After download is complete, close the Oracle and run Cockatrice. - -- We are now ready to sort our set information in our deck editor. - -Editing Set Order and Preference of Card Art --------------------------------------------- - -Many cards have been re-printed in different sets, and in return have -different versions of artwork (like the card “Cancel” which can be found -in many sets, but has different artwork for each, e.g. Zendikar versus -M11: - -![image](pics/fetchc18b.jpg) ![image](pics/fetche1f4.jpg) - -Some players like to have the most current artwork displayed on their -cards, while other players have a favorite set they wish to display -instead. - -- Run Cockatrice and select “Deck editor” from the top right - Cockatrice menu. This will bring up the Deck Editor along with a - list of all cards that are currently in your database which you - downloaded using the Oracle Tool. - -- To change what version of the cards will be shown, click on “Card - database” on the top left of the Deck editor window, and select - “Edit sets…” - - ![image](pics/fetchf924.jpg) - -- This will bring up a new window that has a list of all sets you - currently have downloaded to your database. To change the order of - the sets, simply drag and drop them into place. This will determine - which artwork is shown for your cards. If a card is found in - multiple sets, whichever set is closest to the top of this list will - be the art displayed. Example: If M11 is above Zendikar, The M11 - version of the card “Cancel” will be displayed in your Deck editor - and Cockatrice games. - -NOTE: Your opponent will NOT see what artwork you have selected for each -of your cards. They will only see what they have selected for their own. - -The Deck Editor / Making a Deck -------------------------------- - -The Cockatrice Deck Editor is a tool you can use to make decks to play -online. The cards shown in the Deck Editor are from a database that you -downloaded with the Oracle Tool. If you are missing cards or a new set -has come out, you must re-run the Oracle and download set information. - -![image](pics/fetch52e0.jpg) - -1. Search Bar -: The search bar lets you type in the name of a card and the editor - will only show cards that start with whatever you typed in. Example: - Typing in ‘B’ will show all cards that start with the letter ‘B’ and - typing in ‘Dark’ will show you all cards that start with ‘Dark’ and - so on. If you were looking for the card “Sun Titan”, you would not - type in ‘Titan’ you would have to type in ‘Sun’ first. Typing in - ‘Titan’ will only show you any cards that start with ‘Titan’. - -2. Card Search/Filter -: The Card search button will bring up a new window that helps you - filter out cards more specifically. A variety of check boxes will - help find what you need. Card name lets you filter out only cards - that have a cretin word in them. Card text can help you find key - words like “Haste” or “Infect”. If you were to uncheck all boxes - except for “Instant” along with “Artifact” and “U”, the Deck editor - will only show you all Blue Instant and Artifact cards. - - |Letter | Card Type | - |-------|-----------| - | U | Blue | - | W | White | - | X | Colorless | - | G | Green | - | R | Red | - | B | Black | - -3. Card Data -: This section shows the Oracle text for the card that you currently - have selected. It will show you up-to-date information on the card - such as the Name, Mana cost, Card type, Power/Toughness, and any - abilities the card has. It will not show you flavor text. - -4. Adding/Removing Card Buttons -: The buttons in the bottom middle will add or remove cards from your - Deck List, as well as ad a card specifically to your Sideboard. - Having a card selected on the left column and hitting the Enter key - will also add it to your deck list. - -5. Deck Name/Comments -: The area in the top right lets you name your deck as well as give - any comments or descriptions such as how to play the deck, where the - deck came from, or explain a theme. Putting something in the “Deck - Name” space will NOT be what the file name of the file for your - deck. That is spate in the “save” selection under the “Deck” menu - found at the top left of the Deck Editor window. - -6. Main Deck List -: This area will show you what cards you currently have added to your - deck list. It is sorted by card type and also shows you how many of - each card and card type you have added, as well as keeps track of - how many total cards you have added to your main deck list. This - will not add any number of cards you have added to your Sideboard. - -7. Sideboard List -: The bottom section of the deck list shows any cards you have added - to your sideboard. Again these are split into card types and it will - keep track of how many of each card you have as well as how many of - each card type and total cards in sideboard. This section will not - add any cards from the main deck. Once you have all of your cards - added to your deck, you must save it as a file Cockatrice can read. - Select “Deck” from the top left corner of the Deck Editor screen, - and select “Save Deck” or “Save Deck As…” and it will bring up a new - window where you can select where you would like to save your deck, - as well as assign it a file name. Cockatrice decks are saved as .cod - files. - -Loading a deck list from your clipboard ---------------------------------------- - -If you find a deck online, or you have a deck list saved in a word -document, it is easy to transfer it over into a Cockatrice deck file as -long as it is in a simple deck list format. The simple deck list format -is a list where each line begins with a number, followed by a -whitespace, followed by the cardname, e.g. - - 2 Doom Blade - 13 Island - 10 Swamp - 4 Cancel - ... - -Simply find the word document or deck list online that you wish to save -as a deck, and select the text and copy it to your clipboard. Next, open -the Deck Editor screen, and click on the Deck menu from the top left -corner. Select “Load deck from clipboard…” and the deck editor will -bring up a new window that has the deck list you had copied to your -clipboard. Make sure the Deck list looks correct and hit “OK” in the -bottom right corner of the window. The Deck editor will now add all the -cards in the list to your main deck list. - -![image](pics/loaddeck_clip.jpg) - -![image](pics/okdecklist.jpg) - -NOTE: If you add a card to a deck list with this function that you do -not have downloaded to your personal cockatrice database though the -Oracle tool, the card will take a spot in your main deck list, and count -toward the total number of cards, but it will show up as a blank image -with no Oracle data or card information. - -Cockatrice Settings -------------------- - -TODO - -Learning the Ropes / Starting a Solitaire Game ----------------------------------------------- - -The best way to get familiar with the way Cockatrice plays is to start a -local game that you can play around in by yourself. You could also jump -online and start slowly learning, and let other players help you. - -To start a Solo Local game, in the main Cockatrice window, click on -“Cockatrice” on the top left, and select “Start local game…”. - -![image](pics/fetch2ab8.jpg) - -This will bring up a small window that lets you select how many players -will be in this local game you are creating. For right now, since we -want to do a solo Solitaire game, select one player and hit “OK”. - -![image](pics/fetchf010.jpg) - -### Loading a Deck / Using Sideboard - -This will now bring you to a screen where you load a deck to play with. -On the top left part of this screen you will find a button that is -labeled “Load Local Deck”. Click that button and it will bring up a -window where you can find and select what deck you would like to play -with. - -![image](pics/fetchf0d2.jpg) ![image](pics/fetch55a7.jpg) - -Select a deck or a .cod file and click “Open”. - -![image](pics/fetch9b89.jpg) - -After the file has loaded you will see all of the cards in that deck -laid out on the table. If you hover your mouse over a card, the card -image and Oracle info will show on the right side of the screen. If you -have a sideboard made for the deck there will be a second section on the -table for this sideboard. This screen gives you the ability to double -check your deck to make sure it is not only the correct deck you want to -play with, but it lets you see that all card images have downloaded -properly. If you have cards not showing up at all or they are just blank -cards with names on them, you may not have that set downloaded with your -Oracle tool. If you have a sideboard, you can drag and drop cards from -your main deck to your sideboard or vice-versa. You can do this by -clicking and dragging a card to or from your main deck or sideboard. - -NOTE: Moving cards from your main deck to sideboard will NOT change how -your deck file is saved, it will only change it temporary for the game -you are playing or until you load a new deck. - -When you are satisfied with your deck choice and/or sideboarding -options, click on the red outlined “Ready to start” button found a the -top of the screen. - -### Finding Your Way Around - -The main game screenlooks like this - -![image](pics/fetch7cf0.jpg) - -(Please note your screen will look different due to background image -options.) - -#### Main Table / Play area - -Split into four areas, this is where all the action will go down. - -The Stack -: The area on the left side of the table where Instant and Sorcery - cards will be played. This is for things that will only temporarily - be put on the table, then into the graveyard. Multiple cards may be - added to this area at the same time. Anything on this part of the - table will be seen by all players. - -Battlefield -: This is the soul part of the game table. this is where creatures, - enchantments, artifacts, and even plainswalkers will be placed. As - cards are moved from your had to the table, they will be aligned to - an invisible grid and moved around from there. Tap cards by double - clicking them. Anything on this part of the table will be seen by - all players. - -Land -: This space is for land cards, but any card may be placed here. Tap - cards here by double clicking them. Anything on this part of the - table will be seen by all players. - -Hand -: Every time you draw a card it will go here to your hand. You may - also drag cards from the table back to your hand. Your opponents can - not see what is in your hand. - -#### Player Info Section - -![image](pics/fetch0300.jpg) - -Player Avatar -: This is a $156\times 60$ pixel JPG image that can be uploaded though - the main Cockatrice website. All players in the game room can see - this image. It serves nothing more than an online identity for you - and other players. - -Player Name -: Your online name that you picked though the main Cockatrice website. - -Life Total -: Your in-game life. Using your mouse, if left-clicked will raise this - number by one, and if right-clicked lowered by one. There are also - keyboard shortcuts to change your life total. - -Counters -: These seven multicolored circles are used as counters. They can be - seen by all players and can be changed by left or right clicking on - them to add or subtract a number. Players use them for various digit - counting but primarily used for adding and subtracting floating mana - produced by card effects. The bottom two white counters can be used - for other things like Poison. - -Library -: This is your deck of cards. The number in the middle reflects how - many cards are left in your library. Double clicking the deck lets - you draw a card and add it to your hand, you can also drag cards off - the top into the battlefield or to your hand. Right-clicking the - deck brings up a menu that allows other things to happen like - reviling the top number of cards, shuffling, or moving cards - directly into the graveyard. - -Number of cards in hand -: The number in the middle represents how many cards are currently in - your hand. Other players can see this number but can not see the - cards actually in your hand. - -Graveyard -: Cards can be dragged and dropped into your graveyard from play or - vise-versa, the stack, your hand, or even your library. The number - in the middle represents how many cards are currently in your - graveyard. Any player may right-click on the graveyard and bring up - a menu that shows what cards are in it. - -Exile -: Cards can be dragged and dropped into exile from play or vise-versa, - the stack, your hand, or even your library. The number in the middle - represents how many cards are currently in your exile. Any player - may right-click on the exile and bring up a menu that shows what - cards are in it. - -#### Turn Phases - -![image](pics/fetchfebd.jpg) - -This bar located on the left most side of the screen represents the 11 -steps in a players turn. To go from one phase to the next, you can click -on the square of the phase you want to move to, or you can hit -Ctrl+space to move down to the next. Some phases even have their own -keyboard shortcut. Going from one phase to the next does not actually do -anything to your or your cards, it is only a place marker for your -opponents to see and keep up with what you are doing in your turn. For -example, clicking to the “Draw Phase” will not automatically draw you a -card. It is customary for a player to end their turn on the “End of turn -step” and let their opponent hit the “next turn” button. This is a -courtesy for other players if they wish to do something like use an -instant at the end of your turn, or in response to something you did. - -NOTE: Players sometimes use the term EOT which stands for “End Of Turn”. -This is to let other players know they are doing something in response -to the end of the current turn. - -#### Info/Chat Bar - -![image](pics/fetcha0de.jpg) - -Split into three sections, the Info/Chat bar lets you see a close-up -image of the card your mouse was last over, as well as gives you the -card info for that card. At the bottom of this bar there is a chat log -that helps keep track of events during the game as well as lets you -communicate with other players. if a card is placed on the table, -pointed at, or tapped it will get noted in the chat log as well as has a -link to the card that you can hover over and see an image of at the top -of the bar. - -### Basic Functions - -#### Rolling Dice - -At the beginning of a game players decide who is going first by rolling -a 20 sided die. In Cockatrice we do this by pressing Ctrl-I and hitting -enter. Hitting Ctrl-I brings up a die window and lets you select how -many sides you want on your die. Default is 20, and pressing enter will -“roll” the die. This action will show up in the cat log at on the bottom -right of the screen. You can also find this in the “game” menu at the -top of the window, selecting “player” and clicking on “roll die…” - -![image](pics/fetch7486.jpg) - -![image](pics/fetch3705.jpg) - -#### Draw Cards / Mulligan - -When a game starts and the first player has been selected, all players -will draw seven cards. this can be done by pressing Ctrl-M. Seven cards -will go from your library to your hand. Pressing Ctrl-M again will put -the seven cards from your hand back into your library, shuffle your -library and deal out six new cards to you. Each time you press Ctrl-M it -will give you one less card until you get down to one card, then it will -re-start at seven cards. This function can be found by clicking the -“game” menu on the top of the window, selecting “player” then selecting -“hand” and then “take Mulligan”. If you are playing a friendly game, -press Ctrl-M as normal, but then press Ctrl-D to draw cards until you -have a total of seven again. - -![image](pics/fetchfe20.jpg) - -#### Tapping - -Tapping cards is very basic. If a card is on the table under your -control, you can double click it to tap it and then double click again -to untap it. You can select multiple cards on the table by clicking and -dragging your mouse, then tap or untap all of the selected cards at the -same time. Other players can not tap or untap your cards. Pressing -Ctrl-U will untap everything you control. - -- Untapped - - ![image](pics/fetchb6fe.jpg) - -- Tapped - - ![image](pics/fetch867f.jpg) - -#### Attaching Cards to Cards - -Sometimes an Enchantment -Aura or Equipment cards need to be attached to -other cards that are already on the table. simply put the enchantment or -equipment on the table. Right-click the card and select “attach” (this -can also be done with Ctrl-A). A green arrow will appear, point and -click on the card you wish to attach. You can also attach cards to other -people’s cards. - -![image](pics/fetch100e.jpg) - -![image](pics/fetchb17a.jpg) - -#### Changing Power/Toughness - -Enchantments, Equipment, and other effects sometimes change a creatures -power or toughness. This can be done by right-clicking the card, and -selecting “power / toughness” then selecting which one you wish to do. -Other players can not change your creatures power and toughness. This -can also be done though a series of keyboard shortcuts seen below. - - -------------------------- ------------------------------ - (Select card) Ctrl++ Increase power - (Select card) Ctrl+- Decrease power - (Select card) Alt++ Increase toughness - (Select card) Alt+- Decrease toughness - (Select card) Ctrl+Alt++ Increase power and toughness - (Select card) Ctrl+Alt+- Decrease power and toughness - -------------------------- ------------------------------ - -![image](pics/fetche3fc.jpg) - -![image](pics/fetchd922.jpg) - -#### Adding Counters to Cards - -Sometimes Counters are needed to be placed on cards that the counters on -the side of the screen are not able to track. Cockatrice offers three -different counter color options, Red, Green, and Yellow. Although there -is no set standard on what color stands for what, it is mostly player -preference. Green could be used for +1/+1, red -1/-1, leaving yellow for -charge and quest counters, this is not a set rule. Adding counters is as -simple as right clicking on the card you wish to add counters too, and -currently there is no keyboard shortcut for this process. Removing -counters is the same process, right click and select remove. Other -players can not add or remove counters to or from your cards. - -![image](pics/fetch5170.jpg) - -(One of each counter) - -#### Pointing at Cards / Arrows - -Pointing at cards is needed for resolving spells, or declaring attackers -and blockers. All you need to do is right-click over a card and drag an -arrow over to what you are pointing at. Permanents, spells in the stack, -and even a players life total can be pointed at. You can point at your -opponents cards and life total, and they can point at yours. When your -arrows are no loner needed, press Ctrl-R to remove them from the screen. - -![image](pics/fetch98fd.jpg) - -![image](pics/fetch74b2.jpg) - -#### Creating Tokens - -Creating tokens can sometimes be tedious, but is well worth the effort -to keep a clean and organized game. Pressing Ctrl-T will bring up a -small window to assist you in creating a token. Simply enter the name of -the token you are creating, select its color, and give it a power and -toughness (\#/\#). You can also bring up this token window by selecting -“game” from the top menu, selecting “Player” then clicking on “Create -Token…”. A copy of the Last token made can be done by pressing \*Ctrl+G -or right-clicking on a already made token (or any card on the table) and -selecting “clone” or pressing Ctrl-H\*\*. When a token or clone leaves -play, it will be destroyed and vanish. - -![image](pics/fetch2c36.jpg) - -![image](pics/fetch9bff.jpg) - -![image](pics/fetche6b2.jpg) - -![image](pics/fetch84a2.jpg) - -Make copies of your last token by pressing Ctrl-G. - -![image](pics/fetch6847.jpg) - -Playing Online -============== - -With Cockatrice you will most likely play Magic games over the Internet -with real people all around the world. In order to help maintain a -pleasant environment for users, please read the messages below: - -- User Code of Conduct[^1] – Must Read for all Users - -- How to Report Abuse[^2] – It is recommended to read this as well - -Connect to Server ------------------ - -To connect to the Cockatrice server, launch the Cockatrice program, go -to the “Cockatrice” menu at the top left, and select “Connect”. A window -will appear (see image below). - -![image](pics/fetch23f3.jpg) - -If you have registered with Cockatrice, then enter your Username in the -“Player Name” field and your password in the “Password” field then click -“OK”. You may check the “Remember Password” box if you wish. If you do, -then the next time “Connect” is selected from the “Cockatrice” menu, the -window that appears will already have your Username and Password already -filled. Please take this into consideration if you share a computer with -other people, seeing that you are responsible for anything that happens -on the server with your username (As noted here). If you did not -register with Cockatrice, then simply fill in the Username with whatever -you like and click “OK”. If you would like to become a registered user, -read the instructions from the server’s website. - -Once you are connected to the server, more tabs will appear at the top -of the screen next to the “Deck” tab that you are already on. - -All About Games ---------------- - -This page is about creating, joining, watching, and searching for games -on the Cockatrice Server. In order to participate in any games, you have -to be connected to the server. The games on the server are where all of -the action take place. There will be many games happening on the server -at the same time. Basically, first a game is created by a player (it -could be you). Then other players join the game until the number of -players reaches the number set by that game’s creator. When the game has -no players participating in it, the game disappears. Creating a Game - -To Create a game, go to the “MTG room” tab. Click on the “Create” button -below the Games list. A window will appear (see below). - -![image](pics/fetch54df.jpg) - -Here are all of the options for creating a game: - -Description -: Describe the game in your own words (i.e. “Competitive Standard”, - “Casual EDH- No Infinites”, “RavnicaDraft”, “Here is Chris”) - -Players -: Specify the number of Players in the game. This cannot be changed - after the game is created. The game can only begin when the - specified number of players join. - -Spectators -: Spectators are users that are in a game, but they are not one of the - players. Spectators can see all of the public zones of the game and - everything displayed in the Info/Chat Bar. Any number of users can - join a game as a Spectator (as long as the “Spectators Allowed” box - is checked). - - Spectators Allowed - : Unchecking this box will prohibit any/all users from joining the - game as a Spectator. - - Spectators Need a Password to Join - : Checking this box will make it so that in order for a user to - join as a Spectator, they need to type the password you specify - in the Password Field. - - Spectators can Chat - : Checking this box will allow Spectators to type comments in the - Chat bar during the game. - - Spectators See Everything - : Checking this box will allow Spectators to view cards in all - private zones of all players (hands, libraries, face-down - cards). - -Password Field -: If you type anything in this field, a Player (or Spectator if the - “Spectators Need Password” box is checked) will need to type the - exact same thing you typed in order to join the game (and it is - case-sensitive). - -Only Buddies Can Join -: Checking this box will prevent any user who is not in your Buddy - List from joining the game as a Player or Spectator. NOTE: Your - username is not on your buddy list. If you leave a game you created, - and this box is checked, you will not be able to rejoin. - -Only Registered Users Can Join -: Checking this box will prevent anyone who has not registered on the - Cockatrice website from joining as a Player or Spectator. - -Game Type -: These check boxes have no effect on the game. They inform other - Users browsing the Games list of what format your game is. Users can - choose to view only games of a certain Type/Format. - () - -### Joining a Game - -Most of the time, to join a game you click on the “MTG Room” tab, click -on a game in the Games list, then click Join. If the Game’s creator -specified a password then you will have to type that password in a small -window that appears after you click Join (the password is -case-sensitive). If your User Profile meets the criteria of the Game’s -creator then a new tab will open with that game. There is also an easy -way to join a game in which a User in your Buddy List is playing. Go to -the “User Lists” tab, right click any Username from the Buddies Online -list (at the left of the window) to make a menu appear, and select “Show -this user’s games”. A window will open with a list of games that the -User is either playing or watching. In the same manner as described with -the “MTG Room” tab, simply click on a game and click Join. To watch a -game, the instructions are the same except that you click the “Join as -Spectator” button instead of the Join button. NOTE: If you are a player -in a game and you wish to become a spectator in that game, you must -first leave the game then rejoin as a Spectator. Same thing if you are a -Spectator and wish to play. - -### Searching for Games - -The Games list in the “MTG Room” tab displays by default all games that -have not reached the specified number of players. The “Filter Games” -button makes looking through this list easier if you are looking to join -a particular kind of game. When this button is selected, a window -appears (see below). - -![image](pics/fetchd30e.jpg) - -Game Description -: Displays games with certain descriptions. You can even search - partial names. - -Creator -: Displays games created by Users with that username. It even searches - for partial names. - -Player Count -: Displays all games where the specified number of players is - greater-than or equal to the “at least” number and less-than or - equal to the “at most” number. For instance, setting both numbers to - 3 will display all games whose creators made as 3-player games. - -Show Unavailable Games -: Checking this box will display games that are full and in progress. - You can still join these games as a Spectator if the game’s creator - allows it. - -Game Types -: Displays games with the selected types. Bear in mind that the - Cockatrice software does not enforce deck construction for formats. - So just because a game’s type is EDH/Commander, doesn’t necessarily - mean that is what’s being played in the game. Players can agree to - switch formats in a game. - -Keeping Track of Buddies ------------------------- - -Cockatrice allows registered users to keep track of other registered -users in a buddy list. You won’t be able to do anything with this list -(or other registered users at all) unless you are a registered user and -connected to the server. - -To add a User to your Buddy list, right-click their username and, in the -menu that appears, select “Add to buddy list”. You can right-click and -add a User in this manner anywhere you see their username (under the -“User lists” tab, the “MTG Room” tab, in a game, or in a direct chat). -When you add a User, their username will appear in the list “Buddies -Online” located under the “User lists” tab. If the username appears in a -light shade of gray, then that User is not connected to the server. If -it appears in black, then that User is connected to the server. - -If you see that one of your Buddies is connected, you can see the games -he/she is currently in. Right-click their username, and in the menu that -appears select “Show this user’s games”. A window will appear from which -you can watch or play in a game that your buddy is currently in. - -You can Direct Chat with a Buddy (or any user for that matter) by -right-clicking their username and, from the menu that appears, selecting -“Direct Chat”. When you do this, a new tab will open (both on your -window and on the other User’s window) containing a chat room that can -only be seen and used by the two of you. The place where you type in -your messages is at the very bottom of the window. - -To remove a User from your Buddy list, right-click their username, and -from the menu that appears select “Remove from buddy list”. - -Dealing with and Preventing Unpleasantries ------------------------------------------- - -Make a screenshot, there are several free programs available. -Right-Click a username to add to Ignore List. - -Frequently Asked Questions (FAQ) -================================ - -How to update the card database -------------------------------- - -When a new set comes out, you need to update your card database to be -able to see the new cards in the deck editor and use them in the game. -To do this, open the Oracle tool in the Cockatrice folder. It’s a -stand-alone program and is run directly out of the folder without -running Cockatrice. - -When the Oracle tool is running, open the menu and select ’Download sets -information’. Use the suggested address and click OK. After at most a -few seconds, you should be presented a list of sets to be downloaded. Do -not uncheck the ones you already have: they should be downloaded again. -Click ’Start download’ and wait. When the process is finished, the -database should be up to date. - -Change Card Art / Custom Card Art ---------------------------------- - -Is it possible to change the card art for a specif card, not by changing -the set order in my deck editor? - -YES! It is actually very simple. - -All you have to do is get a .JPG of the card you wish to change and name -the actual file of the image to the cards (exact) name and add ”.Full” -to the end. You can then take this new card image and replace the old -one in your downloaded image folder. - -NOTE: Image size does not matter. Try to use a high resolution .JPG of -the card you want to use for best quality. - -EXAMPLE: Let’s say you wish to turn your M11 Sun Titan from its original -art to a promo version. - -![image](pics/fetch74e3.jpg) - -If you are using Windows 7, you can find the default location for the -Cockatrice card art files under -`C:\Program Files (x86)\Cockatrice\pics\downloadedPics` Once there, look -for the folder of the set for the card we are replacing. For this -example, Sun Titan is in the M11 folder. Open the folder and -paste/replace your new “Promo” Sun Titan.full[.JPG] into this folder, -discarding the original copy. (If you have not yet downloaded the -original image of a card by selecting it in the deck editor or playing -it in an online game, the card image will not yet be there) - -Once you have the new card image in the proper folder, you can now start -Cockatrice (Or re-start if you already had the program open)and select -the deck editor to see your new card image. - -If you are changing the art of a card that has multiple versions in -other sets, make sure you put the new card in whatever folder (or set) -is highest on your card database set list. - -NOTE: Other players on Cockatrice will NOT see your new card image. They -will only see whatever version of the card is in their database. - -Use Higher Resolution Cards ---------------------------- - -Can you get better/higher resolution card art than the default -downloaded card images already used? - -Yes! - -When you click on a card for the first time in the Deck Editor, -Cockatrice goes onto the internet and finds an image of that card from a -database on another website. - -If you find a higher resolution .JPG of a card that you wish to use on -your Cockatrice, you can replace the image with no problem. If you can -find a card image in the 3,000 by 1,000 pixel range, and save it as the -cards (exact) name and add .Full to the end. - -If you are using Windows 7, you can find the default location for the -Cockatrice card art files under -`C:\Program Files (x86)\Cockatrice\pics\downloadedPics`. Once there, -look for the folder of the set for the card you are replacing. (If you -are not running Cockatrice as an administrator, look in -`C:\Users\username\AppData\Local\VirtualStore\Program Files (x86)\Cockatrice\pics\downloadedPics`.) - -Open the folder of the set with the card, and paste/replace the new -higher resolution .JPG image with the old low quality one. - -This process is the same for changing cards to custom images. - -NOTE: Other Cockatrice players will not see your higher resolution card -art, they will only see the image they have in their own database. - -Booster drafts --------------- - -Cockatrice does not support booster drafts, so players need to use an -external service for this. - -Most people use so it’s probably not a bad idea -to register an account there before joining a game in Cockatrice. - -What is Legacy / Vintage / EDH? -------------------------------- - -See . - -Linking cards and URLs in the Cockatrice chat. ----------------------------------------------- - -The Cockatrice chat supports linking of cards and URLs by use of certain -tags around a word or phrase. - -### How to link a card in the Cockatrice chat - -To link a card in the Cockatrice chat, type out the full name of the -card, surrounded by the [card] and [/card] tags. - -For example: - - [card]Black Lotus[/card] - -### How to link a URL in the Cockatrice chat - -To link a URL in the Cockatrice chat, type out the url, surrounded by -the [url] and [/url] tags. - -For example: - - [url]http://www.cockatrice.de[/url] - [url]cockatrice.de[/url] - -Servatrice on CentOS 6 -====================== - -[servatrice] This Howto from woogerworks.com will help you to run your -own Cockatrice server (called Servatrice). An installation script can be -downloaded at: - -BEFORE CONTINUING, PLEASE UNDERSTAND THESE ARE THE VERY MOST BASIC -STEPS. THIS WILL NOT CONFIGURE SECURITY SETTINGS (YOU ARE RESPONSIBLE -FOR YOUR OWN SECURITY)! THIS WILL NOT CONNECT THE SERVER TO A DATABASE! -USE AT YOUR OWN RISK. - -1. Open a command shell and install the prerequisites - - 1. `yum -y groupinstall "development tools"` - - 2. `rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm` - - 3. `yum -y install qt-mysql qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake28 libgcrypt-devel` - - 4. `cd` - -2. Download the source code: - - 1. `git clone https://github.com/Cockatrice/Cockatrice` - - 2. `cd Cockatrice` - - 3. `mkdir build` - - 4. `cd build` - - 5. `cmake28 -DWITH_SERVER=1 ..` - - 6. `make` - - 7. `sudo make install` - -3. Create a servatrice.ini file. There is an example file you can - convert: - `sed -e "s/number_pools=1/number_pools=0/" ../servatrice/servatrice.ini.example > /servatrice.ini` - Here is one example file: - - [server] - port=4747 - statusupdate=15000 - logfile=server.log - name="My own Cockatrice server" - id=1 - number_pools=0 - - [servernetwork] - active=0 - port=14747 - ssl_cert=ssl_cert.pem - ssl_key=ssl_key.pem - - [authentication] - method=none - - [database] - type=none - prefix=cockatrice - hostname=localhost - database=servatrice - user=MYUSERNAMEHERE - password=MYSECUREPASSWORDHERE - - [rooms] - method=config - roomlist\size=1 - roomlist\1\name="FIRST ROOM NAME" - roomlist\1\description="FIRST ROOM DESCRIPTION" - roomlist\1\autojoin=true - roomlist\1\joinmessage="THIS IS A JOIN MESSAGE" - roomlist\1\game_types\size=11 - roomlist\1\game_types\1\name="Vintage (T1)" - roomlist\1\game_types\2\name="Legacy (T1.5)" - roomlist\1\game_types\3\name="Extended (T1.X)" - roomlist\1\game_types\4\name="Modern" - roomlist\1\game_types\5\name="Standard (T2)" - roomlist\1\game_types\6\name="Block Constructed" - roomlist\1\game_types\7\name="EDH/Commander" - roomlist\1\game_types\8\name="Highlander" - roomlist\1\game_types\9\name="2HG" - roomlist\1\game_types\10\name="Draft/Sealed" - roomlist\1\game_types\11\name="Other" - - [game] - max_game_inactivity_time=120 - max_player_inactivity_time=15 - - [security] - max_users_per_address=8 - message_counting_interval=10 - max_message_size_per_interval=1000 - max_message_count_per_interval=10 - max_games_per_user=5 - -4. Install the database server (optional) - - 1. `sudo yum -y install mysql-server mysql php-mysql` - - 2. `sudo service mysqld start` - - 3. `sudo chkconfig mysqld on` - - 4. `mysqladmin -u root password ’password’` - - 5. `mysql -u root -ppassword -e "create database servatrice;"` - - 6. `mysql -u root -ppassword -e "create user ’servatrice’@’localhost’ identified by ’password’;"` - - 7. `mysql -u root -ppassword -e "grant all privileges on servatrice.* to ’servatrice’@’localhost’;"` - - 8. `mysql -u servatrice -ppassword servatrice < ../servatrice/servatrice.sql` - - 9. `mysql -u servatrice -ppassword -e "insert into servatrice.cockatrice_users \ (admin,name,password_sha512,active) values (1,’servatrice’,’MYSHA512PASSWORD’,1);"` - - 10. `sed -i.bak -e "s/password=foobar/password=password/" -e "s/type=none/type=mysql/" \ -e "s/method=none/method=sql/"  /servatrice.ini` - -5. Start Servatrice for testing: `/usr/local/bin/servatrice`. I - recommend to run it later within a `screen` session or write an - init-script. - -You should be able to log in as a regular user using any account name. -You can login using the username *servatrice* and the password -*password* as the first moderator. - -If everything succeeded, you should tweak the servatrice.ini and set -your root password to something strong if not already done (at least 8 -characters, upper case, lower case, numbers, special characters). - -[^1]: TODO, dead forum link - -[^2]: TODO, dead forum link diff --git a/doc/usermanual/Usermanual.pdf b/doc/usermanual/Usermanual.pdf deleted file mode 100644 index 3d2d562d..00000000 Binary files a/doc/usermanual/Usermanual.pdf and /dev/null differ diff --git a/doc/usermanual/Usermanual.tex b/doc/usermanual/Usermanual.tex deleted file mode 100644 index 08259c29..00000000 --- a/doc/usermanual/Usermanual.tex +++ /dev/null @@ -1,814 +0,0 @@ -\documentclass[a4paper]{scrbook} -\usepackage[T1]{fontenc} % Fontencoding for pdf searches -\usepackage{textcomp} % EUR symbol for OT and T1 -\usepackage[osf,sc]{mathpazo} % Palatinofont -\usepackage{ellipsis} % better spaces for ellipses (…) -\usepackage{microtype} % Typographic finetuning -\usepackage{fixltx2e} % LaTeX fixes - -\usepackage[euler]{textgreek} -\usepackage[utf8]{inputenc} % utf8 input -\usepackage{graphicx,framed} % pictures, frames -\usepackage{makeidx} % Index -%\usepackage{fancyhdr} -\usepackage{listings} % source listings, e.g. for shell commands - -\usepackage{flafter} % floating objects -\usepackage{placeins} % \FloatBarrier command, http://www.tex.ac.uk/cgi-bin/texfaq2html?label=floats -\usepackage{color} % colors -\usepackage{tikz} % draw stuff -\usepackage{enumerate} % more enumerations -\usepackage{longtable} % multipage tables -\usepackage{booktabs} % better tables: http://www.ctan.org/tex-archive/macros/latex/contrib/booktabs/ - -\usepackage[nottoc]{tocbibind} % Index + Bibliographie -> toc - -%\setcounter{tocdepth}{3} % Depth for tocs; default = 2 - -%PDF stuff -\usepackage[unicode,linktocpage, -colorlinks, -bookmarks, -bookmarksopen, -pdfpagelabels=true]{hyperref} - -% pdf-specific, no line breaks! -\usepackage{xcolor} -\hypersetup{ - pdftitle = {Cockatrice Usermanual}, - pdfsubject = {Cockatrice, Servatrice, Manual}, - pdfauthor = {}, - pdfkeywords = {Cockatrice, Servatrice, Magic}, - linkcolor=blue!30!black, - citecolor=green!30!black, - urlcolor=blue!25!green!25!black, - % do not underline links - frenchlinks, - % break long links - breaklinks = true, -} - -% better Text/Float-ratio -\setcounter{topnumber}{3} -\renewcommand\topfraction{1} -\setcounter{bottomnumber}{3} -\renewcommand\bottomfraction{1} -\setcounter{totalnumber}{9} -\renewcommand\textfraction{.01} -\renewcommand\floatpagefraction{1} - -\makeindex -%opening -\title{Cockatrice} -\subtitle{Usermanual} - -%\pagestyle{fancy} - -\newcommand{\shellcmd}[1]{\texttt{\scriptsize #1}} - -\begin{document} -\maketitle -\tableofcontents - -\chapter{Preface} -This manual is basically a dump from the cockatrice.de dokuwiki. Cockatrice has some legal problems right now and the page is down. -This document tries to save the documentation about the software, beautify and extend it in the future. -Please contribute to the project, it is much too precious to be destroyed. - -\chapter{Getting Started} -\section{Making A User Profile} -Not available anymore, site is down. -If someone runs his own server where you can register a user profile, read his documentation. - -\section{Downloading and Installing the Cockatrice Program} -Due to a legal dispute there are currently no official builds left, so currently you have to build your own binaries. - -\subsection{Building the Client} -\subsubsection{Windows} -To build Cockatrice, we need the Cockatrice sourcecode, its dependencies and build tools of course. -Everything is freely available. - -There should be two ways to compile Cockatrice: -With Microsoft Visual Studio 2010 (Visual C++ 2010) and with MinGW, a minimalist GNU environment for Windows. - -We suggest to use Visual Studio because there is a severe problem with MinGW: -It doesn't offer a compatible version of its tools. -The Qt library needs a certain, old MinGW version\footnote{The officially needed version is MinGW 4.8.2, the last known working version was MinGW 20120426} which is unavailable on the internet because they use an online installer and don't offer old, stable releases. -Trying to build with the current version of MinGW will result in application crashes! -So MinGW and Qt force us to focus on Visual Studio and don't support MinGW. - -Gladly, Microsoft offers Visual Studio Express free of charge, which is a limited but sufficient version of Visual Studio. -It only requires a free of charge registration after 30 days. - -The following howto which uses Visual C++ 2010 Express has been tested with an up to date Windows 7 64Bit. -The resulting build is a 32bit binary, which runs on both 32bit and 64bit systems. - -\paragraph{Prerequisites} -Here is an introduction of all dependencies and tools needed for Cockatrice, followed by a list of downloadlinks in the same order. -\begin{enumerate} - \item The Microsoft Windows SDK for Windows 7 and .NET Framework 4 provides tools and libraries to create Windows applications. - \item The Visual C++ 2010 SP 1 Compiler Update for Windows SDK 7.1 (KB2519277) is a necessary update for the SDK. - \item Microsoft Visual C++ 2010 Express is the actual development environment. - \item Microsoft Visual Studio 2010 Service Pack 1 (VS10sp1-KB983509) is an update for Visual Studio. - \item The Qt libraries 4.8.x for Windows (VS 2010) are the main dependency for Cockatrice. - \item protobuf 2.5.0 is another dependency for Cockatrice (it has no installer, you need to download the zip file with the sourcecode). - \item cmake version 2.8.12.x is needed to create Cockatrice's project files for Visual Studio. Version 3 has not been tested yet with Cockatrice. - \item git is needed to download the latest Cockatrice source code. - \item Nullsoft Scriptable Install System (NSIS 3.0b0) is a program to create the Windows installer for Cockatrice. -\end{enumerate} - -All downloadlinks together: -\footnotesize{\begin{enumerate} - \item \url{http://www.microsoft.com/en-us/download/details.aspx?id=8279} % Win SDK - \item \url{http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=4422} % SDK Update - \item \url{http://go.microsoft.com/?linkid=9709949} % MSVC - \item \url{http://www.microsoft.com/en-us/download/details.aspx?id=23691} % MSVC SP1 - \item \url{http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-opensource-windows-x86-vs2010-4.8.6.exe} - \item \url{http://www.cmake.org/files/v2.8/cmake-2.8.12.2-win32-x86.exe} - \item \url{http://git-scm.com/download/win} - \item \url{http://protobuf.googlecode.com/files/protobuf-2.5.0.zip} - \item \url{http://nsis.sourceforge.net/Download} -\end{enumerate}} - -\paragraph{Installation of the Prerequisites} -Problems will occur if you don't install the first four steps (all the Microsoft SDK and Visual Studio packages) in the exact same order as printed here! -\begin{enumerate} - \item Install Microsoft Windows SDK for Windows 7 and .NET Framework 4. - \item Apply the Visual C++ 2010 SP 1 Compiler Update for Windows SDK 7.1 - \item Install Visual C++ 2010 Express (Start the web installer, you may disable Silverlight) - \item Apply Visual C++ 2010 Express Service Pack 1 - \item Install Qt4 VS 2010 edition. - \item Install NSIS. - \item Install cmake. Choose to add CMake to the System path. - \item Install git. Choose Run Git from Windows Command Prompt. -\end{enumerate} - -As protobuf does neither provide an installer nor the libraries needed for Cockatrice, you have to build those with Visual Studio from the protobuf sources. -The Cockatrice installer will search for the protobuf libraries within the Cockatrice sources, so before unpacking and building protobuf, you need to download the Cockatrice sources first: -\begin{enumerate} - \item Right click into a folder, select Git Gui, then Clone Existing Directory, - \item enter Source Location \url{https://github.com/Cockatrice/Cockatrice} - \item enter target directory (from now on called Cockatrice), click clone and wait until the sources have been downloaded, - \item close Git Gui. -\end{enumerate} - -Now we prepare protobuf: -\begin{enumerate} - \item Create a \shellcmd{build} directory inside the Cockatrice directory. - \item Copy the protobuf-2.5.0.zip into the build directory. - \item Rightclick the zip, choose Extract all. This uses the Windows included zip unpacker to extract the archive. -\end{enumerate} -To be clear: you will (and must) have a Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0 directory hierarchy after that! - -Now the protobuf dependencies for Cockatrice will be built: -\begin{enumerate} - \item Start Visual C++ 2010 Express and from the File menu Open Project/Solution, move to the Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0/vsprojects directory and choose protobuf.sln. - \item Let you guide through the Conversion Wizard, you don't need to create a backup. - \item After the conversion is finished, open the projects (uncheck ``Ask me for every project in this solution'' and click OK). - \item You don't need to look at the conversion report. - \item Select Release or Debug in the toolbar. Release optimizes the code for size and speed, while Debug creates larger, slower binaries, used for development. You need to use the same setting for Cockatrice later! We suggest to use Release. - \item Rightclick on libprotobuf, choose Build. Note that the output should say: Build started: Project libprotobuf, Configuration: Release Win32 - \item Then rightclick on protoc, choose Build. This will build libprotoc and the protoc executable, also as Release Win32. - \item After the build succeeded, close Visual C++. -\end{enumerate} -The Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0/vsprojects/Release directory now contains libprotobuf.dll, libprotobuf.lib and protoc.exe which are needed for Cockatrice. - -\paragraph{Cockatrice Compilation} -Now everything is ready to compile Cockatrice. -\begin{enumerate} - \item Start CMake GUI, locate the Cockatrice directory and locate the Cockatrice/build directory. - Then click Configure and choose Visual Studio 10. - An error will occur during the Configure process because CMake does not know the location of the protobuf library. - \item To satisfy CMake, check Advanced, enter ``protobuf'' in the Search field, - \begin{enumerate} - \item Set PROTOBUF\_INCLUDE\_DIR to Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0/src/ - \item Set PROTOBUF\_LIBRARY to Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0/vsprojects/Release/libprotobuf.lib - \item Set PROTOBUF\_PROTOC\_EXECUTABLE to Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0/vsprojects/Release/protoc.exe - \item Set PROTOBUF\_PROTOC\_LIBRARY to Cockatrice/build/protobuf-2.5.0/protobuf-2.5.0/vsprojects/Release/libprotoc.lib % TODO: Is this needed? I don't think so; but it doesn't hurt either. - \item Click Configure again. - \end{enumerate} - \item Click Generate, then close CMake GUI. The project files have been generated. - \item Start Visual C++ 2010 Express and from the File menu select Open Project/Solution, point to the Cockatrice/build directory and choose Cockatrice.sln. - \item Select Release or Debug in the toolbar exactly as you did for protobuf. We suggest Release. - \item Rightclick on ALL\_BUILD, choose Build. -\end{enumerate} - -\paragraph{Updating your Cockatrice build} -If you just compiled Cockatrice for the first time, you skip this step obviously. -But if you want to update Cockatrice after the source code changed on github, do it like this: -\begin{enumerate} - \item Start Git Bash and update Cockatrice: - \begin{enumerate} - \item \shellcmd{git pull origin master} - \item \shellcmd{Close Git Bash} - \end{enumerate} - \item Start Visual C++ 2010 Express and build the Cockatrice sources again. -\end{enumerate} -Cockatrice has now been updated and built. You may repeat this process every time when the source code changed. - -\paragraph{Cockatrice installation} -To install Cockatrice, you have to create an installer with NSIS now. -Change to the directory \shellcmd{nsis} in the Cockatrice directory, right click the cockatrice.nsi file and select \shellcmd{Compile NSIS Script}. -The NSIS program then creates a file called cockatrice\_win32\_YYYYmmdd\_git-xxxxxxx.exe. This is the complete, redistributable installer for your Cockatrice build. - -Now install Cockatrice by executing the installer. -Note: if you installed Qt in other than the default path, you have to fix the paths in the cockatrice.nsi file; you can edit this file with a text editor. - -We will remove this static NSIS file in the future and let CMake create the NSIS file on the fly. - -\paragraph{Create a card database} -Start the oracle.exe (the installer does this automatically) and let it generate a current cards.xml file: -\begin{enumerate} - \item File $\to$ Download Sets Information $\to$ OK (if there are no MtG sets listed) - \item Check All, Start Download -\end{enumerate} -Congratulations, you may now use Cockatrice! - -\subsubsection{Linux, BSD, OS X} -The following procedures have been tested with Debian Wheezy, Fedora 18, XUbuntu 13.10, FreeBSD 9.1 and 10.0. -If you use Gentoo with KDE you have the needed prerequisites and may continue with downloading the source. -If you use Bodhi or Arch Linux (AUR) or another distribution that includes Cockatrice, you might install Cockatrice from the default packages -- though the package might be old, -so you probably should continue with this howto. - -Before you install new software, you should update your system. The following instructions failed on a fresh installation of Fedora 18 and FreeBSD 9.1 until the systems were updated. -\begin{enumerate} - \item You need to install the build tools and dependencies. This varies between the Linux distributions. - \begin{description} - \item[Debian, Ubuntu and spin-offs] \shellcmd{sudo apt-get install build-essential git libqt4-dev qtmobility-dev libprotobuf-dev protobuf-compiler cmake} - \item[Fedora] \shellcmd{sudo yum groupinstall "Development Tools"\\ - yum install qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake} - \item[FreeBSD 9] \shellcmd{pkg\_add -r qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf} - \item[FreeBSD 10] \shellcmd{pkg install qt4 qt4-linguist qt4-moc qt4-qmake qt4-rcc qt4-uic git cmake protobuf} - \item[OS X] \shellcmd{brew install qt cmake protobuf} - \end{description} - \item Download the sources from github via \\ \shellcmd{cd\\ git clone https://github.com/Cockatrice/Cockatrice.git} - \item To compile the sources, change into the newly created directory, create a build directory and invoke cmake:\\ - \shellcmd{cd Cockatrice \\ -mkdir build \\ -cd build \\ -cmake ..\\ -make}\\ - \item You may install the program into the directory \shellcmd{/usr/local} by typing \shellcmd{sudo make install} but you should also be able to start - cockatrice and the oracle from the build directory. - \item Before you start Cockatrice for the first time, run \shellcmd{oracle -dlsets} and download available cards, then run \shellcmd{cockatrice}. -The default paths for decks, pics, cards and tokens are located in \\ \shellcmd{/home//.local/share/data/Cockatrice/Cockatrice}. -\end{enumerate} - -\subsection{Building the Server} -You don't need your own server if you plan to play only. But as Cockatrice is open source you are free to run your own. -The compilation works like already written above, but instead of invoking \shellcmd{cmake ..}, you have to do it like this: -\begin{itemize} - \item If you want to build the server, use:\\ \shellcmd{cmake -DWITH\_SERVER=1 ..} - \item If you want to build the server, but not the client, use:\\ \shellcmd{cmake -DWITH\_SERVER=1 -DWITHOUT\_CLIENT=1 ..} -\end{itemize} -Further, the server has a dependency on libgcrypt, so you need to install it as well: -\begin{description} - \item[FreeBSD 10] \shellcmd{pkg install libgcrypt} -\end{description} -There is more information on compiling and running Servatrice on CentOS 6 in chapter \ref{servatrice} on page \pageref{servatrice}. - -\section{Downloading Card Database Using the Oracle} -If you are installing Cockatrice for the first time, changing what sets are in your database or even adding the newest set to your database, this tutorial will show you how to do it properly. - -The Oracle will automatically run after the initial setup of Cockatrice. If you would like to re-install your database or add new sets you can find it (for Windows) by clicking the start menu, going to all programs, selecting the Cockatrice folder, and in there you will find the Oracle tool. -\begin{itemize} - \item When the Oracle importer opens, click on “File” in the top left corner and select “Download sets information…” - \begin{center} -\includegraphics[scale=0.8]{pics/fetch554a} - \end{center} - \item This will bring up a box where you can input the URL of a card database. The default address is \url{http://www.cockatrice.de/files/sets.xml} this was an XML file found on the Cockatrice website that has the current set listings for Magic the Gathering. As the page is down, you have to import the file which is distributed with the Cockatrice sources. - This can also be done from the file menu. - \item Select “OK” to load the set listings. - \item A list of all current sets will be brought up. A default selection of sets will automatically be checked. From here you can check or uncheck all sets, or you may only download specific sets that you wish to play with. - \begin{center} -\includegraphics[scale=0.8]{pics/fetchfc3d} - \end{center} -NOTE: If you are playing against someone who is using a card that is not in your database, you will not see a card image or oracle text for that card. Some players like to download all sets to avoid this issue, but other players who only play specific formats (Like \textsc{T2}, \textsc{Standard}, or \textsc{Extended}) wish to keep their database small with only cards they will be using. - \item After you select which sets you wish to download, select “Start download” at the bottom of the Oracle to download the selected sets information. - \item After download is complete, close the Oracle and run Cockatrice. - \item We are now ready to sort our set information in our deck editor. -\end{itemize} - -\section{Editing Set Order and Preference of Card Art} -Many cards have been re-printed in different sets, and in return have different versions of artwork (like the card “Cancel” which can be found in many sets, but has different artwork for each, e.g. \textsc{Zendikar} versus \textsc{M11}: -\begin{center} -\includegraphics{pics/fetchc18b} -\includegraphics{pics/fetche1f4} -\end{center} - -Some players like to have the most current artwork displayed on their cards, while other players have a favorite set they wish to display instead. - -\begin{itemize} - \item Run Cockatrice and select “Deck editor” from the top right Cockatrice menu. This will bring up the Deck Editor along with a list of all cards that are currently in your database which you downloaded using the Oracle Tool. - \item To change what version of the cards will be shown, click on “Card database” on the top left of the Deck editor window, and select “Edit sets…” - \begin{center} -\includegraphics[scale=0.55]{pics/fetchf924} - \end{center} - \item This will bring up a new window that has a list of all sets you currently have downloaded to your database. To change the order of the sets, simply drag and drop them into place. - This will determine which artwork is shown for your cards. If a card is found in multiple sets, whichever set is closest to the top of this list will be the art displayed. - Example: If \textsc{M11} is above \textsc{Zendikar}, The M11 version of the card “Cancel” will be displayed in your Deck editor and Cockatrice games. -\end{itemize} -NOTE: Your opponent will NOT see what artwork you have selected for each of your cards. They will only see what they have selected for their own. - -\section{The Deck Editor / Making a Deck} -The Cockatrice Deck Editor is a tool you can use to make decks to play online. -The cards shown in the Deck Editor are from a database that you downloaded with the Oracle Tool. If you are missing cards or a new set has come out, you must re-run the Oracle and download set information. -\begin{center} -\includegraphics[scale=0.55]{pics/fetch52e0} -\end{center} - -\begin{description} - \item[1. Search Bar] The search bar lets you type in the name of a card and the editor will only show cards that start with whatever you typed in. Example: Typing in ‘B’ will show all cards that start with the letter ‘B’ and typing in ‘Dark’ will show you all cards that start with ‘Dark’ and so on. If you were looking for the card “Sun Titan”, you would not type in ‘Titan’ you would have to type in ‘Sun’ first. Typing in ‘Titan’ will only show you any cards that start with ‘Titan’. - \item[2. Card Search/Filter] The Card search button will bring up a new window that helps you filter out cards more specifically. A variety of check boxes will help find what you need. Card name lets you filter out only cards that have a cretin word in them. Card text can help you find key words like “Haste” or “Infect”. If you were to uncheck all boxes except for “Instant” along with “Artifact” and “U”, the Deck editor will only show you all Blue Instant and Artifact cards. -\begin{center} -\begin{tabular}{ll} -\toprule Letter & Card Type \\ \midrule -U & Blue \\ -W & White \\ -X & Colorless \\ -G & Green \\ -R & Red\\ -B & Black \\ \bottomrule -\end{tabular} -\end{center} - \item[3. Card Data] This section shows the Oracle text for the card that you currently have selected. It will show you up-to-date information on the card such as the Name, Mana cost, Card type, Power/Toughness, and any abilities the card has. It will not show you flavor text. - \item[4. Adding/Removing Card Buttons] -The buttons in the bottom middle will add or remove cards from your Deck List, as well as ad a card specifically to your Sideboard. Having a card selected on the left column and hitting the Enter key will also add it to your deck list. - \item[5. Deck Name/Comments] -The area in the top right lets you name your deck as well as give any comments or descriptions such as how to play the deck, where the deck came from, or explain a theme. Putting something in the “Deck Name” space will NOT be what the file name of the file for your deck. That is spate in the “save” selection under the “Deck” menu found at the top left of the Deck Editor window. - \item[6. Main Deck List] -This area will show you what cards you currently have added to your deck list. It is sorted by card type and also shows you how many of each card and card type you have added, as well as keeps track of how many total cards you have added to your main deck list. This will not add any number of cards you have added to your Sideboard. - \item[7. Sideboard List] -The bottom section of the deck list shows any cards you have added to your sideboard. Again these are split into card types and it will keep track of how many of each card you have as well as how many of each card type and total cards in sideboard. This section will not add any cards from the main deck. Once you have all of your cards added to your deck, you must save it as a file Cockatrice can read. Select “Deck” from the top left corner of the Deck Editor screen, and select “Save Deck” or “Save Deck As…” and it will bring up a new window where you can select where you would like to save your deck, as well as assign it a file name. Cockatrice decks are saved as .cod files. -\end{description} - -\section{Loading a deck list from your clipboard} -If you find a deck online, or you have a deck list saved in a word document, it is easy to transfer it over into a Cockatrice deck file as long as it is in a simple deck list format. -The simple deck list format is a list where each line begins with a number, followed by a whitespace, followed by the cardname, e.g. -\begin{verbatim} -2 Doom Blade -13 Island -10 Swamp -4 Cancel -... -\end{verbatim} - -Simply find the word document or deck list online that you wish to save as a deck, and select the text and copy it to your clipboard. Next, open the Deck Editor screen, and click on the Deck menu from the top left corner. Select “Load deck from clipboard…” and the deck editor will bring up a new window that has the deck list you had copied to your clipboard. Make sure the Deck list looks correct and hit “OK” in the bottom right corner of the window. The Deck editor will now add all the cards in the list to your main deck list. - -\begin{center} -\includegraphics{pics/loaddeck_clip} -\end{center} - -\begin{center} -\includegraphics{pics/okdecklist} -\end{center} - -NOTE: If you add a card to a deck list with this function that you do not have downloaded to your personal cockatrice database though the Oracle tool, the card will take a spot in your main deck list, and count toward the total number of cards, but it will show up as a blank image with no Oracle data or card information. - -\section{Cockatrice Settings} -TODO - -\section{Learning the Ropes / Starting a Solitaire Game} -The best way to get familiar with the way Cockatrice plays is to start a local game that you can play around in by yourself. You could also jump online and start slowly learning, and let other players help you. - -To start a Solo Local game, in the main Cockatrice window, click on “Cockatrice” on the top left, and select “Start local game…”. - -\begin{center} -\includegraphics{pics/fetch2ab8} -\end{center} - -This will bring up a small window that lets you select how many players will be in this local game you are creating. For right now, since we want to do a solo Solitaire game, select one player and hit “OK”. - -\begin{center} -\includegraphics{pics/fetchf010} -\end{center} - -\subsection{Loading a Deck / Using Sideboard} - -This will now bring you to a screen where you load a deck to play with. On the top left part of this screen you will find a button that is labeled “Load Local Deck”. Click that button and it will bring up a window where you can find and select what deck you would like to play with. - -\begin{center} -\includegraphics{pics/fetchf0d2} -\includegraphics[scale=0.8]{pics/fetch55a7} -\end{center} -Select a deck or a .cod file and click “Open”. - -\begin{center} -\includegraphics[scale=0.4]{pics/fetch9b89} -\end{center} - -After the file has loaded you will see all of the cards in that deck laid out on the table. If you hover your mouse over a card, the card image and Oracle info will show on the right side of the screen. If you have a sideboard made for the deck there will be a second section on the table for this sideboard. This screen gives you the ability to double check your deck to make sure it is not only the correct deck you want to play with, but it lets you see that all card images have downloaded properly. If you have cards not showing up at all or they are just blank cards with names on them, you may not have that set downloaded with your Oracle tool. If you have a sideboard, you can drag and drop cards from your main deck to your sideboard or vice-versa. You can do this by clicking and dragging a card to or from your main deck or sideboard. - -NOTE: Moving cards from your main deck to sideboard will NOT change how your deck file is saved, it will only change it temporary for the game you are playing or until you load a new deck. - -When you are satisfied with your deck choice and/or sideboarding options, click on the red outlined “Ready to start” button found a the top of the screen. - -\subsection{Finding Your Way Around} -The main game screenlooks like this -\begin{center} -\includegraphics[scale=0.4]{pics/fetch7cf0} -\end{center} -(Please note your screen will look different due to background image options.) - -\subsubsection{Main Table / Play area} -Split into four areas, this is where all the action will go down. -\begin{description} - \item[The Stack] The area on the left side of the table where Instant and Sorcery cards will be played. This is for things that will only temporarily be put on the table, then into the graveyard. Multiple cards may be added to this area at the same time. Anything on this part of the table will be seen by all players. - \item[Battlefield] This is the soul part of the game table. this is where creatures, enchantments, artifacts, and even plainswalkers will be placed. As cards are moved from your had to the table, they will be aligned to an invisible grid and moved around from there. Tap cards by double clicking them. Anything on this part of the table will be seen by all players. - \item[Land] This space is for land cards, but any card may be placed here. Tap cards here by double clicking them. Anything on this part of the table will be seen by all players. - \item[Hand] Every time you draw a card it will go here to your hand. You may also drag cards from the table back to your hand. Your opponents can not see what is in your hand. -\end{description} - -\subsubsection{Player Info Section} -\begin{center} -\includegraphics[scale=0.7]{pics/fetch0300} -\end{center} - -\begin{description} - \item[Player Avatar] This is a $156\times 60$ pixel JPG image that can be uploaded though the main Cockatrice website. All players in the game room can see this image. It serves nothing more than an online identity for you and other players. - \item[Player Name] Your online name that you picked though the main Cockatrice website. - \item[Life Total] Your in-game life. Using your mouse, if left-clicked will raise this number by one, and if right-clicked lowered by one. There are also keyboard shortcuts to change your life total. - \item[Counters] These seven multicolored circles are used as counters. They can be seen by all players and can be changed by left or right clicking on them to add or subtract a number. Players use them for various digit counting but primarily used for adding and subtracting floating mana produced by card effects. The bottom two white counters can be used for other things like Poison. - \item[Library] This is your deck of cards. The number in the middle reflects how many cards are left in your library. Double clicking the deck lets you draw a card and add it to your hand, you can also drag cards off the top into the battlefield or to your hand. Right-clicking the deck brings up a menu that allows other things to happen like reviling the top number of cards, shuffling, or moving cards directly into the graveyard. - \item[Number of cards in hand] The number in the middle represents how many cards are currently in your hand. Other players can see this number but can not see the cards actually in your hand. - \item[Graveyard] Cards can be dragged and dropped into your graveyard from play or vise-versa, the stack, your hand, or even your library. The number in the middle represents how many cards are currently in your graveyard. Any player may right-click on the graveyard and bring up a menu that shows what cards are in it. - \item[Exile] Cards can be dragged and dropped into exile from play or vise-versa, the stack, your hand, or even your library. The number in the middle represents how many cards are currently in your exile. Any player may right-click on the exile and bring up a menu that shows what cards are in it. -\end{description} - -\subsubsection{Turn Phases} -\begin{center} -\includegraphics[scale=0.7]{pics/fetchfebd} -\end{center} -This bar located on the left most side of the screen represents the 11 steps in a players turn. To go from one phase to the next, you can click on the square of the phase you want to move to, or you can hit Ctrl+space to move down to the next. Some phases even have their own keyboard shortcut. Going from one phase to the next does not actually do anything to your or your cards, it is only a place marker for your opponents to see and keep up with what you are doing in your turn. For example, clicking to the “Draw Phase” will not automatically draw you a card. It is customary for a player to end their turn on the “End of turn step” and let their opponent hit the “next turn” button. This is a courtesy for other players if they wish to do something like use an instant at the end of your turn, or in response to something you did. - -NOTE: Players sometimes use the term EOT which stands for “End Of Turn”. This is to let other players know they are doing something in response to the end of the current turn. - -\subsubsection{Info/Chat Bar} -\begin{center} -\includegraphics[scale=0.8]{pics/fetcha0de} -\end{center} -Split into three sections, the Info/Chat bar lets you see a close-up image of the card your mouse was last over, as well as gives you the card info for that card. At the bottom of this bar there is a chat log that helps keep track of events during the game as well as lets you communicate with other players. if a card is placed on the table, pointed at, or tapped it will get noted in the chat log as well as has a link to the card that you can hover over and see an image of at the top of the bar. - -\subsection{Basic Functions} -\subsubsection{Rolling Dice} -At the beginning of a game players decide who is going first by rolling a 20 sided die. In Cockatrice we do this by pressing Ctrl-I and hitting enter. Hitting Ctrl-I brings up a die window and lets you select how many sides you want on your die. Default is 20, and pressing enter will “roll” the die. This action will show up in the cat log at on the bottom right of the screen. You can also find this in the “game” menu at the top of the window, selecting “player” and clicking on “roll die…” -\begin{center} - \includegraphics{pics/fetch7486}\\ - \includegraphics{pics/fetch3705} -\end{center} - -\subsubsection{Draw Cards / Mulligan} -When a game starts and the first player has been selected, all players will draw seven cards. this can be done by pressing Ctrl-M. Seven cards will go from your library to your hand. Pressing Ctrl-M again will put the seven cards from your hand back into your library, shuffle your library and deal out six new cards to you. Each time you press Ctrl-M it will give you one less card until you get down to one card, then it will re-start at seven cards. This function can be found by clicking the “game” menu on the top of the window, selecting “player” then selecting “hand” and then “take Mulligan”. If you are playing a friendly game, press Ctrl-M as normal, but then press Ctrl-D to draw cards until you have a total of seven again. -\begin{center} -\includegraphics[scale=0.5]{pics/fetchfe20} -\end{center} - - -\subsubsection{Tapping} -Tapping cards is very basic. If a card is on the table under your control, you can double click it to tap it and then double click again to untap it. You can select multiple cards on the table by clicking and dragging your mouse, then tap or untap all of the selected cards at the same time. Other players can not tap or untap your cards. Pressing Ctrl-U will untap everything you control. - -\begin{itemize} - \item Untapped - \begin{center} - \includegraphics{pics/fetchb6fe} - \end{center} - - \item Tapped - \begin{center} - \includegraphics{pics/fetch867f} - \end{center} -\end{itemize} - -\subsubsection{Attaching Cards to Cards} -Sometimes an Enchantment -Aura or Equipment cards need to be attached to other cards that are already on the table. simply put the enchantment or equipment on the table. Right-click the card and select “attach” (this can also be done with Ctrl-A). A green arrow will appear, point and click on the card you wish to attach. You can also attach cards to other people's cards. -\begin{center} - \includegraphics{pics/fetch100e}\\ - \includegraphics{pics/fetchb17a} -\end{center} - - -\subsubsection{Changing Power/Toughness} -Enchantments, Equipment, and other effects sometimes change a creatures power or toughness. This can be done by right-clicking the card, and selecting “power / toughness” then selecting which one you wish to do. Other players can not change your creatures power and toughness. This can also be done though a series of keyboard shortcuts seen below. - -\begin{center} -\begin{tabular}{ll} -(Select card) Ctrl++ & Increase power \\ -(Select card) Ctrl+- & Decrease power \\ -(Select card) Alt++ & Increase toughness \\ -(Select card) Alt+- & Decrease toughness \\ -(Select card) Ctrl+Alt++ & Increase power and toughness \\ -(Select card) Ctrl+Alt+- & Decrease power and toughness -\end{tabular} -\includegraphics[scale=0.5]{pics/fetche3fc}\\ -\includegraphics{pics/fetchd922} -\end{center} - -\subsubsection{Adding Counters to Cards} -Sometimes Counters are needed to be placed on cards that the counters on the side of the screen are not able to track. Cockatrice offers three different counter color options, Red, Green, and Yellow. Although there is no set standard on what color stands for what, it is mostly player preference. Green could be used for +1/+1, red -1/-1, leaving yellow for charge and quest counters, this is not a set rule. Adding counters is as simple as right clicking on the card you wish to add counters too, and currently there is no keyboard shortcut for this process. Removing counters is the same process, right click and select remove. Other players can not add or remove counters to or from your cards. - -\begin{center} -\includegraphics{pics/fetch5170} -\end{center} -(One of each counter) - -\subsubsection{Pointing at Cards / Arrows} -Pointing at cards is needed for resolving spells, or declaring attackers and blockers. All you need to do is right-click over a card and drag an arrow over to what you are pointing at. Permanents, spells in the stack, and even a players life total can be pointed at. You can point at your opponents cards and life total, and they can point at yours. When your arrows are no loner needed, press Ctrl-R to remove them from the screen. - -\begin{center} -\includegraphics[scale=0.5]{pics/fetch98fd} \\ -\includegraphics[scale=0.5]{pics/fetch74b2} -\end{center} - -\subsubsection{Creating Tokens} -Creating tokens can sometimes be tedious, but is well worth the effort to keep a clean and organized game. Pressing Ctrl-T will bring up a small window to assist you in creating a token. Simply enter the name of the token you are creating, select its color, and give it a power and toughness (\#/\#). You can also bring up this token window by selecting “game” from the top menu, selecting “Player” then clicking on “Create Token…”. A copy of the Last token made can be done by pressing *Ctrl+G or right-clicking on a already made token (or any card on the table) and selecting “clone” or pressing Ctrl-H**. When a token or clone leaves play, it will be destroyed and vanish. - -\begin{center} -\includegraphics[scale=0.5]{pics/fetch2c36} \\ -\includegraphics[scale=0.5]{pics/fetch9bff} \\ -\includegraphics[scale=0.5]{pics/fetche6b2} \\ -\includegraphics[scale=0.5]{pics/fetch84a2} -\end{center} - -Make copies of your last token by pressing Ctrl-G. -\begin{center} -\includegraphics[scale=0.5]{pics/fetch6847} -\end{center} - -\chapter{Playing Online} - With Cockatrice you will most likely play Magic games over the Internet with real people all around the world. In order to help maintain a pleasant environment for users, please read the messages below: - -\begin{itemize} - \item User Code of Conduct\footnote{TODO, dead forum link} -- Must Read for all Users - \item How to Report Abuse\footnote{TODO, dead forum link} -- It is recommended to read this as well -\end{itemize} - -\section{Connect to Server} -To connect to the Cockatrice server, launch the Cockatrice program, go to the “Cockatrice” menu at the top left, and select “Connect”. A window will appear (see image below). -\begin{center} -\includegraphics[scale=0.5]{pics/fetch23f3} -\end{center} -If you have registered with Cockatrice, then enter your Username in the “Player Name” field and your password in the “Password” field then click “OK”. You may check the “Remember Password” box if you wish. If you do, then the next time “Connect” is selected from the “Cockatrice” menu, the window that appears will already have your Username and Password already filled. Please take this into consideration if you share a computer with other people, seeing that you are responsible for anything that happens on the server with your username (As noted here). If you did not register with Cockatrice, then simply fill in the Username with whatever you like and click “OK”. If you would like to become a registered user, read the instructions from the server's website. - -Once you are connected to the server, more tabs will appear at the top of the screen next to the “Deck” tab that you are already on. - -\section{All About Games} -This page is about creating, joining, watching, and searching for games on the Cockatrice Server. In order to participate in any games, you have to be connected to the server. The games on the server are where all of the action take place. There will be many games happening on the server at the same time. Basically, first a game is created by a player (it could be you). Then other players join the game until the number of players reaches the number set by that game's creator. When the game has no players participating in it, the game disappears. -Creating a Game - -To Create a game, go to the “MTG room” tab. Click on the “Create” button below the Games list. A window will appear (see below). -\begin{center} -\includegraphics[scale=0.5]{pics/fetch54df} -\end{center} - -Here are all of the options for creating a game: -\begin{description} - \item[Description] Describe the game in your own words (i.e. “Competitive Standard”, “Casual EDH- No Infinites”, “RavnicaDraft”, “Here is Chris”) - \item[Players] Specify the number of Players in the game. This cannot be changed after the game is created. The game can only begin when the specified number of players join. - \item[Spectators] Spectators are users that are in a game, but they are not one of the players. Spectators can see all of the public zones of the game and everything displayed in the Info/Chat Bar. Any number of users can join a game as a Spectator (as long as the “Spectators Allowed” box is checked). - \begin{description} - \item[Spectators Allowed] Unchecking this box will prohibit any/all users from joining the game as a Spectator. - \item[Spectators Need a Password to Join] Checking this box will make it so that in order for a user to join as a Spectator, they need to type the password you specify in the Password Field. - \item[Spectators can Chat] Checking this box will allow Spectators to type comments in the Chat bar during the game. - \item[Spectators See Everything] Checking this box will allow Spectators to view cards in all private zones of all players (hands, libraries, face-down cards). - \end{description} - \item[Password Field] If you type anything in this field, a Player (or Spectator if the “Spectators Need Password” box is checked) will need to type the exact same thing you typed in order to join the game (and it is case-sensitive). - \item[Only Buddies Can Join] Checking this box will prevent any user who is not in your Buddy List from joining the game as a Player or Spectator. NOTE: Your username is not on your buddy list. If you leave a game you created, and this box is checked, you will not be able to rejoin. - \item[Only Registered Users Can Join] Checking this box will prevent anyone who has not registered on the Cockatrice website from joining as a Player or Spectator. - \item[Game Type] These check boxes have no effect on the game. They inform other Users browsing the Games list of what format your game is. Users can choose to view only games of a certain Type/Format. (\url{http://en.wikipedia.org/wiki/Magic:_The_Gathering_formats}) -\end{description} - -\subsection{Joining a Game} -Most of the time, to join a game you click on the “MTG Room” tab, click on a game in the Games list, then click Join. -If the Game's creator specified a password then you will have to type that password in a small window that appears after you click Join (the password is case-sensitive). -If your User Profile meets the criteria of the Game's creator then a new tab will open with that game. -There is also an easy way to join a game in which a User in your Buddy List is playing. -Go to the “User Lists” tab, right click any Username from the Buddies Online list (at the left of the window) to make a menu appear, and select “Show this user's games”. -A window will open with a list of games that the User is either playing or watching. In the same manner as described with the “MTG Room” tab, simply click on a game and click Join. -To watch a game, the instructions are the same except that you click the “Join as Spectator” button instead of the Join button. -NOTE: If you are a player in a game and you wish to become a spectator in that game, you must first leave the game then rejoin as a Spectator. Same thing if you are a Spectator and wish to play. - -\subsection{Searching for Games} -The Games list in the “MTG Room” tab displays by default all games that have not reached the specified number of players. The “Filter Games” button makes looking through this list easier if you are looking to join a particular kind of game. When this button is selected, a window appears (see below). -\begin{center} -\includegraphics[scale=0.5]{pics/fetchd30e} -\end{center} -\begin{description} - \item[Game Description] Displays games with certain descriptions. You can even search partial names. - \item[Creator] Displays games created by Users with that username. It even searches for partial names. - \item[Player Count] Displays all games where the specified number of players is greater-than or equal to the “at least” number and less-than or equal to the “at most” number. For instance, setting both numbers to 3 will display all games whose creators made as 3-player games. - \item[Show Unavailable Games] Checking this box will display games that are full and in progress. You can still join these games as a Spectator if the game's creator allows it. - \item[Game Types] Displays games with the selected types. Bear in mind that the Cockatrice software does not enforce deck construction for formats. So just because a game's type is EDH/Commander, doesn't necessarily mean that is what's being played in the game. Players can agree to switch formats in a game. -\end{description} - -\section{Keeping Track of Buddies} - - -Cockatrice allows registered users to keep track of other registered users in a buddy list. You won't be able to do anything with this list (or other registered users at all) unless you are a registered user and connected to the server. - -To add a User to your Buddy list, right-click their username and, in the menu that appears, select “Add to buddy list”. You can right-click and add a User in this manner anywhere you see their username (under the “User lists” tab, the “MTG Room” tab, in a game, or in a direct chat). When you add a User, their username will appear in the list “Buddies Online” located under the “User lists” tab. If the username appears in a light shade of gray, then that User is not connected to the server. If it appears in black, then that User is connected to the server. - -If you see that one of your Buddies is connected, you can see the games he/she is currently in. Right-click their username, and in the menu that appears select “Show this user's games”. A window will appear from which you can watch or play in a game that your buddy is currently in. - -You can Direct Chat with a Buddy (or any user for that matter) by right-clicking their username and, from the menu that appears, selecting “Direct Chat”. When you do this, a new tab will open (both on your window and on the other User's window) containing a chat room that can only be seen and used by the two of you. The place where you type in your messages is at the very bottom of the window. - -To remove a User from your Buddy list, right-click their username, and from the menu that appears select “Remove from buddy list”. - -\section{Dealing with and Preventing Unpleasantries} -Make a screenshot, there are several free programs available. -Right-Click a username to add to Ignore List. - -\chapter{Frequently Asked Questions (FAQ)} -\section{How to update the card database} -When a new set comes out, you need to update your card database to be able to see the new cards in the deck editor and use them in the game. To do this, open the Oracle tool in the Cockatrice folder. It's a stand-alone program and is run directly out of the folder without running Cockatrice. - -When the Oracle tool is running, open the menu and select 'Download sets information'. Use the suggested address and click OK. After at most a few seconds, you should be presented a list of sets to be downloaded. Do not uncheck the ones you already have: they should be downloaded again. Click 'Start download' and wait. When the process is finished, the database should be up to date. - -\section{Change Card Art / Custom Card Art} - Is it possible to change the card art for a specif card, not by changing the set order in my deck editor? - -YES! It is actually very simple. - -All you have to do is get a .JPG of the card you wish to change and name the actual file of the image to the cards (exact) name and add ”.Full” to the end. You can then take this new card image and replace the old one in your downloaded image folder. - -NOTE: Image size does not matter. Try to use a high resolution .JPG of the card you want to use for best quality. - -EXAMPLE: Let's say you wish to turn your M11 Sun Titan from its original art to a promo version. - -\begin{center} - \includegraphics[scale=0.5]{pics/fetch74e3} -\end{center} -If you are using Windows 7, you can find the default location for the Cockatrice card art files under \shellcmd{C:\textbackslash Program Files (x86)\textbackslash Cockatrice\textbackslash pics\textbackslash downloadedPics} Once there, look for the folder of the set for the card we are replacing. For this example, Sun Titan is in the M11 folder. Open the folder and paste/replace your new “Promo” Sun Titan.full[.JPG] into this folder, discarding the original copy. (If you have not yet downloaded the original image of a card by selecting it in the deck editor or playing it in an online game, the card image will not yet be there) - -Once you have the new card image in the proper folder, you can now start Cockatrice (Or re-start if you already had the program open)and select the deck editor to see your new card image. - -If you are changing the art of a card that has multiple versions in other sets, make sure you put the new card in whatever folder (or set) is highest on your card database set list. - -NOTE: Other players on Cockatrice will NOT see your new card image. They will only see whatever version of the card is in their database. - -\section{Use Higher Resolution Cards} -Can you get better/higher resolution card art than the default downloaded card images already used? - -Yes! - -When you click on a card for the first time in the Deck Editor, Cockatrice goes onto the internet and finds an image of that card from a database on another website. - -If you find a higher resolution .JPG of a card that you wish to use on your Cockatrice, you can replace the image with no problem. If you can find a card image in the 3,000 by 1,000 pixel range, and save it as the cards (exact) name and add .Full to the end. - -If you are using Windows 7, you can find the default location for the Cockatrice card art files under \shellcmd{C:\textbackslash Program Files (x86)\textbackslash Cockatrice\textbackslash pics\textbackslash downloadedPics}. Once there, look for the folder of the set for the card you are replacing. (If you are not running Cockatrice as an administrator, look in \shellcmd{C:\textbackslash Users\textbackslash username\textbackslash AppData\textbackslash Local\textbackslash VirtualStore\textbackslash Program Files (x86)\textbackslash Cockatrice\textbackslash pics\textbackslash downloadedPics}.) - -Open the folder of the set with the card, and paste/replace the new higher resolution .JPG image with the old low quality one. - -This process is the same for changing cards to custom images. - -NOTE: Other Cockatrice players will not see your higher resolution card art, they will only see the image they have in their own database. - -\section{Booster drafts} - Cockatrice does not support booster drafts, so players need to use an external service for this. - -Most people use \url{http://ccgdecks.com/} so it's probably not a bad idea to register an account there before joining a game in Cockatrice. - -\section{What is Legacy / Vintage / EDH?} -See \url{http://en.wikipedia.org/wiki/Magic:_The_Gathering_formats}. - -\section{Linking cards and URLs in the Cockatrice chat.} -The Cockatrice chat supports linking of cards and URLs by use of certain tags around a word or phrase. - -\subsection{How to link a card in the Cockatrice chat} -To link a card in the Cockatrice chat, type out the full name of the card, surrounded by the [card] and [/card] tags. - -For example: -\begin{verbatim} -[card]Black Lotus[/card] -\end{verbatim} - -\subsection{How to link a URL in the Cockatrice chat} -To link a URL in the Cockatrice chat, type out the url, surrounded by the [url] and [/url] tags. - -For example: -\begin{verbatim} -[url]http://www.cockatrice.de[/url] -[url]cockatrice.de[/url] -\end{verbatim} - - -\chapter{Servatrice on CentOS 6} -\label{servatrice} -This Howto from woogerworks.com will help you to run your own Cockatrice server (called Servatrice). -An installation script can be downloaded at: \url{http://www.woogerworks.com/cockatrice/installatrice} - -BEFORE CONTINUING, PLEASE UNDERSTAND THESE ARE THE VERY MOST BASIC STEPS. -THIS WILL NOT CONFIGURE SECURITY SETTINGS (YOU ARE RESPONSIBLE FOR YOUR OWN SECURITY)! -THIS WILL NOT CONNECT THE SERVER TO A DATABASE! -USE AT YOUR OWN RISK. - -\begin{enumerate} - \item Open a command shell and install the prerequisites -\begin{enumerate} - \item \shellcmd{cd /etc/yum.repos.d/} - \item \shellcmd{sudo wget http://kdeforge.unl.edu/apt/kde-redhat/epel/kde.repo} - \item \shellcmd{yum -y groupinstall "development tools"} - \item \shellcmd{rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86\_64/epel-release-6-8.noarch.rpm} - \item \shellcmd{yum -y install qt-mysql qt-devel qt-mobility-devel protobuf-devel protobuf-compiler cmake28 libgcrypt-devel} - \item \shellcmd{cd} -\end{enumerate} - - \item Download the source code: -\begin{enumerate} - \item \shellcmd{git clone https://github.com/Cockatrice/Cockatrice} - \item \shellcmd{cd Cockatrice} - \item \shellcmd{mkdir build} - \item \shellcmd{cd build} - \item \shellcmd{cmake28 -DWITH\_SERVER=1 ..} - \item \shellcmd{make} - \item \shellcmd{sudo make install} -\end{enumerate} - - \item Create a servatrice.ini file. There is an example file you can convert: -\shellcmd{sed -e "s/number\_pools=1/number\_pools=0/" ../servatrice/servatrice.ini.example > \textasciitilde/servatrice.ini} -Here is one example file: -\begin{framed} -\begin{verbatim} -[server] -port=4747 -statusupdate=15000 -logfile=server.log -name="My own Cockatrice server" -id=1 -number_pools=0 - -[servernetwork] -active=0 -port=14747 -ssl_cert=ssl_cert.pem -ssl_key=ssl_key.pem - -[authentication] -method=none - -[database] -type=none -prefix=cockatrice -hostname=localhost -database=servatrice -user=MYUSERNAMEHERE -password=MYSECUREPASSWORDHERE - -[rooms] -method=config -roomlist\size=1 -roomlist\1\name="FIRST ROOM NAME" -roomlist\1\description="FIRST ROOM DESCRIPTION" -roomlist\1\autojoin=true -roomlist\1\joinmessage="THIS IS A JOIN MESSAGE" -roomlist\1\game_types\size=11 -roomlist\1\game_types\1\name="Vintage (T1)" -roomlist\1\game_types\2\name="Legacy (T1.5)" -roomlist\1\game_types\3\name="Extended (T1.X)" -roomlist\1\game_types\4\name="Modern" -roomlist\1\game_types\5\name="Standard (T2)" -roomlist\1\game_types\6\name="Block Constructed" -roomlist\1\game_types\7\name="EDH/Commander" -roomlist\1\game_types\8\name="Highlander" -roomlist\1\game_types\9\name="2HG" -roomlist\1\game_types\10\name="Draft/Sealed" -roomlist\1\game_types\11\name="Other" - -[game] -max_game_inactivity_time=120 -max_player_inactivity_time=15 - -[security] -max_users_per_address=8 -message_counting_interval=10 -max_message_size_per_interval=1000 -max_message_count_per_interval=10 -max_games_per_user=5 -\end{verbatim} -\end{framed} - - \item Install the database server (optional) -\begin{enumerate} - \item \shellcmd{sudo yum -y install mysql-server mysql php-mysql} - \item \shellcmd{sudo service mysqld start} - \item \shellcmd{sudo chkconfig mysqld on} - \item \shellcmd{mysqladmin -u root password 'password'} - \item \shellcmd{mysql -u root -ppassword -e "create database servatrice;"} - \item \shellcmd{mysql -u root -ppassword -e "create user 'servatrice'@'localhost' identified by 'password';"} - \item \shellcmd{mysql -u root -ppassword -e "grant all privileges on servatrice.* to 'servatrice'@'localhost';"} - \item \shellcmd{mysql -u servatrice -ppassword servatrice < ../servatrice/servatrice.sql} - \item \shellcmd{mysql -u servatrice -ppassword -e "insert into servatrice.cockatrice\_users \textbackslash\\ (admin,name,password\_sha512,active) values (1,'servatrice','MYSHA512PASSWORD',1);"} - \item \shellcmd{sed -i.bak -e "s/password=foobar/password=password/" -e "s/type=none/type=mysql/" \textbackslash\\ -e "s/method=none/method=sql/" ~/servatrice.ini} -\end{enumerate} - - \item Start Servatrice for testing: \shellcmd{/usr/local/bin/servatrice}. I recommend to run it later within a \shellcmd{screen} session or write an init-script. -\end{enumerate} -You should be able to log in as a regular user using any account name. -You can login using the username \textit{servatrice} and the password \textit{password} as the first moderator. - -If everything succeeded, you should tweak the servatrice.ini and set your root password to something strong if not already done (at least 8 characters, upper case, lower case, numbers, special characters). - -%\listoffigures -%\listoftables -%\printindex - -\end{document} diff --git a/doc/usermanual/pics/fetch0300.jpg b/doc/usermanual/pics/fetch0300.jpg deleted file mode 100644 index 90bdcf44..00000000 Binary files a/doc/usermanual/pics/fetch0300.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch100e.jpg b/doc/usermanual/pics/fetch100e.jpg deleted file mode 100644 index 00e9c9ad..00000000 Binary files a/doc/usermanual/pics/fetch100e.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch23f3.jpg b/doc/usermanual/pics/fetch23f3.jpg deleted file mode 100644 index eccdc58e..00000000 Binary files a/doc/usermanual/pics/fetch23f3.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch2ab8.jpg b/doc/usermanual/pics/fetch2ab8.jpg deleted file mode 100644 index fdb4e61e..00000000 Binary files a/doc/usermanual/pics/fetch2ab8.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch2c36.jpg b/doc/usermanual/pics/fetch2c36.jpg deleted file mode 100644 index d6455779..00000000 Binary files a/doc/usermanual/pics/fetch2c36.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch3705.jpg b/doc/usermanual/pics/fetch3705.jpg deleted file mode 100644 index e0262bd1..00000000 Binary files a/doc/usermanual/pics/fetch3705.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch5170.jpg b/doc/usermanual/pics/fetch5170.jpg deleted file mode 100644 index 23b04704..00000000 Binary files a/doc/usermanual/pics/fetch5170.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch52e0.jpg b/doc/usermanual/pics/fetch52e0.jpg deleted file mode 100644 index 73548870..00000000 Binary files a/doc/usermanual/pics/fetch52e0.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch54df.jpg b/doc/usermanual/pics/fetch54df.jpg deleted file mode 100644 index a6d990ae..00000000 Binary files a/doc/usermanual/pics/fetch54df.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch554a.jpg b/doc/usermanual/pics/fetch554a.jpg deleted file mode 100644 index 95e3c39d..00000000 Binary files a/doc/usermanual/pics/fetch554a.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch55a7.jpg b/doc/usermanual/pics/fetch55a7.jpg deleted file mode 100644 index a16ac41a..00000000 Binary files a/doc/usermanual/pics/fetch55a7.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch6847.jpg b/doc/usermanual/pics/fetch6847.jpg deleted file mode 100644 index 3e489efb..00000000 Binary files a/doc/usermanual/pics/fetch6847.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch7486.jpg b/doc/usermanual/pics/fetch7486.jpg deleted file mode 100644 index 30cbf1a8..00000000 Binary files a/doc/usermanual/pics/fetch7486.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch74b2.jpg b/doc/usermanual/pics/fetch74b2.jpg deleted file mode 100644 index 019f1e99..00000000 Binary files a/doc/usermanual/pics/fetch74b2.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch74e3.jpg b/doc/usermanual/pics/fetch74e3.jpg deleted file mode 100644 index ddfaef2e..00000000 Binary files a/doc/usermanual/pics/fetch74e3.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch7cf0.jpg b/doc/usermanual/pics/fetch7cf0.jpg deleted file mode 100644 index 342474b1..00000000 Binary files a/doc/usermanual/pics/fetch7cf0.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch84a2.jpg b/doc/usermanual/pics/fetch84a2.jpg deleted file mode 100644 index bbf941db..00000000 Binary files a/doc/usermanual/pics/fetch84a2.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch867f.jpg b/doc/usermanual/pics/fetch867f.jpg deleted file mode 100644 index 2b11b724..00000000 Binary files a/doc/usermanual/pics/fetch867f.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch98fd.jpg b/doc/usermanual/pics/fetch98fd.jpg deleted file mode 100644 index 36b3f918..00000000 Binary files a/doc/usermanual/pics/fetch98fd.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch9b89.jpg b/doc/usermanual/pics/fetch9b89.jpg deleted file mode 100644 index 424f449b..00000000 Binary files a/doc/usermanual/pics/fetch9b89.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetch9bff.jpg b/doc/usermanual/pics/fetch9bff.jpg deleted file mode 100644 index 8334c313..00000000 Binary files a/doc/usermanual/pics/fetch9bff.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetcha0de.jpg b/doc/usermanual/pics/fetcha0de.jpg deleted file mode 100644 index 6f756f76..00000000 Binary files a/doc/usermanual/pics/fetcha0de.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchb17a.jpg b/doc/usermanual/pics/fetchb17a.jpg deleted file mode 100644 index 9953a254..00000000 Binary files a/doc/usermanual/pics/fetchb17a.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchb6fe.jpg b/doc/usermanual/pics/fetchb6fe.jpg deleted file mode 100644 index bcafcb28..00000000 Binary files a/doc/usermanual/pics/fetchb6fe.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchc18b.jpg b/doc/usermanual/pics/fetchc18b.jpg deleted file mode 100644 index 84374138..00000000 Binary files a/doc/usermanual/pics/fetchc18b.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchd30e.jpg b/doc/usermanual/pics/fetchd30e.jpg deleted file mode 100644 index 63dc6961..00000000 Binary files a/doc/usermanual/pics/fetchd30e.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchd922.jpg b/doc/usermanual/pics/fetchd922.jpg deleted file mode 100644 index 36836357..00000000 Binary files a/doc/usermanual/pics/fetchd922.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetche1f4.jpg b/doc/usermanual/pics/fetche1f4.jpg deleted file mode 100644 index 456d7746..00000000 Binary files a/doc/usermanual/pics/fetche1f4.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetche3fc.jpg b/doc/usermanual/pics/fetche3fc.jpg deleted file mode 100644 index 565d8737..00000000 Binary files a/doc/usermanual/pics/fetche3fc.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetche6b2.jpg b/doc/usermanual/pics/fetche6b2.jpg deleted file mode 100644 index ea64940b..00000000 Binary files a/doc/usermanual/pics/fetche6b2.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchf010.jpg b/doc/usermanual/pics/fetchf010.jpg deleted file mode 100644 index 28edbbaf..00000000 Binary files a/doc/usermanual/pics/fetchf010.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchf0d2.jpg b/doc/usermanual/pics/fetchf0d2.jpg deleted file mode 100644 index 2046c0e0..00000000 Binary files a/doc/usermanual/pics/fetchf0d2.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchf924.jpg b/doc/usermanual/pics/fetchf924.jpg deleted file mode 100644 index 494c1f42..00000000 Binary files a/doc/usermanual/pics/fetchf924.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchfc3d.jpg b/doc/usermanual/pics/fetchfc3d.jpg deleted file mode 100644 index 879c9c05..00000000 Binary files a/doc/usermanual/pics/fetchfc3d.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchfe20.jpg b/doc/usermanual/pics/fetchfe20.jpg deleted file mode 100644 index c5b1730e..00000000 Binary files a/doc/usermanual/pics/fetchfe20.jpg and /dev/null differ diff --git a/doc/usermanual/pics/fetchfebd.jpg b/doc/usermanual/pics/fetchfebd.jpg deleted file mode 100644 index f44cbbe6..00000000 Binary files a/doc/usermanual/pics/fetchfebd.jpg and /dev/null differ diff --git a/doc/usermanual/pics/loaddeck_clip.jpg b/doc/usermanual/pics/loaddeck_clip.jpg deleted file mode 100644 index 5c0da095..00000000 Binary files a/doc/usermanual/pics/loaddeck_clip.jpg and /dev/null differ diff --git a/doc/usermanual/pics/okdecklist.jpg b/doc/usermanual/pics/okdecklist.jpg deleted file mode 100644 index 306fe625..00000000 Binary files a/doc/usermanual/pics/okdecklist.jpg and /dev/null differ diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 413928ea..08fdfabb 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -13,6 +13,7 @@ SET(oracle_SOURCES src/oracleimporter.cpp ../cockatrice/src/carddatabase.cpp ../cockatrice/src/settingscache.cpp + ../cockatrice/src/shortcutssettings.cpp ../cockatrice/src/thememanager.cpp ../cockatrice/src/qt-json/json.cpp ) @@ -126,6 +127,8 @@ ENDIF() # Build oracle binary and link it ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_QM} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS}) +set_property(TARGET oracle PROPERTY CXX_STANDARD 11) +set_property(TARGET oracle PROPERTY CXX_STANDARD_REQUIRED ON) if(Qt4_FOUND) if(MSVC) @@ -152,6 +155,7 @@ if(UNIX) set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) + set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) INSTALL(TARGETS oracle BUNDLE DESTINATION ./) INSTALL(FILES ${oracle_QM} DESTINATION ./oracle.app/Contents/Resources/translations) @@ -228,4 +232,4 @@ Translations = Resources/translations\") include(BundleUtilities) fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\") " COMPONENT Runtime) -endif() \ No newline at end of file +endif() diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 84e742a8..8eef9a2f 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -65,39 +65,37 @@ CardInfo *OracleImporter::addCard(const QString &setName, const QString &cardPT, int cardLoyalty, const QString &cardText, - const QStringList & colors) + const QStringList & colors, + const QStringList & relatedCards, + bool upsideDown + ) { QStringList cardTextRows = cardText.split("\n"); - bool splitCard = false; - if (cardName.contains('(')) { - cardName.remove(QRegExp(" \\(.*\\)")); - splitCard = true; - } + // Workaround for card name weirdness - if (cardName.contains("XX")) - cardName.remove("XX"); cardName = cardName.replace("Æ", "AE"); cardName = cardName.replace("’", "'"); - // Remove {} around mana costs - cardCost.remove(QChar('{')); - cardCost.remove(QChar('}')); - - CardInfo *card; + CardInfo * card; if (cards.contains(cardName)) { card = cards.value(cardName); - if (splitCard && !card->getText().contains(cardText)) - card->setText(card->getText() + "\n---\n" + cardText); } else { + // Remove {} around mana costs + cardCost.remove(QChar('{')); + cardCost.remove(QChar('}')); + + // detect mana generator artifacts bool mArtifact = false; if (cardType.endsWith("Artifact")) for (int i = 0; i < cardTextRows.size(); ++i) if (cardTextRows[i].contains("{T}") && cardTextRows[i].contains("to your mana pool")) mArtifact = true; - + + // detect cards that enter the field tapped bool cipt = cardText.contains("Hideaway") || (cardText.contains(cardName + " enters the battlefield tapped") && !cardText.contains(cardName + " enters the battlefield tapped unless")); - card = new CardInfo(this, cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, cardLoyalty, cipt); + // insert the card and its properties + card = new CardInfo(this, cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, relatedCards, upsideDown, cardLoyalty, cipt); int tableRow = 1; QString mainCardType = card->getMainCardType(); if ((mainCardType == "Land") || mArtifact) @@ -147,101 +145,149 @@ int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) QString cardPT; QString cardText; QStringList colors; + QStringList relatedCards; int cardId; int cardLoyalty; - bool cardIsToken = false; + bool upsideDown = false; QMap splitCards; - while (it.hasNext()) { + while (it.hasNext()) + { map = it.next().toMap(); - if(0 == QString::compare(map.value("layout").toString(), QString("split"), Qt::CaseInsensitive)) + + QString layout = map.value("layout").toString(); + + if(layout == "token") + continue; + + if(layout == "split") { - // Split card handling + // Enqueue split card for later handling cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; - if(splitCards.contains(cardId)) - { - // merge two split cards - QVariantMap tmpMap = splitCards.take(cardId); - QVariantMap * card1 = 0, * card2 = 0; - // same cardid - cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; - // this is currently an integer; can't accept 2 values - cardLoyalty = 0; - - // determine which subcard is the first one in the split - QStringList names=map.contains("names") ? map.value("names").toStringList() : QStringList(""); - if(names.count()>0 && - map.contains("name") && - 0 == QString::compare(map.value("name").toString(), names.at(0))) - { - // map is the left part of the split card, tmpMap is right part - card1 = ↦ - card2 = &tmpMap; - } else { - //tmpMap is the left part of the split card, map is right part - card1 = &tmpMap; - card2 = ↦ - } - - // add first card's data - cardName = card1->contains("name") ? card1->value("name").toString() : QString(""); - cardCost = card1->contains("manaCost") ? card1->value("manaCost").toString() : QString(""); - cmc = card1->contains("cmc") ? card1->value("cmc").toString() : QString("0"); - cardType = card1->contains("type") ? card1->value("type").toString() : QString(""); - cardPT = card1->contains("power") || card1->contains("toughness") ? card1->value("power").toString() + QString('/') + card1->value("toughness").toString() : QString(""); - cardText = card1->contains("text") ? card1->value("text").toString() : QString(""); - - // add second card's data - cardName += card2->contains("name") ? QString(" // ") + card2->value("name").toString() : QString(""); - cardCost += card2->contains("manaCost") ? QString(" // ") + card2->value("manaCost").toString() : QString(""); - cmc += card2->contains("cmc") ? QString(" // ") + card2->value("cmc").toString() : QString("0"); - cardType += card2->contains("type") ? QString(" // ") + card2->value("type").toString() : QString(""); - cardPT += card2->contains("power") || card2->contains("toughness") ? QString(" // ") + card2->value("power").toString() + QString('/') + card2->value("toughness").toString() : QString(""); - cardText += card2->contains("text") ? QString("\n\n---\n\n") + card2->value("text").toString() : QString(""); - - colors.clear(); - extractColors(card1->value("colors").toStringList(), colors); - extractColors(card2->value("colors").toStringList(), colors); - colors.removeDuplicates(); - - } else { - // first card of a pair; enqueue for later merging - // Conditional on cardId because promo prints have no muid - see #640 - if (cardId) - splitCards.insert(cardId, map); - continue; - } - } else { - // normal cards handling - cardName = map.contains("name") ? map.value("name").toString() : QString(""); - cardCost = map.contains("manaCost") ? map.value("manaCost").toString() : QString(""); - cmc = map.contains("cmc") ? map.value("cmc").toString() : QString("0"); - cardType = map.contains("type") ? map.value("type").toString() : QString(""); - cardPT = map.contains("power") || map.contains("toughness") ? map.value("power").toString() + QString('/') + map.value("toughness").toString() : QString(""); - cardText = map.contains("text") ? map.value("text").toString() : QString(""); - cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; - cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; - cardIsToken = map.value("layout") == "token"; - - colors.clear(); - extractColors(map.value("colors").toStringList(), colors); - - // Distinguish Vanguard cards from regular cards of the same name. - if (map.value("layout") == "vanguard") { - cardName += " Avatar"; - } + if (cardId) + splitCards.insertMulti(cardId, map); + continue; } - if (!cardIsToken) { - CardInfo *card = addCard(set->getShortName(), cardName, cardIsToken, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors); + // normal cards handling + cardName = map.contains("name") ? map.value("name").toString() : QString(""); + cardCost = map.contains("manaCost") ? map.value("manaCost").toString() : QString(""); + cmc = map.contains("cmc") ? map.value("cmc").toString() : QString("0"); + cardType = map.contains("type") ? map.value("type").toString() : QString(""); + cardPT = map.contains("power") || map.contains("toughness") ? map.value("power").toString() + QString('/') + map.value("toughness").toString() : QString(""); + cardText = map.contains("text") ? map.value("text").toString() : QString(""); + cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; + cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; + relatedCards = map.contains("names") ? map.value("names").toStringList() : QStringList(); + relatedCards.removeAll(cardName); - if (!set->contains(card)) { - card->addToSet(set); - cards++; - } + if(0 == QString::compare(map.value("layout").toString(), QString("flip"), Qt::CaseInsensitive)) + { + QStringList cardNames = map.contains("names") ? map.value("names").toStringList() : QStringList(); + upsideDown = (cardNames.indexOf(cardName) > 0); + } else { + upsideDown = false; + } + + colors.clear(); + extractColors(map.value("colors").toStringList(), colors); + + CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, upsideDown); + + if (!set->contains(card)) { + card->addToSet(set); + cards++; } } + // split cards handling - get all unique card muids + QList muids = splitCards.uniqueKeys(); + foreach(int muid, muids) + { + // get all cards for this specific muid + QList maps = splitCards.values(muid); + QStringList names; + // now, reorder the cards using the ordered list of names + QMap orderedMaps; + foreach(QVariantMap map, maps) + { + if(names.isEmpty()) + names = map.contains("names") ? map.value("names").toStringList() : QStringList(); + QString name = map.value("name").toString(); + int index = names.indexOf(name); + orderedMaps.insertMulti(index, map); + } + + // clean variables + cardName = ""; + cardCost = ""; + cmc = ""; + cardType = ""; + cardPT = ""; + cardText = ""; + colors.clear(); + // this is currently an integer; can't accept 2 values + cardLoyalty = 0; + + // loop cards and merge their contents + QString prefix = QString(" // "); + QString prefix2 = QString("\n\n---\n\n"); + foreach(QVariantMap map, orderedMaps.values()) + { + if(map.contains("name")) + { + if(!cardName.isEmpty()) + cardName += prefix; + cardName += map.value("name").toString(); + } + if(map.contains("manaCost")) + { + if(!cardCost.isEmpty()) + cardCost += prefix; + cardCost += map.value("manaCost").toString(); + } + if(map.contains("cmc")) + { + if(!cmc.isEmpty()) + cmc += prefix; + cmc += map.value("cmc").toString(); + } + if(map.contains("type")) + { + if(!cardType.isEmpty()) + cardType += prefix; + cardType += map.value("type").toString(); + } + if(map.contains("power") || map.contains("toughness")) + { + if(!cardPT.isEmpty()) + cardPT += prefix; + cardPT += map.value("power").toString() + QString('/') + map.value("toughness").toString(); + } + if(map.contains("text")) + { + if(!cardText.isEmpty()) + cardText += prefix2; + cardText += map.value("text").toString(); + } + + extractColors(map.value("colors").toStringList(), colors); + } + + colors.removeDuplicates(); + relatedCards = QStringList(); + upsideDown = false; + + // add the card + CardInfo *card = addCard(set->getShortName(), cardName, false, muid, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, upsideDown); + + if (!set->contains(card)) { + card->addToSet(set); + cards++; + } + + } + return cards; } diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index e3f06a44..888eabaf 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -29,7 +29,7 @@ private: QVariantMap setsMap; QString dataDir; - CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors); + CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors, const QStringList & relatedCards, bool upsideDown); signals: void setIndexChanged(int cardsImported, int setIndex, const QString &setName); void dataReadProgress(int bytesRead, int totalBytes); diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 75a5204e..8351129c 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -39,6 +39,8 @@ #define ALLSETS_URL "http://mtgjson.com/json/AllSets.json" #endif +#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" + OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) @@ -57,6 +59,8 @@ OracleWizard::OracleWizard(QWidget *parent) addPage(new IntroPage); addPage(new LoadSetsPage); addPage(new SaveSetsPage); + addPage(new LoadTokensPage); + addPage(new SaveTokensPage); retranslateUi(); } @@ -100,6 +104,23 @@ void OracleWizard::disableButtons() button(QWizard::BackButton)->setDisabled(true); } +bool OracleWizard::saveTokensToFile(const QString & fileName) +{ + QFile file(fileName); + if(!file.open(QIODevice::WriteOnly)) + { + qDebug() << "File open (w) failed for" << fileName; + return false; + } + if(file.write(tokensData) == -1) + { + qDebug() << "File write (w) failed for" << fileName; + return false; + } + file.close(); + return true; +} + IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) { @@ -150,11 +171,10 @@ void IntroPage::languageBoxChanged(int index) void IntroPage::retranslateUi() { setTitle(tr("Introduction")); - label->setText(tr("This wizard will import the list of sets and cards " - "that will be used by Cockatrice.
You will need to " - "specify an url or a filename that will be used as a " - "source, and then choose the wanted sets from the list " - "of the available ones.")); + label->setText(tr("This wizard will import the list of sets, cards, and tokens " + "that will be used by Cockatrice." + "\nYou will need to specify a URL or a filename that " + "will be used as a source.")); languageLabel->setText(tr("Language:")); } @@ -205,12 +225,12 @@ void LoadSetsPage::retranslateUi() { setTitle(tr("Source selection")); setSubTitle(tr("Please specify a source for the list of sets and cards. " - "You can specify an url address that will be download or " + "You can specify a URL address that will be downloaded or " "use an existing file from your computer.")); - urlRadioButton->setText(tr("Download url:")); + urlRadioButton->setText(tr("Download URL:")); fileRadioButton->setText(tr("Local file:")); - urlButton->setText(tr("Restore default url")); + urlButton->setText(tr("Restore default URL")); fileButton->setText(tr("Choose file...")); } @@ -251,7 +271,7 @@ bool LoadSetsPage::validatePage() QUrl url = QUrl::fromUserInput(urlLineEdit->text()); if(!url.isValid()) { - QMessageBox::critical(this, tr("Error"), tr("The provided url is not valid.")); + QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); return false; } @@ -302,7 +322,7 @@ void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) progressBar->setMaximum(total); progressBar->setValue(received); } - progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / 1048576)); + progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); } void LoadSetsPage::actDownloadFinishedSetsFile() @@ -529,3 +549,203 @@ bool SaveSetsPage::validatePage() return true; } + +LoadTokensPage::LoadTokensPage(QWidget *parent) + : OracleWizardPage(parent), nam(0) +{ + urlLabel = new QLabel(this); + urlLineEdit = new QLineEdit(this); + + progressLabel = new QLabel(this); + progressBar = new QProgressBar(this); + + urlButton = new QPushButton(this); + connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl())); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(urlLabel, 0, 0); + layout->addWidget(urlLineEdit, 0, 1); + layout->addWidget(urlButton, 1, 1, Qt::AlignRight); + layout->addWidget(progressLabel, 2, 0); + layout->addWidget(progressBar, 2, 1); + + setLayout(layout); +} + +void LoadTokensPage::initializePage() +{ + urlLineEdit->setText(wizard()->settings->value("tokensurl", TOKENS_URL).toString()); + + progressLabel->hide(); + progressBar->hide(); +} + +void LoadTokensPage::retranslateUi() +{ + setTitle(tr("Tokens source selection")); + setSubTitle(tr("Please specify a source for the list of tokens. " + "You can specify a URL address that will be downloaded or " + "use an existing file from your computer.")); + + urlLabel->setText(tr("Download URL:")); + urlButton->setText(tr("Restore default URL")); +} + +void LoadTokensPage::actRestoreDefaultUrl() +{ + urlLineEdit->setText(TOKENS_URL); +} + +bool LoadTokensPage::validatePage() +{ + // once the import is finished, we call next(); skip validation + if(wizard()->hasTokensData()) + return true; + + QUrl url = QUrl::fromUserInput(urlLineEdit->text()); + if(!url.isValid()) + { + QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); + return false; + } + + progressLabel->setText(tr("Downloading (0MB)")); + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->show(); + progressBar->show(); + + wizard()->disableButtons(); + setEnabled(false); + + if(!nam) + nam = new QNetworkAccessManager(this); + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + + connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedTokensFile())); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressTokensFile(qint64, qint64))); + + return false; +} + +void LoadTokensPage::actDownloadProgressTokensFile(qint64 received, qint64 total) +{ + if(total > 0) + { + progressBar->setMaximum(total); + progressBar->setValue(received); + } + progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); +} + +void LoadTokensPage::actDownloadFinishedTokensFile() +{ + progressLabel->hide(); + progressBar->hide(); + + // check for a reply + QNetworkReply *reply = static_cast(sender()); + QNetworkReply::NetworkError errorCode = reply->error(); + if (errorCode != QNetworkReply::NoError) { + QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); + + wizard()->enableButtons(); + setEnabled(true); + + reply->deleteLater(); + return; + } + + // save tokens.xml url, but only if the user customized it and download was successfull + if(urlLineEdit->text() != QString(TOKENS_URL)) + wizard()->settings->setValue("tokensurl", urlLineEdit->text()); + else + wizard()->settings->remove("tokensurl"); + + wizard()->setTokensData(reply->readAll()); + reply->deleteLater(); + + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + wizard()->next(); +} + +SaveTokensPage::SaveTokensPage(QWidget *parent) + : OracleWizardPage(parent) +{ + defaultPathCheckBox = new QCheckBox(this); + defaultPathCheckBox->setChecked(true); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(defaultPathCheckBox, 0, 0); + + setLayout(layout); +} + +void SaveTokensPage::retranslateUi() +{ + setTitle(tr("Tokens imported")); + setSubTitle(tr("The tokens has been imported. " + "Press \"Save\" to save the imported tokens to the Cockatrice tokens database.")); + + defaultPathCheckBox->setText(tr("Save to the default path (recommended)")); +} + +bool SaveTokensPage::validatePage() +{ + bool ok = false; + const QString dataDir = +#if QT_VERSION < 0x050000 + QDesktopServices::storageLocation(QDesktopServices::DataLocation); +#else + QStandardPaths::standardLocations(QStandardPaths::DataLocation).first(); +#endif + QSettings* settings = new QSettings(this); + QString defaultPath = settings->value("paths/tokendatabase").toString(); + QString windowName = tr("Save token database"); + QString fileType = tr("XML; token database (*.xml)"); + + do { + QString fileName; + if (defaultPath.isEmpty()) { + if (defaultPathCheckBox->isChecked()) + fileName = dataDir + "/tokens.xml"; + else + fileName = QFileDialog::getSaveFileName(this, windowName, dataDir + "/tokens.xml", fileType); + settings->setValue("paths/tokendatabase", fileName); + } + else { + if (defaultPathCheckBox->isChecked()) + fileName = defaultPath; + else + fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } + if (fileName.isEmpty()) { + return false; + } + + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { + return false; + } + if (wizard()->saveTokensToFile(fileName)) + { + ok = true; + QMessageBox::information(this, + tr("Success"), + tr("The token database has been saved successfully to\n%1").arg(fileName)); + } else { + QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));; + if (defaultPathCheckBox->isChecked()) + defaultPathCheckBox->setChecked(false); + } + } while (!ok); + + return true; +} diff --git a/oracle/src/oraclewizard.h b/oracle/src/oraclewizard.h index 0656a659..d06e0971 100644 --- a/oracle/src/oraclewizard.h +++ b/oracle/src/oraclewizard.h @@ -27,6 +27,9 @@ public: void enableButtons(); void disableButtons(); void retranslateUi(); + void setTokensData(QByteArray _tokensData) { tokensData = _tokensData; } + bool hasTokensData() { return !tokensData.isEmpty(); } + bool saveTokensToFile(const QString & fileName); public: OracleImporter *importer; QSettings * settings; @@ -35,6 +38,7 @@ private slots: private: QStringList findQmFiles(); QString languageName(const QString &qmFile); + QByteArray tokensData; protected: void changeEvent(QEvent *event); }; @@ -115,4 +119,38 @@ private slots: void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); }; +class LoadTokensPage : public OracleWizardPage +{ + Q_OBJECT +public: + LoadTokensPage(QWidget *parent = 0); + void retranslateUi(); +protected: + void initializePage(); + bool validatePage(); +private: + QLabel *urlLabel; + QLineEdit *urlLineEdit; + QPushButton *urlButton; + QLabel *progressLabel; + QProgressBar * progressBar; + + QNetworkAccessManager *nam; +private slots: + void actRestoreDefaultUrl(); + void actDownloadProgressTokensFile(qint64 received, qint64 total); + void actDownloadFinishedTokensFile(); +}; + +class SaveTokensPage : public OracleWizardPage +{ + Q_OBJECT +public: + SaveTokensPage(QWidget *parent = 0); + void retranslateUi(); +private: + QCheckBox * defaultPathCheckBox; +protected: + bool validatePage(); +}; #endif \ No newline at end of file diff --git a/oracle/translations/oracle_cs.ts b/oracle/translations/oracle_cs.ts index f31d5e6a..41e8e2db 100644 --- a/oracle/translations/oracle_cs.ts +++ b/oracle/translations/oracle_cs.ts @@ -113,6 +113,45 @@
+ + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -183,6 +222,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_de.ts b/oracle/translations/oracle_de.ts index 4be9ab44..0b72b812 100644 --- a/oracle/translations/oracle_de.ts +++ b/oracle/translations/oracle_de.ts @@ -16,7 +16,7 @@ This wizard will import the list of sets and cards that will be used by Cockatrice.<br/>You will need to specify an url or a filename that will be used as a source, and then choose the wanted sets from the list of the available ones. Dieser Assistent wird eine Liste aller Editionen und Karten, die von Cockatrice genutzt werden, importieren. -Sie müssen dazu eine URL oder einen Dateinamen die/der als Quelle genutzt werden soll angeben, dann können aus einer Liste aller verfügbaren Editionen die Gewünschten ausgwählt werden. +Sie müssen dazu eine URL oder einen Dateinamen als Quelle angeben. Danach können Sie aus einer Liste aller verfügbaren Editionen die Gewünschten ausgewählen.
@@ -39,7 +39,7 @@ Sie müssen dazu eine URL oder einen Dateinamen die/der als Quelle genutzt werde Restore default url - URL wiederherstellen + Standard-URL wiederherstellen Choose file... @@ -114,6 +114,45 @@ Sie müssen dazu eine URL oder einen Dateinamen die/der als Quelle genutzt werde Die Datei wurde erfolgreich abgerufen, sie enthält aber keine Editionsdaten. + + LoadTokensPage + + Tokens source selection + Spielstein-Quellenauswahl + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + Bitte geben Sie eine Quelle für die Liste der Spielsteine an. Sie können eine URL Adresse zum Herunterladen eingeben oder eine vorhandene Datei von Ihrem Computer verwenden. + + + Download url: + Download URL: + + + Restore default url + Standard-URL wiederherstellen + + + Error + Fehler + + + The provided url is not valid. + Die eingegebene URL ist nicht gültig. + + + Downloading (0MB) + Herunterladen (0MB) + + + Downloading (%1MB) + Herunterladen (%1MB) + + + Network error: %1. + Netzwerkfehler: %1. + + OracleImporter @@ -186,6 +225,48 @@ Sie müssen dazu eine URL oder einen Dateinamen die/der als Quelle genutzt werde %1 + + SaveTokensPage + + Tokens imported + Spielsteine importiert + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + Spielsteine wurden importiert. Drücken Sie „Speichern“, um die importierten Spielsteine in der Cockatrice Spielsteindatenbank abzuspeichern. + + + Save to the default path (recommended) + Im Standardverzeichnis abspeichern (Empfohlen) + + + Save token database + Spielsteindatenbank speichern + + + XML; token database (*.xml) + XML; Tokendatenbank (*.xml) + + + Success + Erfolgreich + + + The token database has been saved successfully to +%1 + Die Spielsteindatenbank wurde erfolgreich im folgendem Pfad gespeichert: +%1 + + + Error + Fehler + + + The file could not be saved to %1 + Die Datei konnte nicht gespeichert werden: +%1 + + UnZip diff --git a/oracle/translations/oracle_en.ts b/oracle/translations/oracle_en.ts index 8fc700a4..2dd40a6e 100644 --- a/oracle/translations/oracle_en.ts +++ b/oracle/translations/oracle_en.ts @@ -115,6 +115,45 @@ + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -185,6 +224,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_en@pirate.ts b/oracle/translations/oracle_en@pirate.ts new file mode 100644 index 00000000..9741aa6d --- /dev/null +++ b/oracle/translations/oracle_en@pirate.ts @@ -0,0 +1,379 @@ + + + IntroPage + + Introduction + + + + English + English, arr! (Pirate English) + + + Language: + + + + This wizard will import the list of sets and cards that will be used by Cockatrice.<br/>You will need to specify an url or a filename that will be used as a source, and then choose the wanted sets from the list of the available ones. + + + + + LoadSetsPage + + Source selection + + + + Please specify a source for the list of sets and cards. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Local file: + + + + Restore default url + + + + Choose file... + + + + Load sets file + + + + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + + Error + Cap'n? Thar be a problem + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Please choose a file. + + + + Cannot open file '%1'. + + + + Downloading (%1MB) + + + + Network error: %1. + Cap'n? Thar be a problem wi' t' smoke signals: %1. + + + Parsing file + + + + Failed to open Zip archive: %1. + + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + + + + Zip extraction failed: %1. + + + + Sorry, this version of Oracle does not support zipped files. + + + + Do you want to try to download a fresh copy of the uncompressed file instead? + + + + The file was retrieved successfully, but it does not contain any sets data. + + + + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + Cap'n? Thar be a problem + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + Cap'n? Thar be a problem wi' t' smoke signals: %1. + + + + OracleImporter + + Dummy set containing tokens + + + + + OracleWizard + + Oracle Importer + + + + Save + + + + + SaveSetsPage + + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + + + + Error + Cap'n? Thar be a problem + + + No set has been imported. + + + + Import finished: %1 cards. + + + + %1: %2 cards imported + + + + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + + + + The file could not be saved to %1 + + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + Cap'n? Thar be a problem + + + The file could not be saved to %1 + + + + + UnZip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + Cap'n? Thar be a problem wi' t' zlib library. + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + Cap'n? Thar be a unknown problem wi' t' ship. + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + Cap'n? Thar be a problem wi' t' zlib library. + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. + Cap'n? Thar be a unknown problem wi' t' ship. + + + \ No newline at end of file diff --git a/oracle/translations/oracle_es.ts b/oracle/translations/oracle_es.ts index 45fd84a1..549aca0e 100644 --- a/oracle/translations/oracle_es.ts +++ b/oracle/translations/oracle_es.ts @@ -15,18 +15,18 @@ This wizard will import the list of sets and cards that will be used by Cockatrice.<br/>You will need to specify an url or a filename that will be used as a source, and then choose the wanted sets from the list of the available ones. - + Este asistente importará la lista de los sets y cartas que serán usadas por Cocatrice.<br/>Necesitarás especificar la url o el nombre de archivo que será usado como origen y después elegir los sets deseados de la lista de sets disponibles. LoadSetsPage Source selection - + Seleccionar origen Please specify a source for the list of sets and cards. You can specify an url address that will be download or use an existing file from your computer. - + Por favor especifica un origen para la lista de sets y cartas. Puedes especificar la url de donde descargarla o usar un archivo existente de tu ordenador. Download url: @@ -50,11 +50,11 @@ Sets JSON file (*.json *.zip) - + Archivo de sets en formato JSON (*.json *.zip) Sets JSON file (*.json) - + Archivo de sets en formato JSON (*.json) Error @@ -86,30 +86,69 @@ Parsing file - + Procesando archivo Failed to open Zip archive: %1. - + Error al abrir el archivo Zip: %1. Zip extraction failed: the Zip archive doesn't contain exactly one file. - + Fallo al extraer el contenido: el Zip contiene más de un archivo. Zip extraction failed: %1. - + Error al extraer el contenido del Zip: %1. Sorry, this version of Oracle does not support zipped files. - + Lo sentimos, esta versión de Oracle no soporta archivos comprimidos. Do you want to try to download a fresh copy of the uncompressed file instead? - + ¿Prefieres intentar descargar una copia nueva del fichero descomprimido? The file was retrieved successfully, but it does not contain any sets data. + El archivo fue cargado correctamente pero no contiene datos sobre ningún set. + + + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. @@ -117,14 +156,14 @@ OracleImporter Dummy set containing tokens - + Set dedicado para tokens
OracleWizard Oracle Importer - + Importador de Oracle Save @@ -135,15 +174,15 @@ SaveSetsPage Sets imported - + Sets importados The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - + Los siguientes sets han sido importados. Pulsa "Guardar" para guardar las cartas importadas en la base de datos de Cockatrice. Save to the default path (recommended) - + Guardar en la ruta por defecto (recomendado) Error @@ -151,23 +190,23 @@ No set has been imported. - + Ningún set ha sido importado. Import finished: %1 cards. - + Importación terminada: %1 cartas. %1: %2 cards imported - + %1: %2 cartas importadas Save card database - + Guardar base de datos de cartas XML; card database (*.xml) - + XML; base de datos de cartas (*.xml) Success @@ -176,6 +215,47 @@ The card database has been saved successfully to %1 + La base de datos de cartas ha sido guardada correctamente en +%1 + + + The file could not be saved to %1 + El archivo no ha podido ser guardado en %1 + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error @@ -187,27 +267,27 @@ UnZip ZIP operation completed successfully. - + La operación se ha completado correctamente. Failed to initialize or load zlib library. - + Fallo al iniciar o cargar la librería zlib. zlib library error. - + Error en la librería zlib. Unable to create or open file. - + No se ha podido crear o abrir el archivo. Partially corrupted archive. Some files might be extracted. - + Archivo parcialmente dañado. Algunos datos han podido ser extraídos. Corrupted archive. - + Archivo dañado. Wrong password. @@ -215,27 +295,27 @@ No archive has been created yet. - + No se ha creado ningún archivo todavía. File or directory does not exist. - + El archivo o directorio no existen. File read error. - + Error al leer el archivo. File write error. - + Error al escribir en archivo. File seek error. - + Error al buscar en el archivo. Unable to create a directory. - + No es posible crear el directorio. Invalid device. @@ -243,11 +323,11 @@ Invalid or incompatible zip archive. - + Archivo zip inválido o incompatible. Inconsistent headers. Archive might be corrupted. - + Cabezeras inconsistentes. El archivo podría estar dañado. Unknown error. @@ -258,11 +338,11 @@ Zip ZIP operation completed successfully. - + Operación de descompresión completada correctamente. Failed to initialize or load zlib library. - + Fallo al iniciar o cargar la librería zlib. zlib library error. @@ -274,7 +354,7 @@ No archive has been created yet. - + No se ha creado ningún archivo todavía. File or directory does not exist. diff --git a/oracle/translations/oracle_et.ts b/oracle/translations/oracle_et.ts index 5eef531f..0c8e3761 100644 --- a/oracle/translations/oracle_et.ts +++ b/oracle/translations/oracle_et.ts @@ -113,6 +113,45 @@ Fail on edukalt alla laetud, ent ei sisalda andmeid. + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -184,6 +223,46 @@ Faili salvestamine asukohta %1 ebaõnnestus + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_fr.ts b/oracle/translations/oracle_fr.ts index 4bb746fe..23f61c53 100644 --- a/oracle/translations/oracle_fr.ts +++ b/oracle/translations/oracle_fr.ts @@ -15,7 +15,7 @@ This wizard will import the list of sets and cards that will be used by Cockatrice.<br/>You will need to specify an url or a filename that will be used as a source, and then choose the wanted sets from the list of the available ones. - Cet assistant va importer la liste des éditions et des cartes qui seront utilisées par Cockatrice.<br/>Vous devrez spécifier une url ou un fichier local avec la liste d'édition comme fichier source, Puis choisir quelles éditions seront importées parmis la liste proposée. + Cet assistant va importer la liste des éditions et des cartes qui seront utilisées par Cockatrice.<br/>Vous devrez spécifier une url ou un fichier local avec la liste d'édition comme fichier source, puis choisir quelles éditions seront importées parmi la liste proposée. @@ -113,6 +113,45 @@ Le fichier a été trouvé, mais ne contient aucune éditions. + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + URL de téléchargement: + + + Restore default url + Restaurer l'URL par défaut + + + Error + Érreur + + + The provided url is not valid. + L'URL fournit est non valide. + + + Downloading (0MB) + Téléchargement (0MB) + + + Downloading (%1MB) + Téléchargement (%1MB) + + + Network error: %1. + Erreur réseau : %1. + + OracleImporter @@ -184,6 +223,46 @@ Le fichier n'a pu être sauvegarder au chemin '%1' + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + Sauvergarder au chemin par défaut (recommendé) + + + Save token database + Sauvegarder la base des jetons + + + XML; token database (*.xml) + XML; bases de données des jetons (*.xml) + + + Success + Réussite + + + The token database has been saved successfully to +%1 + + + + Error + Érreur + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_it.ts b/oracle/translations/oracle_it.ts index be6d718f..e7f5e912 100644 --- a/oracle/translations/oracle_it.ts +++ b/oracle/translations/oracle_it.ts @@ -113,6 +113,45 @@ Il file è stato analizzato correttamente, ma non contiene i dati di nessun set. + + LoadTokensPage + + Tokens source selection + Selezione sorgente pedine + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + Specifica una sorgente per la lista delle pedine. Puoi specificare un indirizzo url da cui scaricare il file o alternativamente usare un file già presente nel tuo computer. + + + Download url: + Indirizzo download: + + + Restore default url + Usa l'indirizzo predefinito + + + Error + Errore + + + The provided url is not valid. + L'indirizzo specificato non è valido. + + + Downloading (0MB) + Scaricamento (0MB) + + + Downloading (%1MB) + Scaricamento (%1MB) + + + Network error: %1. + Errore di rete: %1 + + OracleImporter @@ -184,6 +223,47 @@ Impossibile salvare il file su %1 + + SaveTokensPage + + Tokens imported + Pedine importate + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + Le pedine sono state importate. Premi "Salva" per salvare le pedine importate nell'archivio delle pedine di Cockatrice. + + + Save to the default path (recommended) + Salva nel percorso predefinito (raccomandato) + + + Save token database + Salva archivio pedine + + + XML; token database (*.xml) + XML; archivio pedine (*.xml) + + + Success + Successo + + + The token database has been saved successfully to +%1 + L'archivio delle pedine è stato salvato correttamente su +%1 + + + Error + Errore + + + The file could not be saved to %1 + Impossibile salvare il file su %1 + + UnZip diff --git a/oracle/translations/oracle_ja.ts b/oracle/translations/oracle_ja.ts index 1d76904e..2c968af9 100644 --- a/oracle/translations/oracle_ja.ts +++ b/oracle/translations/oracle_ja.ts @@ -114,6 +114,45 @@ ファイルは正常に取得されたが、カードセットのデータが含まれていませんでした。 + + LoadTokensPage + + Tokens source selection + トークンのソース選択 + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + トークンのソースを指定してください。ダウンロード可能なURLか、コンピューターにあるソースファイルを指定することができます。 + + + Download url: + ダウンロードURL: + + + Restore default url + デフォルトのURLを復元 + + + Error + エラー + + + The provided url is not valid. + 指定されたURLは無効です。 + + + Downloading (0MB) + ダウンロード中 (0MB) + + + Downloading (%1MB) + ダウンロード中 (%1MB) + + + Network error: %1. + ネットワークエラー: %1。 + + OracleImporter @@ -185,6 +224,47 @@ %1に保存できませんでした。 + + SaveTokensPage + + Tokens imported + トークンインポート + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + トークンがインポートされました。”保存”をクリックするとインポートしたトークンをCockatriceデータベースに保存します。 + + + Save to the default path (recommended) + デフォルトのパスに保存 (推奨) + + + Save token database + トークンデータベースを保存 + + + XML; token database (*.xml) + XML; token database (*.xml) + + + Success + 完了 + + + The token database has been saved successfully to +%1 + トークンデータベースは以下に保存されました: +%1 + + + Error + エラー + + + The file could not be saved to %1 + %1に保存できませんでした。 + + UnZip diff --git a/oracle/translations/oracle_ko.ts b/oracle/translations/oracle_ko.ts index 1deed37c..ab9f3bc2 100644 --- a/oracle/translations/oracle_ko.ts +++ b/oracle/translations/oracle_ko.ts @@ -114,6 +114,46 @@ 파일을 성공적으로 다운로드 하였으나 판본 정보가 들어있지 않습니다. + + LoadTokensPage + + Tokens source selection + 토큰 파일 주소 입력 + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + 토큰 목록 및 정보가 들어있는 파일의 위치를 입력해 주세요. +다운로드 할 수 있는 웹 주소나 컴퓨터에 저장되어 있는 파일을 선택 할 수 있습니다. + + + Download url: + 웹 주소: + + + Restore default url + 기본 주소로 복원 + + + Error + 오류 + + + The provided url is not valid. + 잘못된 주소를 입력하셨습니다. + + + Downloading (0MB) + 다운로드 중 (0MB) + + + Downloading (%1MB) + 다운로드 중 (%1MB) + + + Network error: %1. + 네트워크 오류 : %1. + + OracleImporter @@ -186,6 +226,48 @@ 파일을 %1에 저장 할 수 없습니다. + + SaveTokensPage + + Tokens imported + 토큰 불러오기 완료 + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + 토큰 파일을 불러왔습니다. +"저장" 버튼을 누르면 코카트리스에서 토큰 파일을 불러옵니다. + + + Save to the default path (recommended) + 기본 경로에 저장 (권장) + + + Save token database + 토큰 파일 저장 + + + XML; token database (*.xml) + 토큰 정보 XML 파일 (*.xml) + + + Success + 성공 + + + The token database has been saved successfully to +%1 + 토큰 정보 파일을 다음 위치에 저장했습니다: +%1 + + + Error + 오류 + + + The file could not be saved to %1 + 파일을 %1에 저장 할 수 없습니다. + + UnZip diff --git a/oracle/translations/oracle_nb.ts b/oracle/translations/oracle_nb.ts index 4b710375..695255d3 100644 --- a/oracle/translations/oracle_nb.ts +++ b/oracle/translations/oracle_nb.ts @@ -7,7 +7,7 @@ English - + (Norwegian Bokmål) Language: @@ -113,6 +113,45 @@ + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -183,6 +222,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_nl.ts b/oracle/translations/oracle_nl.ts index 3791c254..b6e5337b 100644 --- a/oracle/translations/oracle_nl.ts +++ b/oracle/translations/oracle_nl.ts @@ -114,6 +114,45 @@ Voer een URL of bestandsnaam in als bron, en selecteer vervolgens uit de lijst v Het bestand is succesvol binnengehaald, maar bevat geen set data. + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -185,6 +224,46 @@ Voer een URL of bestandsnaam in als bron, en selecteer vervolgens uit de lijst v Het bestand kon niet worden opgeslagen in %1 + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_pl.ts b/oracle/translations/oracle_pl.ts index ada1abbc..66999d8e 100644 --- a/oracle/translations/oracle_pl.ts +++ b/oracle/translations/oracle_pl.ts @@ -113,6 +113,45 @@ Plik został pobrany z powodzeniem, ale nie zawiera informacji o dodatkach. + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -184,6 +223,46 @@ Plik nie mógł zostać zapisany do %1 + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_pt.ts b/oracle/translations/oracle_pt.ts index f1d7c97e..5fe02754 100644 --- a/oracle/translations/oracle_pt.ts +++ b/oracle/translations/oracle_pt.ts @@ -78,7 +78,7 @@ Downloading (%1MB) - + A efectuar download (%1MB) Network error: %1. @@ -113,6 +113,45 @@ + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + Erro + + + The provided url is not valid. + + + + Downloading (0MB) + A efectuar download (0MB) + + + Downloading (%1MB) + A efectuar download (%1MB) + + + Network error: %1. + Erro da rede: %1. + + OracleImporter @@ -183,6 +222,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + Sucesso + + + The token database has been saved successfully to +%1 + + + + Error + Erro + + + The file could not be saved to %1 + + + UnZip @@ -274,7 +353,7 @@ No archive has been created yet. - + Ainda não foi criado nenhum arquivo File or directory does not exist. diff --git a/oracle/translations/oracle_pt_BR.ts b/oracle/translations/oracle_pt_BR.ts index 8a1070f6..3ea61640 100644 --- a/oracle/translations/oracle_pt_BR.ts +++ b/oracle/translations/oracle_pt_BR.ts @@ -113,6 +113,45 @@ O arquivo foi recebido com sucesso, mas não contém qualquer informação de expansão. + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -184,6 +223,46 @@ O arquivo não pode ser salvo para %1 + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_ru.ts b/oracle/translations/oracle_ru.ts index 82d1ac28..4bdcf89f 100644 --- a/oracle/translations/oracle_ru.ts +++ b/oracle/translations/oracle_ru.ts @@ -113,6 +113,45 @@ + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -183,6 +222,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_sv.ts b/oracle/translations/oracle_sv.ts index 9b26cb3f..abce6c2e 100644 --- a/oracle/translations/oracle_sv.ts +++ b/oracle/translations/oracle_sv.ts @@ -113,6 +113,45 @@ + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -183,6 +222,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_uk.ts b/oracle/translations/oracle_uk.ts new file mode 100644 index 00000000..3a9bab1d --- /dev/null +++ b/oracle/translations/oracle_uk.ts @@ -0,0 +1,379 @@ + + + IntroPage + + Introduction + + + + English + + + + Language: + + + + This wizard will import the list of sets and cards that will be used by Cockatrice.<br/>You will need to specify an url or a filename that will be used as a source, and then choose the wanted sets from the list of the available ones. + + + + + LoadSetsPage + + Source selection + + + + Please specify a source for the list of sets and cards. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Local file: + + + + Restore default url + + + + Choose file... + + + + Load sets file + + + + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Please choose a file. + + + + Cannot open file '%1'. + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Parsing file + + + + Failed to open Zip archive: %1. + + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + + + + Zip extraction failed: %1. + + + + Sorry, this version of Oracle does not support zipped files. + + + + Do you want to try to download a fresh copy of the uncompressed file instead? + + + + The file was retrieved successfully, but it does not contain any sets data. + + + + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + + OracleImporter + + Dummy set containing tokens + + + + + OracleWizard + + Oracle Importer + + + + Save + + + + + SaveSetsPage + + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + + + + Error + + + + No set has been imported. + + + + Import finished: %1 cards. + + + + %1: %2 cards imported + + + + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + + + + The file could not be saved to %1 + + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + + + UnZip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. + + + + \ No newline at end of file diff --git a/oracle/translations/oracle_zh-Hans.ts b/oracle/translations/oracle_zh-Hans.ts index 2c777ec5..734372d7 100644 --- a/oracle/translations/oracle_zh-Hans.ts +++ b/oracle/translations/oracle_zh-Hans.ts @@ -113,6 +113,45 @@ + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + OracleImporter @@ -183,6 +222,46 @@ + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + UnZip diff --git a/oracle/translations/oracle_zh-Hant.ts b/oracle/translations/oracle_zh-Hant.ts new file mode 100644 index 00000000..8a65203b --- /dev/null +++ b/oracle/translations/oracle_zh-Hant.ts @@ -0,0 +1,379 @@ + + + IntroPage + + Introduction + + + + English + 繁體中文 (Chinese Traditional) + + + Language: + + + + This wizard will import the list of sets and cards that will be used by Cockatrice.<br/>You will need to specify an url or a filename that will be used as a source, and then choose the wanted sets from the list of the available ones. + + + + + LoadSetsPage + + Source selection + + + + Please specify a source for the list of sets and cards. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Local file: + + + + Restore default url + + + + Choose file... + + + + Load sets file + + + + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Please choose a file. + + + + Cannot open file '%1'. + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Parsing file + + + + Failed to open Zip archive: %1. + + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + + + + Zip extraction failed: %1. + + + + Sorry, this version of Oracle does not support zipped files. + + + + Do you want to try to download a fresh copy of the uncompressed file instead? + + + + The file was retrieved successfully, but it does not contain any sets data. + + + + + LoadTokensPage + + Tokens source selection + + + + Please specify a source for the list of tokens. You can specify an url address that will be download or use an existing file from your computer. + + + + Download url: + + + + Restore default url + + + + Error + + + + The provided url is not valid. + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + + OracleImporter + + Dummy set containing tokens + + + + + OracleWizard + + Oracle Importer + + + + Save + + + + + SaveSetsPage + + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + + + + Error + + + + No set has been imported. + + + + Import finished: %1 cards. + + + + %1: %2 cards imported + + + + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + + + + The file could not be saved to %1 + + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + + + UnZip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. + + + + \ No newline at end of file diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 2fe46e00..6fa529ff 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -14,19 +14,13 @@ SET(servatrice_SOURCES src/serversocketinterface.cpp src/settingscache.cpp src/isl_interface.cpp + src/signalhandler.cpp ${VERSION_STRING_CPP} - src/smtp/emailaddress.cpp - src/smtp/mimeattachment.cpp - src/smtp/mimecontentformatter.cpp - src/smtp/mimefile.cpp - src/smtp/mimehtml.cpp - src/smtp/mimeinlinefile.cpp - src/smtp/mimemessage.cpp - src/smtp/mimemultipart.cpp - src/smtp/mimepart.cpp - src/smtp/mimetext.cpp - src/smtp/quotedprintable.cpp - src/smtp/smtpclient.cpp + src/smtpclient.cpp + src/smtp/qxthmac.cpp + src/smtp/qxtmailattachment.cpp + src/smtp/qxtmailmessage.cpp + src/smtp/qxtsmtp.cpp ) set(servatrice_RESOURCES servatrice.qrc) @@ -101,6 +95,8 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # Build servatrice binary and link it ADD_EXECUTABLE(servatrice MACOSX_BUNDLE ${servatrice_SOURCES} ${servatrice_RESOURCES_RCC} ${servatrice_MOC_SRCS}) +set_property(TARGET servatrice PROPERTY CXX_STANDARD 11) +set_property(TARGET servatrice PROPERTY CXX_STANDARD_REQUIRED ON) if(Qt4_FOUND) if(MSVC) @@ -197,4 +193,4 @@ Translations = Resources/translations\") include(BundleUtilities) fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") " COMPONENT Runtime) -endif() \ No newline at end of file +endif() diff --git a/servatrice/migrations/servatrice_0001_to_0002.sql b/servatrice/migrations/servatrice_0001_to_0002.sql new file mode 100644 index 00000000..fc30b4a1 --- /dev/null +++ b/servatrice/migrations/servatrice_0001_to_0002.sql @@ -0,0 +1,8 @@ +-- Servatrice db migration from version 1 to version 2 + +-- FIX #1281 +CREATE TABLE IF NOT EXISTS `cockatrice_activation_emails` ( + `name` varchar(35) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +UPDATE cockatrice_schema_version SET version=2 WHERE version=1; diff --git a/servatrice/migrations/servatrice_0002_to_0003.sql b/servatrice/migrations/servatrice_0002_to_0003.sql new file mode 100644 index 00000000..4a87bea4 --- /dev/null +++ b/servatrice/migrations/servatrice_0002_to_0003.sql @@ -0,0 +1,5 @@ +-- Servatrice db migration from version 2 to version 3 + +alter table cockatrice_users add clientid varchar(15) not null; + +UPDATE cockatrice_schema_version SET version=3 WHERE version=2; diff --git a/servatrice/migrations/servatrice_0003_to_0004.sql b/servatrice/migrations/servatrice_0003_to_0004.sql new file mode 100644 index 00000000..bbfce750 --- /dev/null +++ b/servatrice/migrations/servatrice_0003_to_0004.sql @@ -0,0 +1,5 @@ +-- Servatrice db migration from version 3 to version 4 + +alter table cockatrice_sessions add clientid varchar(15) not null; + +UPDATE cockatrice_schema_version SET version=4 WHERE version=3; diff --git a/servatrice/migrations/servatrice_0004_to_0005.sql b/servatrice/migrations/servatrice_0004_to_0005.sql new file mode 100644 index 00000000..2d4ecf00 --- /dev/null +++ b/servatrice/migrations/servatrice_0004_to_0005.sql @@ -0,0 +1,5 @@ +-- Servatrice db migration from version 4 to version 5 + +alter table cockatrice_bans add clientid varchar(15) not null; + +UPDATE cockatrice_schema_version SET version=5 WHERE version=4; diff --git a/servatrice/migrations/servatrice_0005_to_0006.sql b/servatrice/migrations/servatrice_0005_to_0006.sql new file mode 100644 index 00000000..3f9552b2 --- /dev/null +++ b/servatrice/migrations/servatrice_0005_to_0006.sql @@ -0,0 +1,5 @@ +-- Servatrice db migration from version 5 to version 6 + +alter table cockatrice_users add last_login datetime not null; + +UPDATE cockatrice_schema_version SET version=6 WHERE version=5; diff --git a/servatrice/migrations/servatrice_0006_to_0007.sql b/servatrice/migrations/servatrice_0006_to_0007.sql new file mode 100644 index 00000000..651105a8 --- /dev/null +++ b/servatrice/migrations/servatrice_0006_to_0007.sql @@ -0,0 +1,5 @@ +-- Servatrice db migration from version 6 to version 7 + +alter table cockatrice_rooms add permissionlevel varchar(20) not null after descr; + +UPDATE cockatrice_schema_version SET version=7 WHERE version=6; diff --git a/servatrice/scripts/File.History b/servatrice/scripts/File.History new file mode 100644 index 00000000..8ea21e2c Binary files /dev/null and b/servatrice/scripts/File.History differ diff --git a/servatrice/scripts/linux/db_backup_all b/servatrice/scripts/linux/db_backup_all new file mode 100644 index 00000000..35aa5d41 --- /dev/null +++ b/servatrice/scripts/linux/db_backup_all @@ -0,0 +1,47 @@ +#!/bin/bash +set -u +set -e +SLEEPTIME=5 +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +LOGAPPENDDATE=`date +%m%d%Y` +EXPIRATION=`date +%m%d%Y -d "-3 days"` +DBNAME="servatrice" +APPNAME="servatrice" +ROOTFOLDER="./backups" #set this to the root path you want backups to be stored in +BACKUPDIR="$ROOTFOLDER/$LOGAPPENDDATE/db/$APPNAME" +TABLES=( + "cockatrice_users" + "cockatrice_decklist_files" + "cockatrice_replays" + "cockatrice_buddylist" + "cockatrice_ignorelist" + "cockatrice_bans" + "cockatrice_sessions" + "cockatrice_decklist_folders" + "cockatrice_replays_access" + "cockatrice_games" + "cockatrice_games_players" + "cockatrice_uptime" + "cockatrice_schema_version" + "cockatrice_servermessages" + "cockatrice_servers" + "cockatrice_news" + "cockatrice_rooms" + "cockatrice_rooms_gametypes" + ) + +PROCESSNAME="mysqldump" +if [ "$(pgrep $PROCESSNAME)" == "" ]; +then + [ ! -d $BACKUPDIR ] && mkdir -p $BACKUPDIR/ + for TABLENAME in "${TABLES[@]}" + do + BACKUPFILE="$BACKUPDIR/$APPNAME.$TABLENAME.sql.$LOGAPPENDDATE" + echo "Backing up DB Table [$TABLENAME]" + ionice -c3 nice -n19 mysqldump --defaults-file=$SQLCONFFILE $DBNAME $TABLENAME > $BACKUPFILE + sleep $SLEEPTIME + done + rm -rf "$ROOTFOLDER/$EXPIRATION/" +else + echo "Backup in progress, aborting" +fi diff --git a/servatrice/scripts/linux/db_restore_all b/servatrice/scripts/linux/db_restore_all new file mode 100644 index 00000000..37d1f31e --- /dev/null +++ b/servatrice/scripts/linux/db_restore_all @@ -0,0 +1,53 @@ +#!/bin/bash +set -u +set -e +SLEEPTIME=5 +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +LOGAPPENDDATE=`date +%m%d%Y` +EXPIRATION=`date +%m%d%Y -d "-3 days"` +DBNAME="servatrice" +APPNAME="servatrice" +ROOTFOLDER="./backups" #set this to the root path that contains the backup files +BACKUPDIR="$ROOTFOLDER/$LOGAPPENDDATE/db/$APPNAME" +TABLES=( + "cockatrice_users" + "cockatrice_decklist_files" + "cockatrice_replays" + "cockatrice_buddylist" + "cockatrice_ignorelist" + "cockatrice_bans" + "cockatrice_sessions" + "cockatrice_decklist_folders" + "cockatrice_replays_access" + "cockatrice_games" + "cockatrice_games_players" + "cockatrice_uptime" + "cockatrice_schema_version" + "cockatrice_servermessages" + "cockatrice_servers" + "cockatrice_news" + "cockatrice_rooms" + "cockatrice_rooms_gametypes" + ) + +PROCESSNAME="mysqldump" +if [ "$(pgrep $PROCESSNAME)" == "" ]; +then + [ ! -d $BACKUPDIR ] && mkdir -p $BACKUPDIR/ + for TABLENAME in "${TABLES[@]}" + do + BACKUPFILE="$BACKUPDIR/$APPNAME.$TABLENAME.sql.$LOGAPPENDDATE" + if [ -f "$BACKUPFILE" ] + then + echo "Restoring up DB Table [$TABLENAME]" + ionice -c3 nice -n19 mysql --defaults-file=$SQLCONFFILE $DBNAME < $BACKUPFILE + sleep $SLEEPTIME + else + echo "Missing backup file [$$TABLENAME]" + sleep $SLEEPTIME + fi + done + rm -rf "$ROOTFOLDER/$EXPIRATION/" +else + echo "Restore in progress, aborting" +fi diff --git a/servatrice/scripts/linux/info_db_tablesize b/servatrice/scripts/linux/info_db_tablesize new file mode 100644 index 00000000..29b5e1b9 --- /dev/null +++ b/servatrice/scripts/linux/info_db_tablesize @@ -0,0 +1,8 @@ +#!/bin/bash + +#USE THIS SCRIPT TO IDENTIFY THE SIZE OF YOUR TABLES IN THE DATABASE + +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +mysql --defaults-file=$SQLCONFFILE -e 'SELECT table_name AS "Tables", round(((data_length + index_length) / 1024 / 1024), 2) "Size in MB" FROM information_schema.TABLES WHERE table_schema = "'$DBNAME'" ORDER BY (data_length + index_length) DESC;' diff --git a/servatrice/scripts/linux/maint_countrycodes b/servatrice/scripts/linux/maint_countrycodes new file mode 100644 index 00000000..32d78628 --- /dev/null +++ b/servatrice/scripts/linux/maint_countrycodes @@ -0,0 +1,36 @@ +#!/bin/bash + +# THIS SCRIPT EXPECTS TO BE EXECUTED FROM THE GITHUB SOURCE FOLDER PATH STRUCTURE +# OTHERWISE, UPDATE THE 'COUNTRYCODEIMAGEPATH' TO POINT TO THE FOLDER CONTAINING THE COUNTRY CODE IMAGES +# USE THIS SCRIPT TO COMPARE EXISTING USER ACCOUNTS TO VALID COUNTRY CODES AND CLEAR INVALID COUNTRY CODE DATA + +MODE="report" #set this to correct to fix invalid country codes, otherwise it only reports +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +COUNTRYCODEIMAGEPATH='../../../cockatrice/resources/countries' +VALIDCOUNT=0 +INVALIDCOUNT=0 + +for i in `mysql --defaults-file=$SQLCONFFILE -h localhost -e "select distinct(country) from ""$DBNAME"".""$TABLEPREFIX""_users;"` +do + if [ "$i" != "country" ]; then + if [ -f "$COUNTRYCODEIMAGEPATH/$i.svg" ]; then + ((VALIDCOUNT++)) + else + ((INVALIDCOUNT++)) + + if [ "$MODE" == "correct" ]; then + echo "$i COUNTRY CODE INVALID, ATTEMPTING TO CORRECT" + mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set country = '' where country = '$i';" + fi + fi + fi +done + +if [ "$MODE" == "correct" ]; then + mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set country = lower(country);" +fi + +echo "INVALID: $INVALIDCOUNT" +echo "VALID: $VALIDCOUNT" diff --git a/servatrice/scripts/linux/maint_inactiveaccounts b/servatrice/scripts/linux/maint_inactiveaccounts new file mode 100644 index 00000000..29446356 --- /dev/null +++ b/servatrice/scripts/linux/maint_inactiveaccounts @@ -0,0 +1,9 @@ +#!/bin/bash + +# SCHEDULE WITH CRONTAB ON A REGULAR BASIS. NUMBER OF DAYS IS THE AMOUNT OF DAYS TO KEEP INACTIVE ACCOUNTS (EX: 1 DAY REMOVES ALL INACTIVE ACCOUNTS OLDER THAN A SINGLE DAY). + +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names with in the database +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +NUMBEROFDAYS=5 #set this to the number of days to search for +mysql --defaults-file=$SQLCONFFILE -h localhost -e "delete from ""$DBNAME"".""$TABLEPREFIX""_users where active = 0 AND registrationDate < DATE_SUB(now(), INTERVAL ""$NUMBEROFDAYS"" DAY);" diff --git a/servatrice/scripts/linux/maint_logs b/servatrice/scripts/linux/maint_logs new file mode 100644 index 00000000..4e5a43e4 --- /dev/null +++ b/servatrice/scripts/linux/maint_logs @@ -0,0 +1,9 @@ +#!/bin/bash + +#SCHEDULE WITH CRONTAB AND ADJUST THE INTERVALS FOR THE NUMBER OF DAYS OF LOGS TO KEEP IN THE DATABASE + +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +NUMBEROFDAYS=10 #set this to the number of days desired +mysql --defaults-file=$SQLCONFFILE -h localhost -e 'delete from ""$DBNAME"".""$TABLEPREFIX""_log where log_time < DATE_SUB(now(), INTERVAL ""$NUMBEROFDAYS"" DAY)' diff --git a/servatrice/scripts/linux/maint_replays b/servatrice/scripts/linux/maint_replays new file mode 100644 index 00000000..bee3d0b0 --- /dev/null +++ b/servatrice/scripts/linux/maint_replays @@ -0,0 +1,8 @@ +#!/bin/bash + +# SCHEDULE WITH CRONTAB BASED ON TIME PERIOD REPLAYS SHOULD BE SAVED UNTIL (EX: SCHEDULE ONCE A WEEK TO KEEP A WEEKS WORTH OF REPLAYS IN THE DB) + +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +mysql --defaults-file=$SQLCONFFILE -h localhost -e 'truncate table ""$DBNAME"".""$TABLEPREFIX""_replays;truncate table ""$DBNAME"".""$TABLEPREFIX""_replays_access' diff --git a/servatrice/scripts/linux/maint_sessions b/servatrice/scripts/linux/maint_sessions new file mode 100644 index 00000000..28d49b03 --- /dev/null +++ b/servatrice/scripts/linux/maint_sessions @@ -0,0 +1,9 @@ +#!/bin/bash + +# SCHEDULE WITH CRONTAB TO RUN ON A REGULAR BASIS + +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +NUMBEROFDAYS=10 #set this to the number of days desired +mysql --defaults-file=$SQLCONFFILE -h localhost -e "delete from ""$DBNAME"".""$TABLEPREFIX""_sessions where start_time < DATE_SUB(now(), INTERVAL ""$NUMBEROFDAYS"" DAY)" diff --git a/servatrice/scripts/linux/setup_addfirstadmin b/servatrice/scripts/linux/setup_addfirstadmin new file mode 100644 index 00000000..abbaddcf --- /dev/null +++ b/servatrice/scripts/linux/setup_addfirstadmin @@ -0,0 +1,8 @@ +#!/bin/bash + +# SCRIPT TO ADD THE FIRST ADMIN USER NAMED SERVATRICE WITH THE PASSWORD OF PASSWORD + +DBNAME="servatrice" #set this to the database name used +TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) +SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file +mysql --defaults-file=$SQLCONFFILE -h localhost -e "insert into ""$DBNAME"".""$TABLEPREFIX""_users (admin,name,password_sha512,active) values (1,'servatrice','jbB4kSWDmjaVzMNdU13n73SpdBCJTCJ/JYm5ZBZvfxlzbISbXir+e/aSvMz86KzOoaBfidxO0s6GVd8t00qC0TNPl+udHfECaF7MsA==',1);" diff --git a/servatrice/scripts/maint_removeinactiveplayeraccounts b/servatrice/scripts/maint_removeinactiveplayeraccounts deleted file mode 100644 index cf72f069..00000000 --- a/servatrice/scripts/maint_removeinactiveplayeraccounts +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# SCHEDULE WITH CRONTAB BASED ON TIME PERIOD UNACTIVE ACCOUNT SHOULD BE REMOVED. UPDATE INTERVAL DATE TO BE NUMBER OF DAYS OLD (OR OLDER) TO REMOVE. -mysql --defaults-file=./mysql.cnf -h localhost -e 'delete from servatrice.cockatrice_users where registrationDate < DATE_SUB(now(), INTERVAL 5 DAY) AND active = 0'; diff --git a/servatrice/scripts/maint_replays b/servatrice/scripts/maint_replays deleted file mode 100644 index 1ce1b400..00000000 --- a/servatrice/scripts/maint_replays +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# SCHEDULE WITH CRONTAB BASED ON TIME PERIOD REPLAYS SHOULD BE SAVED UNTIL (EX: SCHEDULE ONCE A WEEK TO KEEP A WEEKS WORTH OF REPLAYS IN THE DB) -mysql --defaults-file=./mysql.cnf -h localhost -e 'truncate table servatrice.cockatrice_replays;truncate table servatrice.cockatrice_replays_access' diff --git a/servatrice/scripts/maint_sessions b/servatrice/scripts/maint_sessions deleted file mode 100644 index 2c41da6d..00000000 --- a/servatrice/scripts/maint_sessions +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# SCHEDULE WITH CRONTAB TO RUN ONCE A MONTH -mysql --defaults-file=./mysql.cnf -h localhost -e "delete from servatrice.cockatrice_sessions where start_time < DATE_SUB(now(), INTERVAL 1 MONTH)" diff --git a/servatrice/scripts/setup_addfirstadmin b/servatrice/scripts/setup_addfirstadmin deleted file mode 100644 index 91731559..00000000 --- a/servatrice/scripts/setup_addfirstadmin +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# SCRIPT TO ADD THE FIRST ADMIN USER NAMED SERVATRICE WITH THE PASSWORD OF PASSWORD -mysql --defaults-file=./mysql.cnf -h localhost -e "insert into servatrice.cockatrice_users (admin,name,password_sha512,active) values (1,'servatrice','jbB4kSWDmjaVzMNdU13n73SpdBCJTCJ/JYm5ZBZvfxlzbISbXir+e/aSvMz86KzOoaBfidxO0s6GVd8t00qC0TNPl+udHfECaF7MsA==',1);" diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index 31483afe..ce57df53 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -38,6 +38,17 @@ logfile=server.log ; it won't be logged. Default is empty; example: "kittens,ponies,faires" logfilters="" +; Set the time interval in seconds that servatrice will use to communicate with each connected client +; to verify the client has not timed out. Defaults is 1 seconds +clientkeepalive=1 + +; Maximum time in seconds a player can stay inactive with there client not even responding to pings, before is +; considered disconnected; default is 15 +max_player_inactivity_time=15 + +; More modern clients generate client IDs based on specific client side information. Enable this option to +' require that clients report the client ID in order to log into the server. Default is false +requireclientid=false [authentication] @@ -51,8 +62,31 @@ method=none ; if the chosen authentication method is password, here you can define the password your users will use to log in password=123456 -; Accept only registered users? default is 0 (accept unregistered users) -regonly=0 +; Accept only registered users? default is false (accept unregistered users) +regonly=false + +[users] + +; The minimum length a username can be +minnamelength=6 + +; The maximum length a username can be +maxnamelength=12 + +; If a username should be allowed to contain lowercase chars [a-z] +allowlowercase=true + +; If a username should be allowed to conatain uppercase chars [A-Z] +allowuppercase=true + +; If a username should be allowed to contain numbers [0-9] +allownumerics=true + +; Define punctuation allowed in usernames +allowedpunctuation=_.- + +; If a username can begin with punctuation defined in allowedpunctuation +allowpunctuationprefix=false [registration] @@ -60,18 +94,18 @@ regonly=0 ; Enable this feature? Default false. ;enabled=false -; Require users to provide an email address in order to register. Newly registered users will receive an -; activation token by email, and will be required to input back this token on cockatrice at the first login +; Require users to provide an email address in order to register. Newly registered users will receive an +; activation token by email, and will be required to input back this token on cockatrice at the first login ; to get their account activated. Default true. ;requireemail=true [smtp] -; Connectin type: currently supported method are "tcp", "ssl" and "tls" +; Connectin type: currently supported method are "tcp" and "ssl"; tls is autodetected if available connection=tcp -; Auth type: currently supported method are "plain" and "login" -auth=plain +; Accept all certificates: in ssl mode, enable this if your server is using an invalid/self signed certificate +acceptallcerts=false; ; Hostname or IP addres of the smtp server host=localhost @@ -96,8 +130,8 @@ subject="Cockatrice server account activation token" ; Email body. You can use these tags here: %username %token ; They will be substituted with the actual values in the email -; -body="Hi %username, thank our for registering on our Cockatrice server\r\nHere's the activation token you need to supply for activatin your account:\r\n\r\n%token\r\n\r\nHappy gaming!" +; +body="Hi %username, thank our for registering on our Cockatrice server\r\nHere's the activation token you need to supply for activating your account:\r\n\r\n%token\r\n\r\nHappy gaming!" [database] @@ -138,6 +172,10 @@ roomlist\1\name="General room" ; Room description for the room number 1 roomlist\1\description="Play anything here." +; Rooms can restrict the level of user that can join. Current supported options are none, registered, moderator, administrator. +; Default is none. +roomlist\1\permissionlevel=none + ; Wether to make users autojoin this room when connected to the server roomlist\1\autojoin=true @@ -155,16 +193,22 @@ roomlist\1\game_types\3\name="GameType3" [game] -; Maximum time in seconds a player can stay inactive, with his client hot even responding to pings, before is -; considered disconnected; default is 15 -max_player_inactivity_time=15 - ; Maximum time in seconds all players in a game can stay inactive before the game is automatically closed; ; default is 120 max_game_inactivity_time=120 +; All actions during a game are recorded and stored in the database as a replay that all participants of +; the game can go back to and review after the game is closed. This can require a fairly large amount of +; storage to save all the information. Disable this option to prevent the storing of replay data in +; the database. Default value is true. +store_replays=true [security] +; You may want to restrict the number of users that can connect to your server at any given time. +enable_max_user_limit=false + +; Maximum number of users that can connect to the server, default is 500. +max_users_total=500 ; Maximum number of users that can connect from the same IP address; useful to avoid bots, default is 4 max_users_per_address=4 diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index 0704b05d..9a6173a5 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` ( PRIMARY KEY (`version`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -INSERT INTO cockatrice_schema_version VALUES(1); +INSERT INTO cockatrice_schema_version VALUES(7); CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` ( `id` int(7) unsigned zerofill NOT NULL auto_increment, @@ -83,6 +83,8 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` ( `registrationDate` datetime NOT NULL, `active` tinyint(1) NOT NULL, `token` binary(16) NOT NULL, + `clientid` varchar(15) NOT NULL, + `last_login` datetime NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `token` (`token`), @@ -131,6 +133,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_bans` ( `minutes` int(6) NOT NULL, `reason` text NOT NULL, `visible_reason` text NOT NULL, + `clientid` varchar(15) NOT NULL, PRIMARY KEY (`user_name`,`time_from`), KEY `time_from` (`time_from`,`ip_address`), KEY `ip_address` (`ip_address`) @@ -143,6 +146,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_sessions` ( `ip_address` char(15) COLLATE utf8_unicode_ci NOT NULL, `start_time` datetime NOT NULL, `end_time` datetime DEFAULT NULL, + `clientid` varchar(15) NOT NULL, PRIMARY KEY (`id`), KEY `username` (`user_name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; @@ -179,6 +183,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_rooms` ( `id` int(7) unsigned NOT NULL auto_increment, `name` varchar(50) NOT NULL, `descr` varchar(255) NOT NULL, + `permissionlevel` varchar(20) NOT NULL, `auto_join` tinyint(1) default 0, `join_message` varchar(255) NOT NULL, PRIMARY KEY (`id`) @@ -205,3 +210,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_log` ( KEY `target_id` (`target_id`), KEY `target_name` (`target_name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_activation_emails` ( + `name` varchar(35) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index 22048ca0..499b468d 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -28,21 +28,18 @@ #include "servatrice.h" #include "server_logger.h" #include "settingscache.h" +#include "signalhandler.h" +#include "smtpclient.h" #include "rng_sfmt.h" #include "version_string.h" #include -#ifdef Q_OS_UNIX -#include -#include -#include -#endif - -#define SIGSEGV_TRACE_LINES 40 RNG_Abstract *rng; ServerLogger *logger; QThread *loggerThread; SettingsCache *settingsCache; +SignalHandler *signalhandler; +SmtpClient *smtpClient; /* Prototypes */ @@ -55,9 +52,6 @@ void myMessageOutput2(QtMsgType type, const char *msg); void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg); void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg); #endif -#ifdef Q_OS_UNIX -void sigSegvHandler(int sig); -#endif /* Implementations */ @@ -130,32 +124,6 @@ void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QStr } #endif -#ifdef Q_OS_UNIX -void sigSegvHandler(int sig) -{ - void *array[SIGSEGV_TRACE_LINES]; - size_t size; - - // get void*'s for all entries on the stack - size = backtrace(array, SIGSEGV_TRACE_LINES); - - // print out all the frames to stderr - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - - if (sig == SIGSEGV) - logger->logMessage("CRASH: SIGSEGV"); - else if (sig == SIGABRT) - logger->logMessage("CRASH: SIGABRT"); - - logger->deleteLater(); - loggerThread->wait(); - delete loggerThread; - - raise(sig); -} -#endif - int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); @@ -202,23 +170,8 @@ int main(int argc, char *argv[]) qInstallMessageHandler(myMessageOutput2); #endif -#ifdef Q_OS_UNIX - struct sigaction hup; - hup.sa_handler = ServerLogger::hupSignalHandler; - sigemptyset(&hup.sa_mask); - hup.sa_flags = 0; - hup.sa_flags |= SA_RESTART; - sigaction(SIGHUP, &hup, 0); - - struct sigaction segv; - segv.sa_handler = sigSegvHandler; - segv.sa_flags = SA_RESETHAND; - sigemptyset(&segv.sa_mask); - sigaction(SIGSEGV, &segv, 0); - sigaction(SIGABRT, &segv, 0); - - signal(SIGPIPE, SIG_IGN); -#endif + signalhandler = new SignalHandler(); + rng = new RNG_SFMT; std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl; @@ -230,6 +183,8 @@ int main(int argc, char *argv[]) testRNG(); if (testHashFunction) testHash(); + + smtpClient = new SmtpClient(); Servatrice *server = new Servatrice(); QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection); @@ -249,7 +204,9 @@ int main(int argc, char *argv[]) std::cerr << "-------------------------" << std::endl; } + delete smtpClient; delete rng; + delete signalhandler; delete settingsCache; logger->deleteLater(); diff --git a/servatrice/src/main.h b/servatrice/src/main.h index 037e9bba..6ce4bb26 100644 --- a/servatrice/src/main.h +++ b/servatrice/src/main.h @@ -2,6 +2,13 @@ #define MAIN_H class ServerLogger; +class QThread; +class SettingsCache; +class SmtpClient; + extern ServerLogger *logger; +extern QThread *loggerThread; +extern SettingsCache *settingsCache; +extern SmtpClient *smtpClient; #endif diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index ad6f6ada..701902f8 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -34,6 +34,7 @@ #include "server_logger.h" #include "main.h" #include "decklist.h" +#include "smtpclient.h" #include "pb/event_server_message.pb.h" #include "pb/event_server_shutdown.pb.h" #include "pb/event_connection_closed.pb.h" @@ -141,7 +142,8 @@ bool Servatrice::initServer() { serverName = settingsCache->value("server/name", "My Cockatrice server").toString(); serverId = settingsCache->value("server/id", 0).toInt(); - bool regServerOnly = settingsCache->value("authentication/regonly", 0).toBool(); + clientIdRequired = settingsCache->value("server/requireclientid",0).toBool(); + regServerOnly = settingsCache->value("authentication/regonly", 0).toBool(); const QString authenticationMethodStr = settingsCache->value("authentication/method").toString(); if (authenticationMethodStr == "sql") { @@ -160,10 +162,20 @@ bool Servatrice::initServer() authenticationMethod = AuthenticationNone; } + qDebug() << "Store Replays: " << settingsCache->value("game/store_replays", true).toBool(); + qDebug() << "Client ID Required: " << clientIdRequired; + bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool(); + qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled; + + if (maxUserLimitEnabled){ + int maxUserLimit = settingsCache->value("security/max_users_total", 500).toInt(); + qDebug() << "Maximum user limit: " << maxUserLimit; + } + bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); - qDebug() << "Registration enabled: " << registrationEnabled; + qDebug() << "Registration enabled: " << regServerOnly; if (registrationEnabled) qDebug() << "Require email address to register: " << requireEmailForRegistration; @@ -199,7 +211,7 @@ bool Servatrice::initServer() const QString roomMethod = settingsCache->value("rooms/method").toString(); if (roomMethod == "sql") { - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, name, descr, auto_join, join_message from {prefix}_rooms order by id asc"); + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, name, descr, permissionlevel, auto_join, join_message from {prefix}_rooms order by id asc"); servatriceDatabaseInterface->execSqlQuery(query); while (query->next()) { QSqlQuery *query2 = servatriceDatabaseInterface->prepareQuery("select name from {prefix}_rooms_gametypes where id_room = :id_room"); @@ -212,8 +224,9 @@ bool Servatrice::initServer() addRoom(new Server_Room(query->value(0).toInt(), query->value(1).toString(), query->value(2).toString(), - query->value(3).toInt(), - query->value(4).toString(), + query->value(3).toString().toLower(), + query->value(4).toInt(), + query->value(5).toString(), gameTypes, this )); @@ -235,6 +248,7 @@ bool Servatrice::initServer() i, settingsCache->value("name").toString(), settingsCache->value("description").toString(), + settingsCache->value("permissionlevel").toString().toLower(), settingsCache->value("autojoin").toBool(), settingsCache->value("joinmessage").toString(), gameTypes, @@ -250,6 +264,7 @@ bool Servatrice::initServer() 0, "General room", "Play anything here.", + "none", true, "", QStringList("Standard"), @@ -264,8 +279,8 @@ bool Servatrice::initServer() updateLoginMessage(); maxGameInactivityTime = settingsCache->value("game/max_game_inactivity_time", 120).toInt(); - maxPlayerInactivityTime = settingsCache->value("game/max_player_inactivity_time", 15).toInt(); - + maxPlayerInactivityTime = settingsCache->value("server/max_player_inactivity_time", 15).toInt(); + pingClockInterval = settingsCache->value("server/clientkeepalive", 1).toInt(); maxUsersPerAddress = settingsCache->value("security/max_users_per_address", 4).toInt(); messageCountingInterval = settingsCache->value("security/message_counting_interval", 10).toInt(); maxMessageCountPerInterval = settingsCache->value("security/max_message_count_per_interval", 15).toInt(); @@ -274,90 +289,90 @@ bool Servatrice::initServer() commandCountingInterval = settingsCache->value("game/command_counting_interval", 10).toInt(); maxCommandCountPerInterval = settingsCache->value("game/max_command_count_per_interval", 20).toInt(); - try { if (settingsCache->value("servernetwork/active", 0).toInt()) { - qDebug() << "Connecting to ISL network."; - const QString certFileName = settingsCache->value("servernetwork/ssl_cert").toString(); - const QString keyFileName = settingsCache->value("servernetwork/ssl_key").toString(); - qDebug() << "Loading certificate..."; - QFile certFile(certFileName); - if (!certFile.open(QIODevice::ReadOnly)) - throw QString("Error opening certificate file: %1").arg(certFileName); - QSslCertificate cert(&certFile); + try { if (settingsCache->value("servernetwork/active", 0).toInt()) { + qDebug() << "Connecting to ISL network."; + const QString certFileName = settingsCache->value("servernetwork/ssl_cert").toString(); + const QString keyFileName = settingsCache->value("servernetwork/ssl_key").toString(); + qDebug() << "Loading certificate..."; + QFile certFile(certFileName); + if (!certFile.open(QIODevice::ReadOnly)) + throw QString("Error opening certificate file: %1").arg(certFileName); + QSslCertificate cert(&certFile); #if QT_VERSION < 0x050000 - if (!cert.isValid()) - throw(QString("Invalid certificate.")); + if (!cert.isValid()) + throw(QString("Invalid certificate.")); #else - const QDateTime currentTime = QDateTime::currentDateTime(); - if(currentTime < cert.effectiveDate() || - currentTime > cert.expiryDate() || - cert.isBlacklisted()) - throw(QString("Invalid certificate.")); + const QDateTime currentTime = QDateTime::currentDateTime(); + if(currentTime < cert.effectiveDate() || + currentTime > cert.expiryDate() || + cert.isBlacklisted()) + throw(QString("Invalid certificate.")); #endif - qDebug() << "Loading private key..."; - QFile keyFile(keyFileName); - if (!keyFile.open(QIODevice::ReadOnly)) - throw QString("Error opening private key file: %1").arg(keyFileName); - QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); - if (key.isNull()) - throw QString("Invalid private key."); + qDebug() << "Loading private key..."; + QFile keyFile(keyFileName); + if (!keyFile.open(QIODevice::ReadOnly)) + throw QString("Error opening private key file: %1").arg(keyFileName); + QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + if (key.isNull()) + throw QString("Invalid private key."); - QMutableListIterator serverIterator(serverList); - while (serverIterator.hasNext()) { - const ServerProperties &prop = serverIterator.next(); - if (prop.cert == cert) { - serverIterator.remove(); - continue; - } + QMutableListIterator serverIterator(serverList); + while (serverIterator.hasNext()) { + const ServerProperties &prop = serverIterator.next(); + if (prop.cert == cert) { + serverIterator.remove(); + continue; + } - QThread *thread = new QThread; - thread->setObjectName("isl_" + QString::number(prop.id)); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + QThread *thread = new QThread; + thread->setObjectName("isl_" + QString::number(prop.id)); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); - interface->moveToThread(thread); - connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); + IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); + interface->moveToThread(thread); + connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); - thread->start(); - QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); - } + thread->start(); + QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); + } - const int networkPort = settingsCache->value("servernetwork/port", 14747).toInt(); - qDebug() << "Starting ISL server on port" << networkPort; + const int networkPort = settingsCache->value("servernetwork/port", 14747).toInt(); + qDebug() << "Starting ISL server on port" << networkPort; - islServer = new Servatrice_IslServer(this, cert, key, this); - if (islServer->listen(QHostAddress::Any, networkPort)) - qDebug() << "ISL server listening."; - else - throw QString("islServer->listen()"); - } } catch (QString error) { - qDebug() << "ERROR --" << error; - return false; - } + islServer = new Servatrice_IslServer(this, cert, key, this); + if (islServer->listen(QHostAddress::Any, networkPort)) + qDebug() << "ISL server listening."; + else + throw QString("islServer->listen()"); + } } catch (QString error) { + qDebug() << "ERROR --" << error; + return false; + } - pingClock = new QTimer(this); - connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); - pingClock->start(1000); + pingClock = new QTimer(this); + connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); + pingClock->start(pingClockInterval * 1000); - int statusUpdateTime = settingsCache->value("server/statusupdate", 15000).toInt(); - statusUpdateClock = new QTimer(this); - connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); - if (statusUpdateTime != 0) { - qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; - statusUpdateClock->start(statusUpdateTime); - } + int statusUpdateTime = settingsCache->value("server/statusupdate", 15000).toInt(); + statusUpdateClock = new QTimer(this); + connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); + if (statusUpdateTime != 0) { + qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; + statusUpdateClock->start(statusUpdateTime); + } - const int numberPools = settingsCache->value("server/number_pools", 1).toInt(); - gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this); - gameServer->setMaxPendingConnections(1000); - const int gamePort = settingsCache->value("server/port", 4747).toInt(); - qDebug() << "Starting server on port" << gamePort; - if (gameServer->listen(QHostAddress::Any, gamePort)) - qDebug() << "Server listening."; - else { - qDebug() << "gameServer->listen(): Error:" << gameServer->errorString(); - return false; - } - return true; + const int numberPools = settingsCache->value("server/number_pools", 1).toInt(); + gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this); + gameServer->setMaxPendingConnections(1000); + const int gamePort = settingsCache->value("server/port", 4747).toInt(); + qDebug() << "Starting server on port" << gamePort; + if (gameServer->listen(QHostAddress::Any, gamePort)) + qDebug() << "Server listening."; + else { + qDebug() << "gameServer->listen(): Error:" << gameServer->errorString(); + return false; + } + return true; } void Servatrice::addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface) @@ -367,20 +382,20 @@ void Servatrice::addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterf void Servatrice::updateServerList() { - qDebug() << "Updating server list..."; + qDebug() << "Updating server list..."; - serverListMutex.lock(); - serverList.clear(); + serverListMutex.lock(); + serverList.clear(); - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, ssl_cert, hostname, address, game_port, control_port from {prefix}_servers order by id asc"); - servatriceDatabaseInterface->execSqlQuery(query); - while (query->next()) { - ServerProperties prop(query->value(0).toInt(), QSslCertificate(query->value(1).toString().toUtf8()), query->value(2).toString(), QHostAddress(query->value(3).toString()), query->value(4).toInt(), query->value(5).toInt()); - serverList.append(prop); - qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6").arg(prop.id).arg(QString(prop.cert.digest().toHex())).arg(prop.hostname).arg(prop.address.toString()).arg(prop.gamePort).arg(prop.controlPort); - } + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, ssl_cert, hostname, address, game_port, control_port from {prefix}_servers order by id asc"); + servatriceDatabaseInterface->execSqlQuery(query); + while (query->next()) { + ServerProperties prop(query->value(0).toInt(), QSslCertificate(query->value(1).toString().toUtf8()), query->value(2).toString(), QHostAddress(query->value(3).toString()), query->value(4).toInt(), query->value(5).toInt()); + serverList.append(prop); + qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6").arg(prop.id).arg(QString(prop.cert.digest().toHex())).arg(prop.hostname).arg(prop.address.toString()).arg(prop.gamePort).arg(prop.controlPort); + } - serverListMutex.unlock(); + serverListMutex.unlock(); } QList Servatrice::getServerList() const @@ -399,7 +414,7 @@ int Servatrice::getUsersWithAddress(const QHostAddress &address) const for (int i = 0; i < clients.size(); ++i) if (static_cast(clients[i])->getPeerAddress() == address) ++result; - + return result; } @@ -465,6 +480,32 @@ void Servatrice::statusUpdate() query->bindValue(":tx", tx); query->bindValue(":rx", rx); servatriceDatabaseInterface->execSqlQuery(query); + + // send activation emails + bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); + bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); + if (registrationEnabled && requireEmailForRegistration) + { + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_activation_emails a left join {prefix}_users b on a.name = b.name"); + if (!servatriceDatabaseInterface->execSqlQuery(query)) + return; + + QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name"); + + while (query->next()) { + const QString userName = query->value(0).toString(); + const QString emailAddress = query->value(1).toString(); + const QString token = query->value(2).toString(); + + if(smtpClient->enqueueActivationTokenMail(userName, emailAddress, token)) + { + queryDelete->bindValue(":name", userName); + servatriceDatabaseInterface->execSqlQuery(queryDelete); + } + } + + smtpClient->sendAllEmails(); + } } void Servatrice::scheduleShutdown(const QString &reason, int minutes) @@ -516,7 +557,7 @@ void Servatrice::shutdownTimeout() clients[i]->sendProtocolItem(*se); clientsLock.unlock(); delete se; - + if (!shutdownMinutes) deleteLater(); } diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 717e5ab8..4e4a74b2 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -43,124 +43,128 @@ class ServerSocketInterface; class IslInterface; class Servatrice_GameServer : public QTcpServer { - Q_OBJECT + Q_OBJECT private: - Servatrice *server; - QList connectionPools; + Servatrice *server; + QList connectionPools; public: - Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent = 0); - ~Servatrice_GameServer(); + Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent = 0); + ~Servatrice_GameServer(); protected: #if QT_VERSION < 0x050000 - void incomingConnection(int socketDescriptor); + void incomingConnection(int socketDescriptor); #else - void incomingConnection(qintptr socketDescriptor); + void incomingConnection(qintptr socketDescriptor); #endif }; class Servatrice_IslServer : public QTcpServer { - Q_OBJECT + Q_OBJECT private: - Servatrice *server; - QSslCertificate cert; - QSslKey privateKey; + Servatrice *server; + QSslCertificate cert; + QSslKey privateKey; public: - Servatrice_IslServer(Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0) - : QTcpServer(parent), server(_server), cert(_cert), privateKey(_privateKey) { } + Servatrice_IslServer(Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0) + : QTcpServer(parent), server(_server), cert(_cert), privateKey(_privateKey) { } protected: #if QT_VERSION < 0x050000 - void incomingConnection(int socketDescriptor); + void incomingConnection(int socketDescriptor); #else - void incomingConnection(qintptr socketDescriptor); + void incomingConnection(qintptr socketDescriptor); #endif }; class ServerProperties { public: - int id; - QSslCertificate cert; - QString hostname; - QHostAddress address; - int gamePort; - int controlPort; - - ServerProperties(int _id, const QSslCertificate &_cert, const QString &_hostname, const QHostAddress &_address, int _gamePort, int _controlPort) - : id(_id), cert(_cert), hostname(_hostname), address(_address), gamePort(_gamePort), controlPort(_controlPort) { } + int id; + QSslCertificate cert; + QString hostname; + QHostAddress address; + int gamePort; + int controlPort; + + ServerProperties(int _id, const QSslCertificate &_cert, const QString &_hostname, const QHostAddress &_address, int _gamePort, int _controlPort) + : id(_id), cert(_cert), hostname(_hostname), address(_address), gamePort(_gamePort), controlPort(_controlPort) { } }; class Servatrice : public Server { - Q_OBJECT + Q_OBJECT public: - enum AuthenticationMethod { AuthenticationNone, AuthenticationSql, AuthenticationPassword }; + enum AuthenticationMethod { AuthenticationNone, AuthenticationSql, AuthenticationPassword }; private slots: - void statusUpdate(); - void shutdownTimeout(); + void statusUpdate(); + void shutdownTimeout(); protected: - void doSendIslMessage(const IslMessage &msg, int serverId); + void doSendIslMessage(const IslMessage &msg, int serverId); private: - enum DatabaseType { DatabaseNone, DatabaseMySql }; - AuthenticationMethod authenticationMethod; - DatabaseType databaseType; - QTimer *pingClock, *statusUpdateClock; - Servatrice_GameServer *gameServer; - Servatrice_IslServer *islServer; - QString serverName; - mutable QMutex loginMessageMutex; - QString loginMessage; - QString dbPrefix; - Servatrice_DatabaseInterface *servatriceDatabaseInterface; - int serverId; - int uptime; - QMutex txBytesMutex, rxBytesMutex; - quint64 txBytes, rxBytes; - int maxGameInactivityTime, maxPlayerInactivityTime; - int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser, commandCountingInterval, maxCommandCountPerInterval; + enum DatabaseType { DatabaseNone, DatabaseMySql }; + AuthenticationMethod authenticationMethod; + DatabaseType databaseType; + QTimer *pingClock, *statusUpdateClock; + Servatrice_GameServer *gameServer; + Servatrice_IslServer *islServer; + QString serverName; + mutable QMutex loginMessageMutex; + QString loginMessage; + QString dbPrefix; + Servatrice_DatabaseInterface *servatriceDatabaseInterface; + int serverId; + int uptime; + QMutex txBytesMutex, rxBytesMutex; + quint64 txBytes, rxBytes; + int maxGameInactivityTime, maxPlayerInactivityTime; + int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser, commandCountingInterval, maxCommandCountPerInterval, pingClockInterval; - QString shutdownReason; - int shutdownMinutes; - QTimer *shutdownTimer; - bool isFirstShutdownMessage; - - mutable QMutex serverListMutex; - QList serverList; - void updateServerList(); - - QMap islInterfaces; + QString shutdownReason; + int shutdownMinutes; + QTimer *shutdownTimer; + bool isFirstShutdownMessage, clientIdRequired, regServerOnly; + + mutable QMutex serverListMutex; + QList serverList; + void updateServerList(); + + QMap islInterfaces; public slots: - void scheduleShutdown(const QString &reason, int minutes); - void updateLoginMessage(); + void scheduleShutdown(const QString &reason, int minutes); + void updateLoginMessage(); public: - Servatrice(QObject *parent = 0); - ~Servatrice(); - bool initServer(); - QString getServerName() const { return serverName; } - QString getLoginMessage() const { QMutexLocker locker(&loginMessageMutex); return loginMessage; } - bool getGameShouldPing() const { return true; } - int getMaxGameInactivityTime() const { return maxGameInactivityTime; } - int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; } - int getMaxUsersPerAddress() const { return maxUsersPerAddress; } - int getMessageCountingInterval() const { return messageCountingInterval; } - int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; } - int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; } - int getMaxGamesPerUser() const { return maxGamesPerUser; } + Servatrice(QObject *parent = 0); + ~Servatrice(); + bool initServer(); + QString getServerName() const { return serverName; } + QString getLoginMessage() const { QMutexLocker locker(&loginMessageMutex); return loginMessage; } + bool permitUnregisteredUsers() const { return authenticationMethod != AuthenticationNone; } + bool getGameShouldPing() const { return true; } + bool getClientIdRequired() const { return clientIdRequired; } + bool getRegOnlyServer() const { return regServerOnly; } + int getPingClockInterval() const { return pingClockInterval; } + int getMaxGameInactivityTime() const { return maxGameInactivityTime; } + int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; } + int getMaxUsersPerAddress() const { return maxUsersPerAddress; } + int getMessageCountingInterval() const { return messageCountingInterval; } + int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; } + int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; } + int getMaxGamesPerUser() const { return maxGamesPerUser; } int getCommandCountingInterval() const { return commandCountingInterval; } int getMaxCommandCountPerInterval() const { return maxCommandCountPerInterval; } - AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; } - QString getDbPrefix() const { return dbPrefix; } - int getServerId() const { return serverId; } - int getUsersWithAddress(const QHostAddress &address) const; - QList getUsersWithAddressAsList(const QHostAddress &address) const; - void incTxBytes(quint64 num); - void incRxBytes(quint64 num); - void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface); - - bool islConnectionExists(int serverId) const; - void addIslInterface(int serverId, IslInterface *interface); - void removeIslInterface(int serverId); - QReadWriteLock islLock; + AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; } + QString getDbPrefix() const { return dbPrefix; } + int getServerId() const { return serverId; } + int getUsersWithAddress(const QHostAddress &address) const; + QList getUsersWithAddressAsList(const QHostAddress &address) const; + void incTxBytes(quint64 num); + void incRxBytes(quint64 num); + void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface); - QList getServerList() const; + bool islConnectionExists(int serverId) const; + void addIslInterface(int serverId, IslInterface *interface); + void removeIslInterface(int serverId); + QReadWriteLock islLock; + + QList getServerList() const; }; #endif diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index bfbbcd00..ae8279fa 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -44,7 +44,7 @@ bool Servatrice_DatabaseInterface::initDatabase(const QString &type, const QStri sqlDatabase.setDatabaseName(databaseName); sqlDatabase.setUserName(userName); sqlDatabase.setPassword(password); - + return openDatabase(); } @@ -52,7 +52,7 @@ bool Servatrice_DatabaseInterface::openDatabase() { if (sqlDatabase.isOpen()) sqlDatabase.close(); - + const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); qDebug() << QString("[%1] Opening database...").arg(poolStr); if (!sqlDatabase.open()) { @@ -69,10 +69,13 @@ bool Servatrice_DatabaseInterface::openDatabase() if (versionQuery->next()) { const int dbversion = versionQuery->value(0).toInt(); const int expectedversion = DATABASE_SCHEMA_VERSION; - if(dbversion != expectedversion) + if(dbversion < expectedversion) { qCritical() << QString("[%1] Error opening database: the database schema version is too old, you need to run the migrations to update it from version %2 to version %3").arg(poolStr).arg(dbversion).arg(expectedversion); return false; + } else if(dbversion > expectedversion) { + qCritical() << QString("[%1] Error opening database: the database schema version %2 is too new, you need to update servatrice (this servatrice actually uses version %3)").arg(poolStr).arg(dbversion).arg(expectedversion); + return false; } } else { qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the cockatrice_schema_version contains a single record)").arg(poolStr); @@ -89,7 +92,7 @@ bool Servatrice_DatabaseInterface::checkSql() { if (!sqlDatabase.isValid()) return false; - + if (!sqlDatabase.exec("select 1").isActive()) return openDatabase(); return true; @@ -118,16 +121,35 @@ bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query) return false; } -bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user) +bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString & error) { - static QRegExp re = QRegExp("[a-zA-Z0-9_\\.-]+"); - return re.exactMatch(user); -} + int minNameLength = settingsCache->value("users/minnamelength", 6).toInt(); + int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt(); + bool allowLowercase = settingsCache->value("users/allowlowercase", true).toBool(); + bool allowUppercase = settingsCache->value("users/allowuppercase", true).toBool(); + bool allowNumerics = settingsCache->value("users/allownumerics", true).toBool(); + bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool(); + QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString(); + error = QString("%1|%2|%3|%4|%5|%6|%7").arg(minNameLength).arg(maxNameLength).arg(allowLowercase).arg(allowUppercase).arg(allowNumerics).arg(allowPunctuationPrefix).arg(allowedPunctuation); -// TODO move this to Server -bool Servatrice_DatabaseInterface::getRequireRegistration() -{ - return settingsCache->value("authentication/regonly", 0).toBool(); + if (user.length() < minNameLength || user.length() > maxNameLength) + return false; + + if (!allowPunctuationPrefix && allowedPunctuation.contains(user.at(0))) + return false; + + QString regEx("["); + if (allowLowercase) + regEx.append("a-z"); + if (allowUppercase) + regEx.append("A-Z"); + if(allowNumerics) + regEx.append("0-9"); + regEx.append(QRegExp::escape(allowedPunctuation)); + regEx.append("]+"); + + static QRegExp re = QRegExp(regEx); + return re.exactMatch(user); } bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active) @@ -207,7 +229,7 @@ QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const & } } -AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &banSecondsLeft) +AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &banSecondsLeft) { switch (server->getAuthenticationMethod()) { case Servatrice::AuthenticationNone: return UnknownUser; @@ -222,25 +244,25 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot if (!checkSql()) return UnknownUser; - if (!usernameIsValid(user)) + if (!usernameIsValid(user, reasonStr)) return UsernameInvalid; - - if (checkUserIsBanned(handler->getAddress(), user, reasonStr, banSecondsLeft)) + + if (checkUserIsBanned(handler->getAddress(), user, clientId, reasonStr, banSecondsLeft)) return UserIsBanned; - + QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { qDebug("Login denied: SQL error"); return NotLoggedIn; } - + if (passwordQuery->next()) { const QString correctPassword = passwordQuery->value(0).toString(); const bool userIsActive = passwordQuery->value(1).toBool(); if(!userIsActive) { qDebug("Login denied: user not active"); - return UserIsInactive; + return UserIsInactive; } if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) { qDebug("Login accepted: password right"); @@ -258,7 +280,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot return UnknownUser; } -bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining) +bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining) { if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql) return false; @@ -269,14 +291,51 @@ bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, c } return - checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) - || checkUserIsNameBanned(userName, banReason, banSecondsRemaining); + checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) || checkUserIsNameBanned(userName, banReason, banSecondsRemaining) || checkUserIsIdBanned(clientId, banReason, banSecondsRemaining); } +bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining) +{ + if (clientId.isEmpty()) + return false; + + QSqlQuery *idBanQuery = prepareQuery( + "select" + " timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute))," + " b.minutes <=> 0," + " b.visible_reason" + " from {prefix}_bans b" + " where" + " b.time_from = (select max(c.time_from)" + " from {prefix}_bans c" + " where c.clientid = :id)" + " and b.clientid = :id2"); + + idBanQuery->bindValue(":id", clientId); + idBanQuery->bindValue(":id2", clientId); + if (!execSqlQuery(idBanQuery)) { + qDebug() << "Id ban check failed: SQL error." << idBanQuery->lastError(); + return false; + } + + if (idBanQuery->next()) { + const int secondsLeft = idBanQuery->value(0).toInt(); + const bool permanentBan = idBanQuery->value(1).toInt(); + if ((secondsLeft > 0) || permanentBan) { + banReason = idBanQuery->value(2).toString(); + banSecondsRemaining = permanentBan ? 0 : secondsLeft; + qDebug() << "User is banned by client id" << clientId; + return true; + } + } + return false; +} + + bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining) { - QSqlQuery *nameBanQuery = prepareQuery("select time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1"); + QSqlQuery *nameBanQuery = prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1"); nameBanQuery->bindValue(":name1", userName); nameBanQuery->bindValue(":name2", userName); if (!execSqlQuery(nameBanQuery)) { @@ -285,7 +344,7 @@ bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName } if (nameBanQuery->next()) { - const int secondsLeft = -nameBanQuery->value(0).toInt(); + const int secondsLeft = nameBanQuery->value(0).toInt(); const bool permanentBan = nameBanQuery->value(1).toInt(); if ((secondsLeft > 0) || permanentBan) { banReason = nameBanQuery->value(2).toString(); @@ -301,7 +360,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, { QSqlQuery *ipBanQuery = prepareQuery( "select" - " time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute)))," + " timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute))," " b.minutes <=> 0," " b.visible_reason" " from {prefix}_bans b" @@ -319,7 +378,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, } if (ipBanQuery->next()) { - const int secondsLeft = -ipBanQuery->value(0).toInt(); + const int secondsLeft = ipBanQuery->value(0).toInt(); const bool permanentBan = ipBanQuery->value(1).toInt(); if ((secondsLeft > 0) || permanentBan) { banReason = ipBanQuery->value(2).toString(); @@ -335,7 +394,7 @@ bool Servatrice_DatabaseInterface::activeUserExists(const QString &user) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); - + QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name and active = 1"); query->bindValue(":name", user); if (!execSqlQuery(query)) @@ -349,7 +408,7 @@ bool Servatrice_DatabaseInterface::userExists(const QString &user) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); - + QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name"); query->bindValue(":name", user); if (!execSqlQuery(query)) @@ -377,13 +436,13 @@ bool Servatrice_DatabaseInterface::isInBuddyList(const QString &whoseList, const { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return false; - + if (!checkSql()) return false; - + int id1 = getUserIdInDB(whoseList); int id2 = getUserIdInDB(who); - + QSqlQuery *query = prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2"); query->bindValue(":id_user1", id1); query->bindValue(":id_user2", id2); @@ -396,13 +455,13 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return false; - + if (!checkSql()) return false; - + int id1 = getUserIdInDB(whoseList); int id2 = getUserIdInDB(who); - + QSqlQuery *query = prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2"); query->bindValue(":id_user1", id1); query->bindValue(":id_user2", id2); @@ -414,11 +473,11 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId) { ServerInfo_User result; - + if (withId) result.set_id(query->value(0).toInt()); result.set_name(query->value(1).toString().toStdString()); - + const int is_admin = query->value(2).toInt(); int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered; if (is_admin == 1) @@ -451,6 +510,14 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer qint64 accountAgeInSeconds = regDate.secsTo(QDateTime::currentDateTime()); result.set_accountage_secs(accountAgeInSeconds); } + + const QString email = query->value(8).toString(); + if (!email.isEmpty()) + result.set_email(email.toStdString()); + + const QString clientid = query->value(9).toString(); + if (!clientid.isEmpty()) + result.set_clientid(clientid.toStdString()); } return result; } @@ -460,16 +527,16 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b ServerInfo_User result; result.set_name(name.toStdString()); result.set_user_level(ServerInfo_User::IsUser); - + if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (!checkSql()) return result; - - QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate from {prefix}_users where name = :name and active = 1"); + + QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email, clientid from {prefix}_users where name = :name and active = 1"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; - + if (query->next()) return evalUserQueryResult(query, true, withId); else @@ -502,25 +569,27 @@ void Servatrice_DatabaseInterface::unlockSessionTables() bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName) { // Call only after lockSessionTables(). - - QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and end_time is null"); + + QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null"); + query->bindValue(":id_server", server->getServerId()); query->bindValue(":user_name", userName); execSqlQuery(query); return query->next(); } -qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address) +qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address, const QString &clientId) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return -1; - + if (!checkSql()) return -1; - - QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time) values(:user_name, :id_server, :ip_address, NOW())"); + + QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time, clientid) values(:user_name, :id_server, :ip_address, NOW(), :client_id)"); query->bindValue(":user_name", userName); query->bindValue(":id_server", server->getServerId()); query->bindValue(":ip_address", address); + query->bindValue(":client_id", clientId); if (execSqlQuery(query)) return query->lastInsertId().toInt(); return -1; @@ -530,13 +599,13 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return; - + if (!checkSql()) return; QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write"); execSqlQuery(query); - + query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session"); query->bindValue(":id_session", sessionId); execSqlQuery(query); @@ -548,7 +617,7 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId) QMap Servatrice_DatabaseInterface::getBuddyList(const QString &name) { QMap result; - + if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); @@ -556,7 +625,7 @@ QMap Servatrice_DatabaseInterface::getBuddyList(const query->bindValue(":name", name); if (!execSqlQuery(query)) return result; - + while (query->next()) { const ServerInfo_User &temp = evalUserQueryResult(query, false); result.insert(QString::fromStdString(temp.name()), temp); @@ -568,7 +637,7 @@ QMap Servatrice_DatabaseInterface::getBuddyList(const QMap Servatrice_DatabaseInterface::getIgnoreList(const QString &name) { QMap result; - + if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); @@ -576,7 +645,7 @@ QMap Servatrice_DatabaseInterface::getIgnoreList(const query->bindValue(":name", name); if (!execSqlQuery(query)) return result; - + while (query->next()) { ServerInfo_User temp = evalUserQueryResult(query, false); result.insert(QString::fromStdString(temp.name()), temp); @@ -589,13 +658,13 @@ int Servatrice_DatabaseInterface::getNextGameId() { if (!sqlDatabase.isValid()) return server->getNextLocalGameId(); - + if (!checkSql()) return -1; - + QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())"); execSqlQuery(query); - + return query->lastInsertId().toInt(); } @@ -603,10 +672,10 @@ int Servatrice_DatabaseInterface::getNextReplayId() { if (!checkSql()) return -1; - + QSqlQuery *query = prepareQuery("insert into {prefix}_replays () values ()"); execSqlQuery(query); - + return query->lastInsertId().toInt(); } @@ -614,7 +683,10 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, { if (!checkSql()) return; - + + if (!settingsCache->value("game/store_replays", 1).toBool() ) + return; + QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames; QSetIterator playerIterator(allPlayersEver); while (playerIterator.hasNext()) { @@ -632,20 +704,20 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, userIds.append(id); replayNames.append(QString::fromStdString(gameInfo.description())); } - + QVariantList replayIds, replayGameIds, replayDurations, replayBlobs; for (int i = 0; i < replayList.size(); ++i) { QByteArray blob; const unsigned int size = replayList[i]->ByteSize(); blob.resize(size); replayList[i]->SerializeToArray(blob.data(), size); - + replayIds.append(QVariant((qulonglong) replayList[i]->replay_id())); replayGameIds.append(gameInfo.game_id()); replayDurations.append(replayList[i]->duration_seconds()); replayBlobs.append(blob); } - + { QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, creator_name=:creator_name, password=:password, game_types=:game_types, player_count=:player_count, time_finished=now() where id=:id_game"); query->bindValue(":room_name", roomName); @@ -684,17 +756,17 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId) { checkSql(); - + QSqlQuery *query = prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user"); query->bindValue(":id", deckId); query->bindValue(":id_user", userId); execSqlQuery(query); if (!query->next()) throw Response::RespNameNotFound; - + DeckList *deck = new DeckList; deck->loadFromString_Native(query->value(0).toString()); - + return deck; } @@ -736,4 +808,86 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId, const QString query->bindValue(":target_id", (targetType == MessageTargetChat && targetId < 1) ? QVariant() : targetId); query->bindValue(":target_name", targetName); execSqlQuery(query); -} \ No newline at end of file +} + +bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword) +{ + if(server->getAuthenticationMethod() != Servatrice::AuthenticationSql) + return true; + + if (!checkSql()) + return true; + + QString error; + if (!usernameIsValid(user, error)) + return true; + + QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name"); + passwordQuery->bindValue(":name", user); + if (!execSqlQuery(passwordQuery)) { + qDebug("Change password denied: SQL error"); + return true; + } + + if (!passwordQuery->next()) + return true; + + const QString correctPassword = passwordQuery->value(0).toString(); + if (correctPassword != PasswordHasher::computeHash(oldPassword, correctPassword.left(16))) + return true; + + QString passwordSha512 = PasswordHasher::computeHash(newPassword, PasswordHasher::generateRandomSalt()); + + passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password where name = :name"); + passwordQuery->bindValue(":password", passwordSha512); + passwordQuery->bindValue(":name", user); + if (!execSqlQuery(passwordQuery)) { + qDebug("Change password denied: SQL error"); + return true; + } + return false; +} + +int Servatrice_DatabaseInterface::getActiveUserCount() +{ + int userCount = 0; + + if (!checkSql()) + return userCount; + + QSqlQuery *query = prepareQuery("select count(*) from {prefix}_sessions where id_server = :serverid AND end_time is NULL"); + query->bindValue(":serverid", server->getServerId()); + if (!execSqlQuery(query)){ + return userCount; + } + + if (query->next()){ + userCount = query->value(0).toInt(); + } + + return userCount; +} + +void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName, const QString &userClientID) +{ + + if (!checkSql()) + return; + + QSqlQuery *query = prepareQuery("update {prefix}_users set clientid = :clientid where name = :username"); + query->bindValue(":clientid", userClientID); + query->bindValue(":username", userName); + execSqlQuery(query); + +} + +void Servatrice_DatabaseInterface::updateUsersLastLoginTime(const QString &userName) +{ + if (!checkSql()) + return; + + QSqlQuery *query = prepareQuery("update {prefix}_users set last_login = NOW() where name = :user_name"); + query->bindValue(":user_name", userName); + execSqlQuery(query); + +} diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 10770691..0ca48440 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -9,67 +9,75 @@ #include "server.h" #include "server_database_interface.h" -#define DATABASE_SCHEMA_VERSION 1 +#define DATABASE_SCHEMA_VERSION 7 class Servatrice; class Servatrice_DatabaseInterface : public Server_DatabaseInterface { - Q_OBJECT + Q_OBJECT private: - int instanceId; - QSqlDatabase sqlDatabase; - QHash preparedStatements; - Servatrice *server; - ServerInfo_User evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId = false); - /** Must be called after checkSql and server is known to be in auth mode. */ - bool checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining); - /** Must be called after checkSql and server is known to be in auth mode. */ - bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining); - QChar getGenderChar(ServerInfo_User_Gender const &gender); + int instanceId; + QSqlDatabase sqlDatabase; + QHash preparedStatements; + Servatrice *server; + ServerInfo_User evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId = false); + /** Must be called after checkSql and server is known to be in auth mode. */ + bool checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining); + /** Must be called after checkSql and server is known to be in auth mode. */ + bool checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining); + /** Must be called after checkSql and server is known to be in auth mode. */ + bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining); + protected: - AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft); + AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft); + public slots: - void initDatabase(const QSqlDatabase &_sqlDatabase); + void initDatabase(const QSqlDatabase &_sqlDatabase); + public: - Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); - ~Servatrice_DatabaseInterface(); - bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName, - const QString &userName, const QString &password); - bool openDatabase(); - bool checkSql(); - QSqlQuery * prepareQuery(const QString &queryText); - bool execSqlQuery(QSqlQuery *query); - const QSqlDatabase &getDatabase() { return sqlDatabase; } + Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); + ~Servatrice_DatabaseInterface(); + bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName, + const QString &userName, const QString &password); + bool openDatabase(); + bool checkSql(); + QSqlQuery * prepareQuery(const QString &queryText); + bool execSqlQuery(QSqlQuery *query); + const QSqlDatabase &getDatabase() { return sqlDatabase; } - bool activeUserExists(const QString &user); - bool userExists(const QString &user); - int getUserIdInDB(const QString &name); - QMap getBuddyList(const QString &name); - QMap getIgnoreList(const QString &name); - bool isInBuddyList(const QString &whoseList, const QString &who); - bool isInIgnoreList(const QString &whoseList, const QString &who); - ServerInfo_User getUserData(const QString &name, bool withId = false); - void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList); - DeckList *getDeckFromDatabase(int deckId, int userId); - - int getNextGameId(); - int getNextReplayId(); - - qint64 startSession(const QString &userName, const QString &address); - void endSession(qint64 sessionId); - - void clearSessionTables(); - void lockSessionTables(); - void unlockSessionTables(); - bool userSessionExists(const QString &userName); - bool usernameIsValid(const QString &user); - bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining); + bool activeUserExists(const QString &user); + bool userExists(const QString &user); + int getUserIdInDB(const QString &name); + QMap getBuddyList(const QString &name); + QMap getIgnoreList(const QString &name); + bool isInBuddyList(const QString &whoseList, const QString &who); + bool isInIgnoreList(const QString &whoseList, const QString &who); + ServerInfo_User getUserData(const QString &name, bool withId = false); + void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, + const QSet &allPlayersEver, const QSet&allSpectatorsEver, const QList &replayList); + DeckList *getDeckFromDatabase(int deckId, int userId); - bool getRequireRegistration(); - bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false); - bool activateUser(const QString &userName, const QString &token); + int getNextGameId(); + int getNextReplayId(); + int getActiveUserCount(); + qint64 startSession(const QString &userName, const QString &address, const QString &clientId); + void endSession(qint64 sessionId); + void clearSessionTables(); + void lockSessionTables(); + void unlockSessionTables(); + bool userSessionExists(const QString &userName); + bool usernameIsValid(const QString &user, QString & error); + bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining); - void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName); + bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, + const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false); + bool activateUser(const QString &userName, const QString &token); + void updateUsersClientID(const QString &userName, const QString &userClientID); + void updateUsersLastLoginTime(const QString &userName); + void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, + LogMessage_TargetType targetType, const int targetId, const QString &targetName); + bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword); + QChar getGenderChar(ServerInfo_User_Gender const &gender); }; #endif diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index 3fd9c925..2b841ace 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -1,17 +1,11 @@ #include "server_logger.h" #include "settingscache.h" -#include #include #include #include #include #include #include -#ifdef Q_OS_UNIX -# include -# include -# include -#endif ServerLogger::ServerLogger(bool _logToConsole, QObject *parent) : QObject(parent), logToConsole(_logToConsole), flushRunning(false) @@ -44,13 +38,6 @@ void ServerLogger::startLog(const QString &logFileName) logFile = 0; return; } - -#ifdef Q_OS_UNIX - ::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD); - - snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this); - connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); -#endif } else logFile = 0; @@ -119,35 +106,15 @@ void ServerLogger::flushBuffer() } } -void ServerLogger::hupSignalHandler(int /*unused*/) +void ServerLogger::rotateLogs() { -#ifdef Q_OS_UNIX if (!logFile) return; - - char a = 1; - ssize_t writeValue = ::write(sigHupFD[0], &a, sizeof(a)); - Q_UNUSED(writeValue); -#endif -} -void ServerLogger::handleSigHup() -{ -#ifdef Q_OS_UNIX - if (!logFile) - return; - - snHup->setEnabled(false); - char tmp; - ssize_t readValue = ::read(sigHupFD[1], &tmp, sizeof(tmp)); - Q_UNUSED(readValue); + flushBuffer(); logFile->close(); logFile->open(QIODevice::Append); - - snHup->setEnabled(true); -#endif } QFile *ServerLogger::logFile; -int ServerLogger::sigHupFD[2]; diff --git a/servatrice/src/server_logger.h b/servatrice/src/server_logger.h index 67b293cf..478d0460 100644 --- a/servatrice/src/server_logger.h +++ b/servatrice/src/server_logger.h @@ -7,7 +7,6 @@ #include #include -class QSocketNotifier; class QFile; class Server_ProtocolHandler; @@ -16,19 +15,16 @@ class ServerLogger : public QObject { public: ServerLogger(bool _logToConsole, QObject *parent = 0); ~ServerLogger(); - static void hupSignalHandler(int unused); public slots: void startLog(const QString &logFileName); void logMessage(QString message, void *caller = 0); + void rotateLogs(); private slots: - void handleSigHup(); void flushBuffer(); signals: void sigFlushBuffer(); private: bool logToConsole; - static int sigHupFD[2]; - QSocketNotifier *snHup; static QFile *logFile; bool flushRunning; QStringList buffer; diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index a2fa26b2..83cb0a5f 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include +#include #include #include #include @@ -48,6 +50,7 @@ #include "pb/event_server_identification.pb.h" #include "pb/event_add_to_list.pb.h" #include "pb/event_remove_from_list.pb.h" +#include "pb/event_notify_user.pb.h" #include "pb/response_deck_list.pb.h" #include "pb/response_deck_download.pb.h" #include "pb/response_deck_upload.pb.h" @@ -58,8 +61,6 @@ #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_deckstorage.pb.h" -#include "smtp/SmtpMime" - #include "version_string.h" #include #include @@ -120,14 +121,30 @@ bool ServerSocketInterface::initSession() SessionEvent *identSe = prepareSessionEvent(identEvent); sendProtocolItem(*identSe); delete identSe; - - int maxUsers = servatrice->getMaxUsersPerAddress(); + + //limit the number of total users based on configuration settings + bool enforceUserLimit = settingsCache->value("security/enable_max_user_limit", false).toBool(); + if (enforceUserLimit){ + int userLimit = settingsCache->value("security/max_users_total", 500).toInt(); + int playerCount = (databaseInterface->getActiveUserCount() + 1); + if (playerCount > userLimit){ + std::cerr << "Max Users Total Limit Reached, please increase the max_users_total setting." << std::endl; + logger->logMessage(QString("Max Users Total Limit Reached, please increase the max_users_total setting."), this); + Event_ConnectionClosed event; + event.set_reason(Event_ConnectionClosed::USER_LIMIT_REACHED); + SessionEvent *se = prepareSessionEvent(event); + sendProtocolItem(*se); + delete se; + return false; + } + } //allow unlimited number of connections from the trusted sources - QString trustedSources = settingsCache->value("server/trusted_sources","127.0.0.1,::1").toString(); + QString trustedSources = settingsCache->value("security/trusted_sources","127.0.0.1,::1").toString(); if (trustedSources.contains(socket->peerAddress().toString(),Qt::CaseInsensitive)) return true; + int maxUsers = servatrice->getMaxUsersPerAddress(); if ((maxUsers > 0) && (servatrice->getUsersWithAddress(socket->peerAddress()) >= maxUsers)) { Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::TOO_MANY_CONNECTIONS); @@ -272,6 +289,10 @@ Response::ResponseCode ServerSocketInterface::processExtendedSessionCommand(int case SessionCommand::REPLAY_DELETE_MATCH: return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc); case SessionCommand::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); break; case SessionCommand::ACTIVATE: return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); break; + + case SessionCommand::ACCOUNT_EDIT: return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc); + case SessionCommand::ACCOUNT_IMAGE: return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc); + case SessionCommand::ACCOUNT_PASSWORD: return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc); default: return Response::RespFunctionNotAllowed; } } @@ -289,6 +310,8 @@ Response::ResponseCode ServerSocketInterface::processExtendedAdminCommand(int cm switch ((AdminCommand::AdminCommandType) cmdType) { case AdminCommand::SHUTDOWN_SERVER: return cmdShutdownServer(cmd.GetExtension(Command_ShutdownServer::ext), rc); case AdminCommand::UPDATE_SERVER_MESSAGE: return cmdUpdateServerMessage(cmd.GetExtension(Command_UpdateServerMessage::ext), rc); + case AdminCommand::RELOAD_CONFIG: return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc); + case AdminCommand::ADJUST_MOD: return cmdAdjustMod(cmd.GetExtension(Command_AdjustMod::ext), rc); default: return Response::RespFunctionNotAllowed; } } @@ -736,20 +759,40 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban if (trustedSources.contains(address,Qt::CaseInsensitive)) address = ""; - QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason)"); + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason, clientid) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason, :client_id)"); query->bindValue(":user_name", userName); query->bindValue(":ip_address", address); query->bindValue(":id_admin", userInfo->id()); query->bindValue(":minutes", minutes); query->bindValue(":reason", QString::fromStdString(cmd.reason())); query->bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason())); + query->bindValue(":client_id", QString::fromStdString(cmd.clientid())); sqlInterface->execSqlQuery(query); servatrice->clientsLock.lockForRead(); QList userList = servatrice->getUsersWithAddressAsList(QHostAddress(address)); - ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); - if (user && !userList.contains(user)) + + if (!userName.isEmpty()) { + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); userList.append(user); + } + + if (userName.isEmpty() && address.isEmpty() && (!QString::fromStdString(cmd.clientid()).isEmpty())) { + QSqlQuery *query = sqlInterface->prepareQuery("select name from {prefix}_users where clientid = :client_id"); + query->bindValue(":client_id", QString::fromStdString(cmd.clientid())); + sqlInterface->execSqlQuery(query); + if (!sqlInterface->execSqlQuery(query)){ + qDebug("ClientID username ban lookup failed: SQL Error"); + } else { + while (query->next()) { + userName = query->value(0).toString(); + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user && !userList.contains(user)) + userList.append(user); + } + } + } + if (!userList.isEmpty()) { Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::BANNED); @@ -772,6 +815,7 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc) { QString userName = QString::fromStdString(cmd.user_name()); + QString clientId = QString::fromStdString(cmd.clientid()); qDebug() << "Got register command: " << userName; bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); @@ -788,15 +832,21 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R } // TODO: Move this method outside of the db interface - if (!sqlInterface->usernameIsValid(userName)) + QString errorString; + if (!sqlInterface->usernameIsValid(userName, errorString)) + { + Response_Register *re = new Response_Register; + re->set_denied_reason_str(errorString.toStdString()); + rc.setResponseExtension(re); return Response::RespUsernameInvalid; + } if(sqlInterface->userExists(userName)) return Response::RespUserAlreadyExists; QString banReason; int banSecondsRemaining; - if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, banReason, banSecondsRemaining)) + if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, clientId, banReason, banSecondsRemaining)) { Response_Register *re = new Response_Register; re->set_denied_reason_str(banReason.toStdString()); @@ -826,8 +876,11 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R qDebug() << "Accepted register command for user: " << userName; if(requireEmailForRegistration) { - // TODO call a slot on another thread to send email - sendActivationTokenMail(userName, emailAddress, token); + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_activation_emails (name) values(:name)"); + query->bindValue(":name", userName); + if (!sqlInterface->execSqlQuery(query)) + return Response::RespRegistrationFailed; + return Response::RespRegistrationAcceptedNeedsActivation; } else { return Response::RespRegistrationAccepted; @@ -837,88 +890,6 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R } } -bool ServerSocketInterface::sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token) -{ - QString tmp = settingsCache->value("smtp/connection", "tcp").toString(); - SmtpClient::ConnectionType connection = SmtpClient::TcpConnection; - if(tmp == "ssl") - connection = SmtpClient::SslConnection; - else if(tmp == "tls") - connection = SmtpClient::TlsConnection; - - tmp = settingsCache->value("smtp/auth", "plain").toString(); - SmtpClient::AuthMethod auth = SmtpClient::AuthPlain; - if(tmp == "login") - auth = SmtpClient::AuthLogin; - - QString host = settingsCache->value("smtp/host", "localhost").toString(); - int port = settingsCache->value("smtp/port", 25).toInt(); - QString username = settingsCache->value("smtp/username", "").toString(); - QString password = settingsCache->value("smtp/password", "").toString(); - QString email = settingsCache->value("smtp/email", "").toString(); - QString name = settingsCache->value("smtp/name", "").toString(); - QString subject = settingsCache->value("smtp/subject", "").toString(); - QString body = settingsCache->value("smtp/body", "").toString(); - - if(email.isEmpty()) - { - qDebug() << "[MAIL] Missing email field" << endl; - return false; - } - if(body.isEmpty()) - { - qDebug() << "[MAIL] Missing body field" << endl; - return false; - } - if(recipient.isEmpty()) - { - qDebug() << "[MAIL] Missing recipient field" << endl; - return false; - } - if(token.isEmpty()) - { - qDebug() << "[MAIL] Missing token field" << endl; - return false; - } - - SmtpClient smtp(host, port, connection); - smtp.setUser(username); - smtp.setPassword(password); - smtp.setAuthMethod(auth); - - MimeMessage message; - EmailAddress sender(email, name); - message.setSender(&sender); - EmailAddress to(recipient, nickname); - message.addRecipient(&to); - message.setSubject(subject); - - MimeText text; - text.setText(body.replace("%username", nickname).replace("%token", token)); - message.addPart(&text); - - // Now we can send the mail - - if (!smtp.connectToHost()) { - qDebug() << "[MAIL] Failed to connect to host" << host << "on port" << port; - return false; - } - - if (!smtp.login()) { - qDebug() << "[MAIL] Failed to login as " << username; - return false; - } - - if (!smtp.sendMail(message)) { - qDebug() << "[MAIL] Failed to send mail to " << recipient; - return false; - } - - smtp.quit(); - qDebug() << "[MAIL] Sent activation email to " << recipient << " for nickname " << nickname; - return true; -} - bool ServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress) { // TODO: implement @@ -941,6 +912,75 @@ Response::ResponseCode ServerSocketInterface::cmdActivateAccount(const Command_A } } +Response::ResponseCode ServerSocketInterface::cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer & /* rc */) +{ + if (authState != PasswordRight) + return Response::RespFunctionNotAllowed; + + QString realName = QString::fromStdString(cmd.real_name()); + QString emailAddress = QString::fromStdString(cmd.email()); + ServerInfo_User_Gender gender = cmd.gender(); + QString country = QString::fromStdString(cmd.country()); + + QString userName = QString::fromStdString(userInfo->name()); + + + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set realname=:realName, email=:email, gender=:gender, country=:country where name=:userName"); + query->bindValue(":realName", realName); + query->bindValue(":email", emailAddress); + query->bindValue(":gender", sqlInterface->getGenderChar(gender)); + query->bindValue(":country", country); + query->bindValue(":userName", userName); + if (!sqlInterface->execSqlQuery(query)) + return Response::RespInternalError; + + userInfo->set_real_name(cmd.real_name()); + userInfo->set_email(cmd.email()); + userInfo->set_gender(cmd.gender()); + userInfo->set_country(cmd.country()); + + return Response::RespOk; +} + +Response::ResponseCode ServerSocketInterface::cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer & /* rc */) +{ + if (authState != PasswordRight) + return Response::RespFunctionNotAllowed; + + QByteArray image(cmd.image().c_str(), cmd.image().length()); + int id = userInfo->id(); + + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set avatar_bmp=:image where id=:id"); + query->bindValue(":image", image); + query->bindValue(":id", id); + if (!sqlInterface->execSqlQuery(query)) + return Response::RespInternalError; + + userInfo->set_avatar_bmp(cmd.image().c_str(), cmd.image().length()); + return Response::RespOk; +} + +Response::ResponseCode ServerSocketInterface::cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer & /* rc */) +{ + if (authState != PasswordRight) + return Response::RespFunctionNotAllowed; + + QString oldPassword = QString::fromStdString(cmd.old_password()); + QString newPassword = QString::fromStdString(cmd.new_password()); + + // TODO make this configurable? + if(newPassword.length() < 6) + return Response::RespPasswordTooShort; + + QString userName = QString::fromStdString(userInfo->name()); + + bool changeFailed = databaseInterface->changeUserPassword(userName, oldPassword, newPassword); + + if(changeFailed) + return Response::RespWrongPassword; + + return Response::RespOk; +} // ADMIN FUNCTIONS. // Permission is checked by the calling function. @@ -956,3 +996,59 @@ Response::ResponseCode ServerSocketInterface::cmdShutdownServer(const Command_Sh QMetaObject::invokeMethod(server, "scheduleShutdown", Q_ARG(QString, QString::fromStdString(cmd.reason())), Q_ARG(int, cmd.minutes())); return Response::RespOk; } + +Response::ResponseCode ServerSocketInterface::cmdReloadConfig(const Command_ReloadConfig & /* cmd */, ResponseContainer & /*rc*/) +{ + logDebugMessage("Received admin command: reloading configuration"); + settingsCache->sync(); + return Response::RespOk; +} + +Response::ResponseCode ServerSocketInterface::cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/) { + + QString userName = QString::fromStdString(cmd.user_name()); + + if (cmd.should_be_mod()) { + QSqlQuery *query = sqlInterface->prepareQuery( + "update {prefix}_users set admin = :adminlevel where name = :username"); + query->bindValue(":adminlevel", 2); + query->bindValue(":username", userName); + if (!sqlInterface->execSqlQuery(query)){ + logger->logMessage(QString::fromStdString("Failed to promote user %1: %2").arg(userName).arg(query->lastError().text())); + return Response::RespInternalError; + } + + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user) { + Event_NotifyUser event; + event.set_type(Event_NotifyUser::PROMOTED); + SessionEvent *se = user->prepareSessionEvent(event); + user->sendProtocolItem(*se); + delete se; + } + } else { + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set admin = :adminlevel where name = :username"); + query->bindValue(":adminlevel", 0); + query->bindValue(":username", userName); + if (!sqlInterface->execSqlQuery(query)){ + logger->logMessage(QString::fromStdString("Failed to demote user %1: %2").arg(userName).arg(query->lastError().text())); + return Response::RespInternalError; + } + + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user) { + Event_ConnectionClosed event; + event.set_reason(Event_ConnectionClosed::DEMOTED); + event.set_reason_str("Your moderator status has been revoked."); + event.set_end_time(QDateTime::currentDateTime().toTime_t()); + + SessionEvent *se = user->prepareSessionEvent(event); + user->sendProtocolItem(*se); + delete se; + } + + QMetaObject::invokeMethod(user, "prepareDestroy", Qt::QueuedConnection); + } + + return Response::RespOk; +} \ No newline at end of file diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index c89709d3..a733c6b7 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -47,6 +47,11 @@ class Command_ReplayDeleteMatch; class Command_BanFromServer; class Command_UpdateServerMessage; class Command_ShutdownServer; +class Command_ReloadConfig; + +class Command_AccountEdit; +class Command_AccountImage; +class Command_AccountPassword; class ServerSocketInterface : public Server_ProtocolHandler { @@ -94,12 +99,16 @@ private: Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); + Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &/* cmd */, ResponseContainer & /*rc*/); + Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/); Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc); - bool sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); + Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); public: ServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); ~ServerSocketInterface(); diff --git a/servatrice/src/signalhandler.cpp b/servatrice/src/signalhandler.cpp new file mode 100644 index 00000000..d06fc1db --- /dev/null +++ b/servatrice/src/signalhandler.cpp @@ -0,0 +1,101 @@ +#include + +#include "signalhandler.h" +#include "server_logger.h" +#include "settingscache.h" +#include "main.h" + +#ifdef Q_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#endif + +#define SIGSEGV_TRACE_LINES 40 + +int SignalHandler::sigHupFD[2]; + +SignalHandler::SignalHandler(QObject *parent) +: QObject(parent) +{ +#ifdef Q_OS_UNIX + ::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD); + + snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this); + connect(snHup, SIGNAL(activated(int)), this, SLOT(internalSigHupHandler())); + + struct sigaction hup; + hup.sa_handler = SignalHandler::sigHupHandler; + sigemptyset(&hup.sa_mask); + hup.sa_flags = 0; + hup.sa_flags |= SA_RESTART; + sigaction(SIGHUP, &hup, 0); + + struct sigaction segv; + segv.sa_handler = SignalHandler::sigSegvHandler; + segv.sa_flags = SA_RESETHAND; + sigemptyset(&segv.sa_mask); + sigaction(SIGSEGV, &segv, 0); + sigaction(SIGABRT, &segv, 0); + + signal(SIGPIPE, SIG_IGN); +#endif +} + +void SignalHandler::sigHupHandler(int /* sig */) +{ +#ifdef Q_OS_UNIX + char a = 1; + ssize_t writeValue = ::write(sigHupFD[0], &a, sizeof(a)); + Q_UNUSED(writeValue); +#endif +} + +void SignalHandler::internalSigHupHandler() +{ + snHup->setEnabled(false); +#ifdef Q_OS_UNIX + char tmp; + ssize_t readValue = ::read(sigHupFD[1], &tmp, sizeof(tmp)); + Q_UNUSED(readValue); + + std::cerr << "Received SIGHUP" << std::endl; +#endif + logger->logMessage("Received SIGHUP, rotating logs and reloading configuration", this); + logger->rotateLogs(); + + settingsCache->sync(); + + snHup->setEnabled(true); +} + +void SignalHandler::sigSegvHandler(int sig) +{ +#ifdef Q_OS_UNIX + void *array[SIGSEGV_TRACE_LINES]; + size_t size; + + // get void*'s for all entries on the stack + size = backtrace(array, SIGSEGV_TRACE_LINES); + + // print out all the frames to stderr + fprintf(stderr, "Error: signal %d:\n", sig); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + if (sig == SIGSEGV) + logger->logMessage("CRASH: SIGSEGV"); + else if (sig == SIGABRT) + logger->logMessage("CRASH: SIGABRT"); + + logger->deleteLater(); + loggerThread->wait(); + delete loggerThread; + + raise(sig); +#endif +} + diff --git a/servatrice/src/signalhandler.h b/servatrice/src/signalhandler.h new file mode 100644 index 00000000..314afa3b --- /dev/null +++ b/servatrice/src/signalhandler.h @@ -0,0 +1,24 @@ +#ifndef SIGNALHANDLER_H +#define SIGNALHANDLER_H + +#include + +class QSocketNotifier; + +class SignalHandler: public QObject +{ + Q_OBJECT +public: + SignalHandler(QObject *parent = 0); + ~SignalHandler() { }; + static void sigHupHandler(int /* sig */); + static void sigSegvHandler(int sig); +private: + static int sigHupFD[2]; + QSocketNotifier *snHup; +private slots: + void internalSigHupHandler(); + +}; + +#endif diff --git a/servatrice/src/smtp/SmtpMime b/servatrice/src/smtp/SmtpMime deleted file mode 100644 index 940996b8..00000000 --- a/servatrice/src/smtp/SmtpMime +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef SMTPMIME_H -#define SMTPMIME_H - -#include "smtpclient.h" -#include "mimepart.h" -#include "mimehtml.h" -#include "mimeattachment.h" -#include "mimemessage.h" -#include "mimetext.h" -#include "mimeinlinefile.h" -#include "mimefile.h" - -#endif // SMTPMIME_H diff --git a/servatrice/src/smtp/emailaddress.cpp b/servatrice/src/smtp/emailaddress.cpp deleted file mode 100644 index 9480e5d4..00000000 --- a/servatrice/src/smtp/emailaddress.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "emailaddress.h" - -/* [1] Constructors and Destructors */ - -EmailAddress::EmailAddress(const QString & address, const QString & name) -{ - this->address = address; - this->name = name; -} - -EmailAddress::~EmailAddress() -{ -} - -/* [1] --- */ - - -/* [2] Getters and Setters */ - -void EmailAddress::setName(const QString & name) -{ - this->name = name; - -} - -void EmailAddress::setAddress(const QString & address) -{ - this->address = address; -} - -const QString & EmailAddress::getName() const -{ - return name; -} - -const QString & EmailAddress::getAddress() const -{ - return address; -} - -/* [2] --- */ - diff --git a/servatrice/src/smtp/emailaddress.h b/servatrice/src/smtp/emailaddress.h deleted file mode 100644 index 4ecf6efc..00000000 --- a/servatrice/src/smtp/emailaddress.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef EMAILADDRESS_H -#define EMAILADDRESS_H - -#include - -#include "smtpexports.h" - -class SMTP_EXPORT EmailAddress : public QObject -{ - Q_OBJECT -public: - - /* [1] Constructors and Destructors */ - - EmailAddress(); - EmailAddress(const QString & address, const QString & name=""); - - ~EmailAddress(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - void setName(const QString & name); - void setAddress(const QString & address); - - const QString & getName() const; - const QString & getAddress() const; - - /* [2] --- */ - - -private: - - /* [3] Private members */ - - QString name; - QString address; - - /* [3] --- */ -}; - -#endif // EMAILADDRESS_H diff --git a/servatrice/src/smtp/mimeattachment.cpp b/servatrice/src/smtp/mimeattachment.cpp deleted file mode 100644 index 0dea6222..00000000 --- a/servatrice/src/smtp/mimeattachment.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimeattachment.h" -#include - -/* [1] Constructors and Destructors */ - -MimeAttachment::MimeAttachment(QFile *file) - : MimeFile(file) -{ -} -MimeAttachment::MimeAttachment(const QByteArray& stream, const QString& fileName): MimeFile(stream, fileName) -{ - -} - -MimeAttachment::~MimeAttachment() -{ -} - -/* [1] --- */ - - -/* [2] Protected methods */ - -void MimeAttachment::prepare() -{ - this->header += "Content-disposition: attachment\r\n"; - - /* !!! IMPORTANT !!! */ - MimeFile::prepare(); -} - -/* [2] --- */ diff --git a/servatrice/src/smtp/mimeattachment.h b/servatrice/src/smtp/mimeattachment.h deleted file mode 100644 index 4a92e9a1..00000000 --- a/servatrice/src/smtp/mimeattachment.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEATTACHMENT_H -#define MIMEATTACHMENT_H - -#include -#include "mimepart.h" -#include "mimefile.h" - -#include "smtpexports.h" - -class SMTP_EXPORT MimeAttachment : public MimeFile -{ - Q_OBJECT -public: - - /* [1] Constructors and Destructors */ - - MimeAttachment(QFile* file); - MimeAttachment(const QByteArray& stream, const QString& fileName); - - ~MimeAttachment(); - - /* [1] --- */ - -protected: - - /* [2] Protected methods */ - - virtual void prepare(); - - /* [2] --- */ -}; - -#endif // MIMEATTACHMENT_H diff --git a/servatrice/src/smtp/mimecontentformatter.cpp b/servatrice/src/smtp/mimecontentformatter.cpp deleted file mode 100644 index 7f5a6e43..00000000 --- a/servatrice/src/smtp/mimecontentformatter.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimecontentformatter.h" - -MimeContentFormatter::MimeContentFormatter(int max_length) : - max_length(max_length) -{} - -QString MimeContentFormatter::format(const QString &content, bool quotedPrintable) const { - - QString out; - - int chars = 0; - for (int i = 0; i < content.length() ; ++i) { - chars++; - if (!quotedPrintable) { - if (chars > max_length) { - out.append("\r\n"); - chars = 1; - } - } - else { - if (content[i] == '\n') { // new line - out.append(content[i]); - chars = 0; - continue; - } - - if ((chars > max_length - 1) - || ((content[i] == '=') && (chars > max_length - 3) )) { - out.append('='); - out.append("\r\n"); - chars = 1; - } - - } - out.append(content[i]); - } - - return out; - -} - -void MimeContentFormatter::setMaxLength(int l) { - max_length = l; -} - -int MimeContentFormatter::getMaxLength() const { - return max_length; -} diff --git a/servatrice/src/smtp/mimecontentformatter.h b/servatrice/src/smtp/mimecontentformatter.h deleted file mode 100644 index bf751588..00000000 --- a/servatrice/src/smtp/mimecontentformatter.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMECONTENTFORMATTER_H -#define MIMECONTENTFORMATTER_H - -#include -#include - -#include "smtpexports.h" - -class SMTP_EXPORT MimeContentFormatter : public QObject -{ - Q_OBJECT -public: - MimeContentFormatter (int max_length = 76); - - void setMaxLength(int l); - int getMaxLength() const; - - QString format(const QString &content, bool quotedPrintable = false) const; - -protected: - int max_length; - -}; - -#endif // MIMECONTENTFORMATTER_H diff --git a/servatrice/src/smtp/mimefile.cpp b/servatrice/src/smtp/mimefile.cpp deleted file mode 100644 index c6f313a8..00000000 --- a/servatrice/src/smtp/mimefile.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimefile.h" -#include - -/* [1] Constructors and Destructors */ - -MimeFile::MimeFile(QFile *file) -{ - this->file = file; - this->cType = "application/octet-stream"; - this->cName = QFileInfo(*file).fileName(); - this->cEncoding = Base64; -} - -MimeFile::MimeFile(const QByteArray& stream, const QString& fileName) -{ - this->cEncoding = Base64; - this->cType = "application/octet-stream"; - this->file = 0; - this->cName = fileName; - this->content = stream; -} - -MimeFile::~MimeFile() -{ - if (file) - delete file; -} - -/* [1] --- */ - - -/* [2] Getters and setters */ - -/* [2] --- */ - - -/* [3] Protected methods */ - -void MimeFile::prepare() -{ - if (this->file) - { - file->open(QIODevice::ReadOnly); - this->content = file->readAll(); - file->close(); - } - /* !!! IMPORTANT !!!! */ - MimePart::prepare(); -} - -/* [3] --- */ - diff --git a/servatrice/src/smtp/mimefile.h b/servatrice/src/smtp/mimefile.h deleted file mode 100644 index 13f444de..00000000 --- a/servatrice/src/smtp/mimefile.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEFILE_H -#define MIMEFILE_H - -#include "mimepart.h" -#include - -#include "smtpexports.h" - -class SMTP_EXPORT MimeFile : public MimePart -{ - Q_OBJECT -public: - - /* [1] Constructors and Destructors */ - - MimeFile(const QByteArray& stream, const QString& fileName); - MimeFile(QFile *f); - ~MimeFile(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - - /* [2] --- */ - -protected: - - /* [3] Protected members */ - - QFile* file; - - /* [3] --- */ - - - /* [4] Protected methods */ - - virtual void prepare(); - - /* [4] --- */ - -}; - -#endif // MIMEFILE_H diff --git a/servatrice/src/smtp/mimehtml.cpp b/servatrice/src/smtp/mimehtml.cpp deleted file mode 100644 index 5594d3a4..00000000 --- a/servatrice/src/smtp/mimehtml.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimehtml.h" - -/* [1] Constructors and Destructors */ - -MimeHtml::MimeHtml(const QString &html) : MimeText(html) -{ - this->cType = "text/html"; -} - -MimeHtml::~MimeHtml() {} - -/* [1] --- */ - - -/* [2] Getters and Setters */ - -void MimeHtml::setHtml(const QString & html) -{ - this->text = html; -} - -const QString & MimeHtml::getHtml() const -{ - return text; -} - - -/* [2] --- */ - - -/* [3] Protected methods */ - -void MimeHtml::prepare() -{ - /* !!! IMPORTANT !!! */ - MimeText::prepare(); -} - -/* [3] --- */ diff --git a/servatrice/src/smtp/mimehtml.h b/servatrice/src/smtp/mimehtml.h deleted file mode 100644 index f3f54300..00000000 --- a/servatrice/src/smtp/mimehtml.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEHTML_H -#define MIMEHTML_H - -#include "mimetext.h" - -#include "smtpexports.h" - -class SMTP_EXPORT MimeHtml : public MimeText -{ - Q_OBJECT -public: - - /* [1] Constructors and Destructors */ - - MimeHtml(const QString &html = ""); - ~MimeHtml(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - - void setHtml(const QString & html); - - const QString& getHtml() const; - - /* [2] --- */ - -protected: - - /* [3] Protected members */ - - /* [3] --- */ - - - /* [4] Protected methods */ - - virtual void prepare(); - - /* [4] --- */ -}; - -#endif // MIMEHTML_H diff --git a/servatrice/src/smtp/mimeinlinefile.cpp b/servatrice/src/smtp/mimeinlinefile.cpp deleted file mode 100644 index 0823b0db..00000000 --- a/servatrice/src/smtp/mimeinlinefile.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimeinlinefile.h" - -/* [1] Constructors and Destructors */ - -MimeInlineFile::MimeInlineFile(QFile *f) - : MimeFile(f) -{ -} - -MimeInlineFile::~MimeInlineFile() -{} - -/* [1] --- */ - - -/* [2] Getters and Setters */ - -/* [2] --- */ - - -/* [3] Protected methods */ - -void MimeInlineFile::prepare() -{ - this->header += "Content-Disposition: inline\r\n"; - - /* !!! IMPORTANT !!! */ - MimeFile::prepare(); -} - -/* [3] --- */ - - - diff --git a/servatrice/src/smtp/mimeinlinefile.h b/servatrice/src/smtp/mimeinlinefile.h deleted file mode 100644 index 98fe3cab..00000000 --- a/servatrice/src/smtp/mimeinlinefile.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEINLINEFILE_H -#define MIMEINLINEFILE_H - -#include "mimefile.h" - -#include "smtpexports.h" - -class SMTP_EXPORT MimeInlineFile : public MimeFile -{ -public: - - /* [1] Constructors and Destructors */ - - MimeInlineFile(QFile *f); - ~MimeInlineFile(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - - /* [2] --- */ - -protected: - - /* [3] Protected members */ - - /* [3] --- */ - - - /* [4] Protected methods */ - - virtual void prepare(); - - /* [4] --- */ -}; - -#endif // MIMEINLINEFILE_H diff --git a/servatrice/src/smtp/mimemessage.cpp b/servatrice/src/smtp/mimemessage.cpp deleted file mode 100644 index 0c4ebc2d..00000000 --- a/servatrice/src/smtp/mimemessage.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimemessage.h" - -#include -#include "quotedprintable.h" -#include - -/* [1] Constructors and Destructors */ -MimeMessage::MimeMessage(bool createAutoMimeContent) : - hEncoding(MimePart::_8Bit) -{ - if (createAutoMimeContent) - this->content = new MimeMultiPart(); - - autoMimeContentCreated = createAutoMimeContent; -} - -MimeMessage::~MimeMessage() -{ - if (this->autoMimeContentCreated) - { - this->autoMimeContentCreated = false; - delete (this->content); - } -} - -/* [1] --- */ - - -/* [2] Getters and Setters */ -MimePart& MimeMessage::getContent() { - return *content; -} - -void MimeMessage::setContent(MimePart *content) { - if (this->autoMimeContentCreated) - { - this->autoMimeContentCreated = false; - delete (this->content); - } - this->content = content; -} - -void MimeMessage::setSender(EmailAddress* e) -{ - this->sender = e; -} - -void MimeMessage::addRecipient(EmailAddress* rcpt, RecipientType type) -{ - switch (type) - { - case To: - recipientsTo << rcpt; - break; - case Cc: - recipientsCc << rcpt; - break; - case Bcc: - recipientsBcc << rcpt; - break; - } -} - -void MimeMessage::addTo(EmailAddress* rcpt) { - this->recipientsTo << rcpt; -} - -void MimeMessage::addCc(EmailAddress* rcpt) { - this->recipientsCc << rcpt; -} - -void MimeMessage::addBcc(EmailAddress* rcpt) { - this->recipientsBcc << rcpt; -} - -void MimeMessage::setSubject(const QString & subject) -{ - this->subject = subject; -} - -void MimeMessage::addPart(MimePart *part) -{ - if (typeid(*content) == typeid(MimeMultiPart)) { - ((MimeMultiPart*) content)->addPart(part); - }; -} - -void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc) -{ - this->hEncoding = hEnc; -} - -const EmailAddress & MimeMessage::getSender() const -{ - return *sender; -} - -const QList & MimeMessage::getRecipients(RecipientType type) const -{ - switch (type) - { - default: - case To: - return recipientsTo; - case Cc: - return recipientsCc; - case Bcc: - return recipientsBcc; - } -} - -const QString & MimeMessage::getSubject() const -{ - return subject; -} - -const QList & MimeMessage::getParts() const -{ - if (typeid(*content) == typeid(MimeMultiPart)) { - return ((MimeMultiPart*) content)->getParts(); - } - else { - QList *res = new QList(); - res->append(content); - return *res; - } -} - -/* [2] --- */ - - -/* [3] Public Methods */ - -QString MimeMessage::toString() -{ - QString mime; - - /* =========== MIME HEADER ============ */ - - /* ---------- Sender / From ----------- */ - mime = "From:"; - if (sender->getName() != "") - { - switch (hEncoding) - { - case MimePart::Base64: - mime += " =?utf-8?B?" + QByteArray().append(sender->getName()).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(sender->getName())).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - mime += " " + sender->getName(); - } - } - mime += " <" + sender->getAddress() + ">\r\n"; - /* ---------------------------------- */ - - - /* ------- Recipients / To ---------- */ - mime += "To:"; - QList::iterator it; int i; - for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i) - { - if (i != 0) { mime += ","; } - - if ((*it)->getName() != "") - { - switch (hEncoding) - { - case MimePart::Base64: - mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - mime += " " + (*it)->getName(); - } - } - mime += " <" + (*it)->getAddress() + ">"; - } - mime += "\r\n"; - /* ---------------------------------- */ - - /* ------- Recipients / Cc ---------- */ - if (recipientsCc.size() != 0) { - mime += "Cc:"; - } - for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i) - { - if (i != 0) { mime += ","; } - - if ((*it)->getName() != "") - { - switch (hEncoding) - { - case MimePart::Base64: - mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - mime += " " + (*it)->getName(); - } - } - mime += " <" + (*it)->getAddress() + ">"; - } - if (recipientsCc.size() != 0) { - mime += "\r\n"; - } - /* ---------------------------------- */ - - /* ------------ Subject ------------- */ - mime += "Subject: "; - - - switch (hEncoding) - { - case MimePart::Base64: - mime += "=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?="; - break; - case MimePart::QuotedPrintable: - mime += "=?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(subject)).replace(' ', "_").replace(':',"=3A") + "?="; - break; - default: - mime += subject; - } - /* ---------------------------------- */ - - mime += "\r\n"; - mime += "MIME-Version: 1.0\r\n"; - - mime += content->toString(); - return mime; -} - -/* [3] --- */ diff --git a/servatrice/src/smtp/mimemessage.h b/servatrice/src/smtp/mimemessage.h deleted file mode 100644 index 18b4ec3b..00000000 --- a/servatrice/src/smtp/mimemessage.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEMESSAGE_H -#define MIMEMESSAGE_H - -#include "mimepart.h" -#include "mimemultipart.h" -#include "emailaddress.h" -#include - -#include "smtpexports.h" - -class SMTP_EXPORT MimeMessage : public QObject -{ -public: - - enum RecipientType { - To, // primary - Cc, // carbon copy - Bcc // blind carbon copy - }; - - /* [1] Constructors and Destructors */ - - MimeMessage(bool createAutoMimeConent = true); - ~MimeMessage(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - - void setSender(EmailAddress* e); - void addRecipient(EmailAddress* rcpt, RecipientType type = To); - void addTo(EmailAddress* rcpt); - void addCc(EmailAddress* rcpt); - void addBcc(EmailAddress* rcpt); - void setSubject(const QString & subject); - void addPart(MimePart* part); - - void setHeaderEncoding(MimePart::Encoding); - - const EmailAddress & getSender() const; - const QList & getRecipients(RecipientType type = To) const; - const QString & getSubject() const; - const QList & getParts() const; - - MimePart& getContent(); - void setContent(MimePart *content); - /* [2] --- */ - - - /* [3] Public methods */ - - virtual QString toString(); - - /* [3] --- */ - -protected: - - /* [4] Protected members */ - - EmailAddress* sender; - QList recipientsTo, recipientsCc, recipientsBcc; - QString subject; - MimePart *content; - bool autoMimeContentCreated; - - MimePart::Encoding hEncoding; - - /* [4] --- */ - - -}; - -#endif // MIMEMESSAGE_H diff --git a/servatrice/src/smtp/mimemultipart.cpp b/servatrice/src/smtp/mimemultipart.cpp deleted file mode 100644 index 19d47297..00000000 --- a/servatrice/src/smtp/mimemultipart.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimemultipart.h" -#include -#include - -const QString MULTI_PART_NAMES[] = { - "multipart/mixed", // Mixed - "multipart/digest", // Digest - "multipart/alternative", // Alternative - "multipart/related", // Related - "multipart/report", // Report - "multipart/signed", // Signed - "multipart/encrypted" // Encrypted -}; - -MimeMultiPart::MimeMultiPart(MultiPartType type) -{ - this->type = type; - this->cType = MULTI_PART_NAMES[this->type]; - this->cEncoding = _8Bit; - - QCryptographicHash md5(QCryptographicHash::Md5); - md5.addData(QByteArray().append(qrand())); - cBoundary = md5.result().toHex(); -} - -MimeMultiPart::~MimeMultiPart() { - -} - -void MimeMultiPart::addPart(MimePart *part) { - parts.append(part); -} - -const QList & MimeMultiPart::getParts() const { - return parts; -} - -void MimeMultiPart::prepare() { - QList::iterator it; - - content = ""; - for (it = parts.begin(); it != parts.end(); it++) { - content += "--" + cBoundary + "\r\n"; - (*it)->prepare(); - content += (*it)->toString(); - }; - - content += "--" + cBoundary + "--\r\n"; - - MimePart::prepare(); -} - -void MimeMultiPart::setMimeType(const MultiPartType type) { - this->type = type; - this->cType = MULTI_PART_NAMES[type]; -} - -MimeMultiPart::MultiPartType MimeMultiPart::getMimeType() const { - return type; -} diff --git a/servatrice/src/smtp/mimemultipart.h b/servatrice/src/smtp/mimemultipart.h deleted file mode 100644 index a8e9f599..00000000 --- a/servatrice/src/smtp/mimemultipart.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEMULTIPART_H -#define MIMEMULTIPART_H - -#include "mimepart.h" - -#include "smtpexports.h" - -class SMTP_EXPORT MimeMultiPart : public MimePart -{ - Q_OBJECT -public: - - /* [0] Enums */ - enum MultiPartType { - Mixed = 0, // RFC 2046, section 5.1.3 - Digest = 1, // RFC 2046, section 5.1.5 - Alternative = 2, // RFC 2046, section 5.1.4 - Related = 3, // RFC 2387 - Report = 4, // RFC 6522 - Signed = 5, // RFC 1847, section 2.1 - Encrypted = 6 // RFC 1847, section 2.2 - }; - - /* [0] --- */ - - /* [1] Constructors and Destructors */ - MimeMultiPart(const MultiPartType type = Related); - - ~MimeMultiPart(); - - /* [1] --- */ - - /* [2] Getters and Setters */ - - void setMimeType(const MultiPartType type); - MultiPartType getMimeType() const; - - const QList & getParts() const; - - /* [2] --- */ - - /* [3] Public methods */ - - void addPart(MimePart *part); - - virtual void prepare(); - - /* [3] --- */ - -protected: - QList< MimePart* > parts; - - MultiPartType type; - -}; - -#endif // MIMEMULTIPART_H diff --git a/servatrice/src/smtp/mimepart.cpp b/servatrice/src/smtp/mimepart.cpp deleted file mode 100644 index 443f863e..00000000 --- a/servatrice/src/smtp/mimepart.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimepart.h" -#include "quotedprintable.h" - -/* [1] Constructors and Destructors */ - -MimePart::MimePart() -{ - cEncoding = _7Bit; - prepared = false; - cBoundary = ""; -} - -MimePart::~MimePart() -{ - return; -} - -/* [1] --- */ - - -/* [2] Getters and Setters */ - -void MimePart::setContent(const QByteArray & content) -{ - this->content = content; -} - -void MimePart::setHeader(const QString & header) -{ - this->header = header; -} - -void MimePart::addHeaderLine(const QString & line) -{ - this->header += line + "\r\n"; -} - -const QString& MimePart::getHeader() const -{ - return header; -} - -const QByteArray& MimePart::getContent() const -{ - return content; -} - -void MimePart::setContentId(const QString & cId) -{ - this->cId = cId; -} - -const QString & MimePart::getContentId() const -{ - return this->cId; -} - -void MimePart::setContentName(const QString & cName) -{ - this->cName = cName; -} - -const QString & MimePart::getContentName() const -{ - return this->cName; -} - -void MimePart::setContentType(const QString & cType) -{ - this->cType = cType; -} - -const QString & MimePart::getContentType() const -{ - return this->cType; -} - -void MimePart::setCharset(const QString & charset) -{ - this->cCharset = charset; -} - -const QString & MimePart::getCharset() const -{ - return this->cCharset; -} - -void MimePart::setEncoding(Encoding enc) -{ - this->cEncoding = enc; -} - -MimePart::Encoding MimePart::getEncoding() const -{ - return this->cEncoding; -} - -MimeContentFormatter& MimePart::getContentFormatter() -{ - return this->formatter; -} - -/* [2] --- */ - - -/* [3] Public methods */ - -QString MimePart::toString() -{ - if (!prepared) - prepare(); - - return mimeString; -} - -/* [3] --- */ - - -/* [4] Protected methods */ - -void MimePart::prepare() -{ - mimeString = QString(); - - /* === Header Prepare === */ - - /* Content-Type */ - mimeString.append("Content-Type: ").append(cType); - - if (cName != "") - mimeString.append("; name=\"").append(cName).append("\""); - - if (cCharset != "") - mimeString.append("; charset=").append(cCharset); - - if (cBoundary != "") - mimeString.append("; boundary=").append(cBoundary); - - mimeString.append("\r\n"); - /* ------------ */ - - /* Content-Transfer-Encoding */ - mimeString.append("Content-Transfer-Encoding: "); - switch (cEncoding) - { - case _7Bit: - mimeString.append("7bit\r\n"); - break; - case _8Bit: - mimeString.append("8bit\r\n"); - break; - case Base64: - mimeString.append("base64\r\n"); - break; - case QuotedPrintable: - mimeString.append("quoted-printable\r\n"); - break; - } - /* ------------------------ */ - - /* Content-Id */ - if (cId != NULL) - mimeString.append("Content-ID: <").append(cId).append(">\r\n"); - /* ---------- */ - - /* Addition header lines */ - - mimeString.append(header).append("\r\n"); - - /* ------------------------- */ - - /* === End of Header Prepare === */ - - /* === Content === */ - switch (cEncoding) - { - case _7Bit: - mimeString.append(QString(content).toLatin1()); - break; - case _8Bit: - mimeString.append(content); - break; - case Base64: - mimeString.append(formatter.format(content.toBase64())); - break; - case QuotedPrintable: - mimeString.append(formatter.format(QuotedPrintable::encode(content), true)); - break; - } - mimeString.append("\r\n"); - /* === End of Content === */ - - prepared = true; -} - -/* [4] --- */ diff --git a/servatrice/src/smtp/mimepart.h b/servatrice/src/smtp/mimepart.h deleted file mode 100644 index 8636035d..00000000 --- a/servatrice/src/smtp/mimepart.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMEPART_H -#define MIMEPART_H - -#include -#include "mimecontentformatter.h" - -#include "smtpexports.h" - -class SMTP_EXPORT MimePart : public QObject -{ - Q_OBJECT -public: - - /* [0] Enumerations */ - enum Encoding { - _7Bit, - _8Bit, - Base64, - QuotedPrintable - }; - - - /* [0] --- */ - - - /* [1] Constructors and Destructors */ - - MimePart(); - ~MimePart(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - - const QString& getHeader() const; - const QByteArray& getContent() const; - - void setContent(const QByteArray & content); - void setHeader(const QString & header); - - void addHeaderLine(const QString & line); - - void setContentId(const QString & cId); - const QString & getContentId() const; - - void setContentName(const QString & cName); - const QString & getContentName() const; - - void setContentType(const QString & cType); - const QString & getContentType() const; - - void setCharset(const QString & charset); - const QString & getCharset() const; - - void setEncoding(Encoding enc); - Encoding getEncoding() const; - - MimeContentFormatter& getContentFormatter(); - - /* [2] --- */ - - - /* [3] Public methods */ - - virtual QString toString(); - - virtual void prepare(); - - /* [3] --- */ - - - -protected: - - /* [4] Protected members */ - - QString header; - QByteArray content; - - QString cId; - QString cName; - QString cType; - QString cCharset; - QString cBoundary; - Encoding cEncoding; - - QString mimeString; - bool prepared; - - MimeContentFormatter formatter; - - /* [4] --- */ -}; - -#endif // MIMEPART_H diff --git a/servatrice/src/smtp/mimetext.cpp b/servatrice/src/smtp/mimetext.cpp deleted file mode 100644 index 20b1b0a0..00000000 --- a/servatrice/src/smtp/mimetext.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "mimetext.h" - -/* [1] Constructors and Destructors */ - -MimeText::MimeText(const QString &txt) -{ - this->text = txt; - this->cType = "text/plain"; - this->cCharset = "utf-8"; - this->cEncoding = _8Bit; -} - -MimeText::~MimeText() { } - -/* [1] --- */ - - -/* [2] Getters and Setters */ - -void MimeText::setText(const QString & text) -{ - this->text = text; -} - -const QString & MimeText::getText() const -{ - return text; -} - -/* [2] --- */ - - -/* [3] Protected Methods */ - -void MimeText::prepare() -{ - this->content.clear(); - this->content.append(text); - - /* !!! IMPORTANT !!! */ - MimePart::prepare(); -} - -/* [3] --- */ diff --git a/servatrice/src/smtp/mimetext.h b/servatrice/src/smtp/mimetext.h deleted file mode 100644 index c0d29835..00000000 --- a/servatrice/src/smtp/mimetext.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef MIMETEXT_H -#define MIMETEXT_H - -#include "mimepart.h" - -#include "smtpexports.h" - -class SMTP_EXPORT MimeText : public MimePart -{ -public: - - /* [1] Constructors and Destructors */ - - MimeText(const QString &text = ""); - ~MimeText(); - - /* [1] --- */ - - - /* [2] Getters and Setters*/ - - void setText(const QString & text); - - const QString & getText() const; - - /* [2] --- */ - -protected: - - /* [3] Protected members */ - - QString text; - /* [3] --- */ - - - /* [4] Protected methods */ - - void prepare(); - - /* [4] --- */ - -}; - -#endif // MIMETEXT_H diff --git a/servatrice/src/smtp/quotedprintable.cpp b/servatrice/src/smtp/quotedprintable.cpp deleted file mode 100644 index aa3eda6a..00000000 --- a/servatrice/src/smtp/quotedprintable.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "quotedprintable.h" - -QString QuotedPrintable::encode(const QByteArray &input) -{ - QString output; - - char byte; - const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - for (int i = 0; i < input.length() ; ++i) - { - byte = input[i]; - - if ((byte == 0x20) || ((byte >= 33) && (byte <= 126) && (byte != 61))) - { - output.append(byte); - } - else - { - output.append('='); - output.append(hex[((byte >> 4) & 0x0F)]); - output.append(hex[(byte & 0x0F)]); - } - } - - return output; -} - - -QByteArray QuotedPrintable::decode(const QString &input) -{ - // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F - const int hexVal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15}; - - QByteArray output; - - for (int i = 0; i < input.length(); ++i) - { - if (input.at(i).toLatin1() == '=') - { - output.append((hexVal[input.at(i + 1).toLatin1() - '0'] << 4) + hexVal[input.at(i + 2).toLatin1() - '0']); - i += 2; - } - else - { - output.append(input.at(i).toLatin1()); - } - } - - return output; -} diff --git a/servatrice/src/smtp/quotedprintable.h b/servatrice/src/smtp/quotedprintable.h deleted file mode 100644 index 03d3acf8..00000000 --- a/servatrice/src/smtp/quotedprintable.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef QUOTEDPRINTABLE_H -#define QUOTEDPRINTABLE_H - -#include -#include - -#include "smtpexports.h" - -class SMTP_EXPORT QuotedPrintable : public QObject -{ - Q_OBJECT -public: - - static QString encode(const QByteArray &input); - static QByteArray decode(const QString &input); - -private: - QuotedPrintable(); -}; - -#endif // QUOTEDPRINTABLE_H diff --git a/servatrice/src/smtp/qxtglobal.h b/servatrice/src/smtp/qxtglobal.h new file mode 100644 index 00000000..51d374a7 --- /dev/null +++ b/servatrice/src/smtp/qxtglobal.h @@ -0,0 +1,208 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtCore module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#ifndef QXTGLOBAL_H +#define QXTGLOBAL_H + +#include + +#define QXT_VERSION 0x000602 +#define QXT_VERSION_STR "0.6.2" +#define QXT_STATIC + +//--------------------------global macros------------------------------ + +#ifndef QXT_NO_MACROS + +#endif // QXT_NO_MACROS + +//--------------------------export macros------------------------------ + +#define QXT_DLLEXPORT DO_NOT_USE_THIS_ANYMORE + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_CORE) +# define QXT_CORE_EXPORT Q_DECL_EXPORT +# else +# define QXT_CORE_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_CORE_EXPORT +#endif // BUILD_QXT_CORE + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_GUI) +# define QXT_GUI_EXPORT Q_DECL_EXPORT +# else +# define QXT_GUI_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_GUI_EXPORT +#endif // BUILD_QXT_GUI + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_NETWORK) +# define QXT_NETWORK_EXPORT Q_DECL_EXPORT +# else +# define QXT_NETWORK_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_NETWORK_EXPORT +#endif // BUILD_QXT_NETWORK + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_SQL) +# define QXT_SQL_EXPORT Q_DECL_EXPORT +# else +# define QXT_SQL_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_SQL_EXPORT +#endif // BUILD_QXT_SQL + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_WEB) +# define QXT_WEB_EXPORT Q_DECL_EXPORT +# else +# define QXT_WEB_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_WEB_EXPORT +#endif // BUILD_QXT_WEB + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_BERKELEY) +# define QXT_BERKELEY_EXPORT Q_DECL_EXPORT +# else +# define QXT_BERKELEY_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_BERKELEY_EXPORT +#endif // BUILD_QXT_BERKELEY + +#if !defined(QXT_STATIC) +# if defined(BUILD_QXT_ZEROCONF) +# define QXT_ZEROCONF_EXPORT Q_DECL_EXPORT +# else +# define QXT_ZEROCONF_EXPORT Q_DECL_IMPORT +# endif +#else +# define QXT_ZEROCONF_EXPORT +#endif // QXT_ZEROCONF_EXPORT + +#if defined BUILD_QXT_CORE || defined BUILD_QXT_GUI || defined BUILD_QXT_SQL || defined BUILD_QXT_NETWORK || defined BUILD_QXT_WEB || defined BUILD_QXT_BERKELEY || defined BUILD_QXT_ZEROCONF +# define BUILD_QXT +#endif + +QXT_CORE_EXPORT const char* qxtVersion(); + +#ifndef QT_BEGIN_NAMESPACE +#define QT_BEGIN_NAMESPACE +#endif + +#ifndef QT_END_NAMESPACE +#define QT_END_NAMESPACE +#endif + +#ifndef QT_FORWARD_DECLARE_CLASS +#define QT_FORWARD_DECLARE_CLASS(Class) class Class; +#endif + +/**************************************************************************** +** This file is derived from code bearing the following notice: +** The sole author of this file, Adam Higerd, has explicitly disclaimed all +** copyright interest and protection for the content within. This file has +** been placed in the public domain according to United States copyright +** statute and case law. In jurisdictions where this public domain dedication +** is not legally recognized, anyone who receives a copy of this file is +** permitted to use, modify, duplicate, and redistribute this file, in whole +** or in part, with no restrictions or conditions. In these jurisdictions, +** this file shall be copyright (C) 2006-2008 by Adam Higerd. +****************************************************************************/ + +#define QXT_DECLARE_PRIVATE(PUB) friend class PUB##Private; QxtPrivateInterface qxt_d; +#define QXT_DECLARE_PUBLIC(PUB) friend class PUB; +#define QXT_INIT_PRIVATE(PUB) qxt_d.setPublic(this); +#define QXT_D(PUB) PUB##Private& d = qxt_d() +#define QXT_P(PUB) PUB& p = qxt_p() + +template +class QxtPrivate +{ +public: + virtual ~QxtPrivate() + {} + inline void QXT_setPublic(PUB* pub) + { + qxt_p_ptr = pub; + } + +protected: + inline PUB& qxt_p() + { + return *qxt_p_ptr; + } + inline const PUB& qxt_p() const + { + return *qxt_p_ptr; + } + +private: + PUB* qxt_p_ptr; +}; + +template +class QxtPrivateInterface +{ + friend class QxtPrivate; +public: + QxtPrivateInterface() + { + pvt = new PVT; + } + ~QxtPrivateInterface() + { + delete pvt; + } + + inline void setPublic(PUB* pub) + { + pvt->QXT_setPublic(pub); + } + inline PVT& operator()() + { + return *static_cast(pvt); + } + inline const PVT& operator()() const + { + return *static_cast(pvt); + } +private: + QxtPrivateInterface(const QxtPrivateInterface&) { } + QxtPrivateInterface& operator=(const QxtPrivateInterface&) { } + QxtPrivate* pvt; +}; + +#endif // QXT_GLOBAL diff --git a/servatrice/src/smtp/qxthmac.cpp b/servatrice/src/smtp/qxthmac.cpp new file mode 100644 index 00000000..12c6ae74 --- /dev/null +++ b/servatrice/src/smtp/qxthmac.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtCore module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#include "qxthmac.h" +#include + +#if QT_VERSION >= 0x040300 + + +/* +\class QxtHmac + +\inmodule QxtCore + +\brief The QxtHmac class calculates keyed-Hash Message Authentication Codes + +HMAC is a well-known algorithm for generating a message authentication code (MAC) that can be used to verify the +integrity and authenticity of a message. + +This class requires Qt 4.3.0 or greater. + +To verify a message, the sender creates a MAC using a key, which is a secret known only to the sender and recipient, +and the content of the message. This MAC is then sent along with the message. The recipient then creates another MAC +using the shared key and the content of the message. If the two codes match, the message is verified. + +HMAC has been used as a password encryption scheme. The final output of the HMAC algorithm depends on the shared key +and an inner hash. This inner hash is generated from the message content and the key. To use HMAC as a password +scheme, the key should be the username; the message should be the user's password. The authenticating party (for +instance, a login server) only needs to store this inner hash generated by the innerHash() function. When requesting +authentication, the user calculates a HMAC using this key and message and sends his username and this HMAC to the +authenticator. The authenticator can then use verify() using the provided HMAC and the stored inner hash. When using +this scheme, the password is never stored or transmitted in plain text. +*/ + +#ifndef QXT_DOXYGEN_RUN +class QxtHmacPrivate : public QxtPrivate +{ +public: + QXT_DECLARE_PUBLIC(QxtHmac) + QxtHmacPrivate() : ohash(0), ihash(0) {} + ~QxtHmacPrivate() + { + // deleting NULL is safe, so no tests are needed here + delete ohash; + delete ihash; + } + QCryptographicHash* ohash; + QCryptographicHash* ihash; + QByteArray opad, ipad, result; + QCryptographicHash::Algorithm algorithm; +}; +#endif + +/*! + * Constructs a QxtHmac object using the specified algorithm. + */ +QxtHmac::QxtHmac(QCryptographicHash::Algorithm algorithm) +{ + QXT_INIT_PRIVATE(QxtHmac); + qxt_d().ohash = new QCryptographicHash(algorithm); + qxt_d().ihash = new QCryptographicHash(algorithm); + qxt_d().algorithm = algorithm; +} + +/*! + * Sets the shared secret key for the message authentication code. + * + * Any data that had been processed using addData() will be discarded. + */ +void QxtHmac::setKey(QByteArray key) +{ + // We make the assumption that all hashes use a 512-bit block size; as of Qt 4.4.0 this is true of all supported hash functions + QxtHmacPrivate* d = &qxt_d(); + d->opad = QByteArray(64, 0x5c); + d->ipad = QByteArray(64, 0x36); + if (key.size() > 64) + { + key = QCryptographicHash::hash(key, d->algorithm); + } + for (int i = key.size() - 1; i >= 0; --i) + { + d->opad[i] = d->opad[i] ^ key[i]; + d->ipad[i] = d->ipad[i] ^ key[i]; + } + reset(); +} + +/*! + * Resets the object. + * + * Any data that had been processed using addData() will be discarded. + * The key, if set, will be preserved. + */ +void QxtHmac::reset() +{ + QxtHmacPrivate* d = &qxt_d(); + d->ihash->reset(); + d->ihash->addData(d->ipad); +} + +/*! + * Returns the inner hash of the HMAC function. + * + * This hash can be stored in lieu of the shared secret on the authenticating side + * and used for verifying an HMAC code. When used in this manner, HMAC can be used + * to provide a form of secure password authentication. See the documentation above + * for details. + */ +QByteArray QxtHmac::innerHash() const +{ + return qxt_d().ihash->result(); +} + +/*! + * Returns the authentication code for the message. + */ +QByteArray QxtHmac::result() +{ + QxtHmacPrivate* d = &qxt_d(); + Q_ASSERT(d->opad.size()); + if (d->result.size()) + return d->result; + d->ohash->reset(); + d->ohash->addData(d->opad); + d->ohash->addData(innerHash()); + d->result = d->ohash->result(); + return d->result; +} + +/*! + * Verifies the authentication code against a known inner hash. + * + * \sa innerHash() + */ +bool QxtHmac::verify(const QByteArray& otherInner) +{ + result(); // populates d->result + QxtHmacPrivate* d = &qxt_d(); + d->ohash->reset(); + d->ohash->addData(d->opad); + d->ohash->addData(otherInner); + return d->result == d->ohash->result(); +} + +/*! + * Adds the provided data to the message to be authenticated. + */ +void QxtHmac::addData(const char* data, int length) +{ + Q_ASSERT(qxt_d().opad.size()); + qxt_d().ihash->addData(data, length); + qxt_d().result.clear(); +} + +/*! + * Adds the provided data to the message to be authenticated. + */ +void QxtHmac::addData(const QByteArray& data) +{ + addData(data.constData(), data.size()); +} + +/*! + * Returns the HMAC of the provided data using the specified key and hashing algorithm. + */ +QByteArray QxtHmac::hash(const QByteArray& key, const QByteArray& data, Algorithm algorithm) +{ + QxtHmac hmac(algorithm); + hmac.setKey(key); + hmac.addData(data); + return hmac.result(); +} + +/*! + * Verifies a HMAC against a known key and inner hash using the specified hashing algorithm. + */ +bool QxtHmac::verify(const QByteArray& key, const QByteArray& hmac, const QByteArray& inner, Algorithm algorithm) +{ + QxtHmac calc(algorithm); + calc.setKey(key); + + QxtHmacPrivate* d = &calc.qxt_d(); + d->ohash->reset(); + d->ohash->addData(d->opad); + d->ohash->addData(inner); + return hmac == d->ohash->result(); +} + +#endif diff --git a/servatrice/src/smtp/qxthmac.h b/servatrice/src/smtp/qxthmac.h new file mode 100644 index 00000000..8e0d3d4a --- /dev/null +++ b/servatrice/src/smtp/qxthmac.h @@ -0,0 +1,64 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtCore module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#ifndef QXTHMAC_H +#define QXTHMAC_H + +#include + +#if QT_VERSION < 0x040300 +# warning QxtHmac requires Qt 4.3.0 or greater +#else + +#include +#include "qxtglobal.h" + +class QxtHmacPrivate; +class QXT_CORE_EXPORT QxtHmac +{ +public: + typedef QCryptographicHash::Algorithm Algorithm; + + QxtHmac(QCryptographicHash::Algorithm algorithm); + + void setKey(QByteArray key); + void reset(); + + void addData(const char* data, int length); + void addData(const QByteArray& data); + + QByteArray innerHash() const; + QByteArray result(); + bool verify(const QByteArray& otherInner); + + static QByteArray hash(const QByteArray& key, const QByteArray& data, Algorithm algorithm); + static bool verify(const QByteArray& key, const QByteArray& hmac, const QByteArray& inner, Algorithm algorithm); + +private: + QXT_DECLARE_PRIVATE(QxtHmac) +}; + +#endif +#endif diff --git a/servatrice/src/smtp/qxtmail_p.h b/servatrice/src/smtp/qxtmail_p.h new file mode 100644 index 00000000..a9a9dc90 --- /dev/null +++ b/servatrice/src/smtp/qxtmail_p.h @@ -0,0 +1,34 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTMAIL_P_H +#define QXTMAIL_P_H + +#include + +#define QXT_MUST_QP(x) (x < char(32) || x > char(126) || x == '=' || x == '?') +QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, + const QByteArray& prefix = QByteArray()); + +#endif // QXTMAIL_P_H diff --git a/servatrice/src/smtp/qxtmailattachment.cpp b/servatrice/src/smtp/qxtmailattachment.cpp new file mode 100644 index 00000000..59ad464f --- /dev/null +++ b/servatrice/src/smtp/qxtmailattachment.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +/*! + * \class QxtMailAttachment + * \inmodule QxtNetwork + * \brief The QxtMailAttachment class represents an attachement to a QxtMailMessage + */ + + + + +#include "qxtmailattachment.h" +#include "qxtmail_p.h" +#include +#include +#include +#include +#include + +struct QxtMailAttachmentPrivate : public QSharedData +{ + QHash extraHeaders; + QString contentType; + QPointer content; + bool deleteContent; + + QxtMailAttachmentPrivate() + { + content = 0; + deleteContent = false; + contentType = "text/plain"; + } + + ~QxtMailAttachmentPrivate() + { + if (deleteContent && content) + content->deleteLater(); + deleteContent = false; + content = 0; + } +}; + +QxtMailAttachment::QxtMailAttachment() +{ + qxt_d = new QxtMailAttachmentPrivate; +} + +QxtMailAttachment::QxtMailAttachment(const QxtMailAttachment& other) : qxt_d(other.qxt_d) +{ + // trivial copy constructor +} + +QxtMailAttachment::QxtMailAttachment(const QByteArray& content, const QString& contentType) +{ + qxt_d = new QxtMailAttachmentPrivate; + setContentType(contentType); + setContent(content); +} + +QxtMailAttachment::QxtMailAttachment(QIODevice* content, const QString& contentType) +{ + qxt_d = new QxtMailAttachmentPrivate; + setContentType(contentType); + setContent(content); +} + +QxtMailAttachment& QxtMailAttachment::operator=(const QxtMailAttachment & other) +{ + qxt_d = other.qxt_d; + return *this; +} + +QxtMailAttachment::~QxtMailAttachment() +{ + // trivial destructor +} + +QIODevice* QxtMailAttachment::content() const +{ + return qxt_d->content; +} + +void QxtMailAttachment::setContent(const QByteArray& content) +{ + if (qxt_d->deleteContent && qxt_d->content) + qxt_d->content->deleteLater(); + qxt_d->content = new QBuffer; + static_cast(qxt_d->content.data())->setData(content); +} + +void QxtMailAttachment::setContent(QIODevice* content) +{ + if (qxt_d->deleteContent && qxt_d->content) + qxt_d->content->deleteLater(); + qxt_d->content = content; +} + +bool QxtMailAttachment::deleteContent() const +{ + return qxt_d->deleteContent; +} + +void QxtMailAttachment::setDeleteContent(bool enable) +{ + qxt_d->deleteContent = enable; +} + +QString QxtMailAttachment::contentType() const +{ + return qxt_d->contentType; +} + +void QxtMailAttachment::setContentType(const QString& contentType) +{ + qxt_d->contentType = contentType; +} + +QHash QxtMailAttachment::extraHeaders() const +{ + return qxt_d->extraHeaders; +} + +QString QxtMailAttachment::extraHeader(const QString& key) const +{ + return qxt_d->extraHeaders[key.toLower()]; +} + +bool QxtMailAttachment::hasExtraHeader(const QString& key) const +{ + return qxt_d->extraHeaders.contains(key.toLower()); +} + +void QxtMailAttachment::setExtraHeader(const QString& key, const QString& value) +{ + qxt_d->extraHeaders[key.toLower()] = value; +} + +void QxtMailAttachment::setExtraHeaders(const QHash& a) +{ + QHash& headers = qxt_d->extraHeaders; + headers.clear(); + foreach(const QString& key, a.keys()) + { + headers[key.toLower()] = a[key]; + } +} + +void QxtMailAttachment::removeExtraHeader(const QString& key) +{ + qxt_d->extraHeaders.remove(key.toLower()); +} + +QByteArray QxtMailAttachment::mimeData() +{ + QIODevice* c = content(); + if (!c) + { + qWarning() << "QxtMailAttachment::mimeData(): Content not set or already output"; + return QByteArray(); + } + if (!c->isOpen() && !c->open(QIODevice::ReadOnly)) + { + qWarning() << "QxtMailAttachment::mimeData(): Cannot open content for reading"; + return QByteArray(); + } + + QTextCodec* latin1 = QTextCodec::codecForName("latin1"); + QByteArray rv = "Content-Type: " + qxt_d->contentType.toLatin1() + "\r\nContent-Transfer-Encoding: base64\r\n"; + foreach(const QString& r, qxt_d->extraHeaders.keys()) + { + rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r), latin1); + } + rv += "\r\n"; + + while (!c->atEnd()) + { + rv += c->read(57).toBase64() + "\r\n"; + } + setContent((QIODevice*)0); + return rv; +} + +QxtMailAttachment QxtMailAttachment::fromFile(const QString& filename) +{ + QxtMailAttachment rv(new QFile(filename)); + rv.setDeleteContent(true); + return rv; +} diff --git a/servatrice/src/smtp/qxtmailattachment.h b/servatrice/src/smtp/qxtmailattachment.h new file mode 100644 index 00000000..23955ff9 --- /dev/null +++ b/servatrice/src/smtp/qxtmailattachment.h @@ -0,0 +1,73 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTMAILATTACHMENT_H +#define QXTMAILATTACHMENT_H + +#include "qxtglobal.h" + +#include +#include +#include +#include +#include +#include + +struct QxtMailAttachmentPrivate; +class QXT_NETWORK_EXPORT QxtMailAttachment +{ +public: + QxtMailAttachment(); + QxtMailAttachment(const QxtMailAttachment& other); + QxtMailAttachment(const QByteArray& content, const QString& contentType = QString("application/octet-stream")); + QxtMailAttachment(QIODevice* content, const QString& contentType = QString("application/octet-stream")); + QxtMailAttachment& operator=(const QxtMailAttachment& other); + ~QxtMailAttachment(); + static QxtMailAttachment fromFile(const QString& filename); + + QIODevice* content() const; + void setContent(const QByteArray& content); + void setContent(QIODevice* content); + + bool deleteContent() const; + void setDeleteContent(bool enable); + + QString contentType() const; + void setContentType(const QString& contentType); + + QHash extraHeaders() const; + QString extraHeader(const QString&) const; + bool hasExtraHeader(const QString&) const; + void setExtraHeader(const QString& key, const QString& value); + void setExtraHeaders(const QHash&); + void removeExtraHeader(const QString& key); + + QByteArray mimeData(); + +private: + QSharedDataPointer qxt_d; +}; +Q_DECLARE_TYPEINFO(QxtMailAttachment, Q_MOVABLE_TYPE); + +#endif // QXTMAILATTACHMENT_H diff --git a/servatrice/src/smtp/qxtmailmessage.cpp b/servatrice/src/smtp/qxtmailmessage.cpp new file mode 100644 index 00000000..d90740d9 --- /dev/null +++ b/servatrice/src/smtp/qxtmailmessage.cpp @@ -0,0 +1,537 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + + +/*! + * \class QxtMailMessage + * \inmodule QxtNetwork + * \brief The QxtMailMessage class encapsulates an e-mail according to RFC 2822 and related specifications + * TODO: {implicitshared} + */ + + +#include "qxtmailmessage.h" +#include "qxtmail_p.h" +#include +#include +#include +#include + + +struct QxtMailMessagePrivate : public QSharedData +{ + QxtMailMessagePrivate() {} + QxtMailMessagePrivate(const QxtMailMessagePrivate& other) + : QSharedData(other), rcptTo(other.rcptTo), rcptCc(other.rcptCc), rcptBcc(other.rcptBcc), + subject(other.subject), body(other.body), sender(other.sender), + extraHeaders(other.extraHeaders), attachments(other.attachments) {} + QStringList rcptTo, rcptCc, rcptBcc; + QString subject, body, sender; + QHash extraHeaders; + QHash attachments; + mutable QByteArray boundary; +}; + +QxtMailMessage::QxtMailMessage() +{ + qxt_d = new QxtMailMessagePrivate; +} + +QxtMailMessage::QxtMailMessage(const QxtMailMessage& other) : qxt_d(other.qxt_d) +{ + // trivial copy constructor +} + +QxtMailMessage::QxtMailMessage(const QString& sender, const QString& recipient) +{ + qxt_d = new QxtMailMessagePrivate; + setSender(sender); + addRecipient(recipient); +} + +QxtMailMessage::~QxtMailMessage() +{ + // trivial destructor +} + +QxtMailMessage& QxtMailMessage::operator=(const QxtMailMessage & other) +{ + qxt_d = other.qxt_d; + return *this; +} + +QString QxtMailMessage::sender() const +{ + return qxt_d->sender; +} + +void QxtMailMessage::setSender(const QString& a) +{ + qxt_d->sender = a; +} + +QString QxtMailMessage::subject() const +{ + return qxt_d->subject; +} + +void QxtMailMessage::setSubject(const QString& a) +{ + qxt_d->subject = a; +} + +QString QxtMailMessage::body() const +{ + return qxt_d->body; +} + +void QxtMailMessage::setBody(const QString& a) +{ + qxt_d->body = a; +} + +QStringList QxtMailMessage::recipients(QxtMailMessage::RecipientType type) const +{ + if (type == Bcc) + return qxt_d->rcptBcc; + if (type == Cc) + return qxt_d->rcptCc; + return qxt_d->rcptTo; +} + +void QxtMailMessage::addRecipient(const QString& a, QxtMailMessage::RecipientType type) +{ + if (type == Bcc) + qxt_d->rcptBcc.append(a); + else if (type == Cc) + qxt_d->rcptCc.append(a); + else + qxt_d->rcptTo.append(a); +} + +void QxtMailMessage::removeRecipient(const QString& a) +{ + qxt_d->rcptTo.removeAll(a); + qxt_d->rcptCc.removeAll(a); + qxt_d->rcptBcc.removeAll(a); +} + +QHash QxtMailMessage::extraHeaders() const +{ + return qxt_d->extraHeaders; +} + +QString QxtMailMessage::extraHeader(const QString& key) const +{ + return qxt_d->extraHeaders[key.toLower()]; +} + +bool QxtMailMessage::hasExtraHeader(const QString& key) const +{ + return qxt_d->extraHeaders.contains(key.toLower()); +} + +void QxtMailMessage::setExtraHeader(const QString& key, const QString& value) +{ + qxt_d->extraHeaders[key.toLower()] = value; +} + +void QxtMailMessage::setExtraHeaders(const QHash& a) +{ + QHash& headers = qxt_d->extraHeaders; + headers.clear(); + foreach(const QString& key, a.keys()) + { + headers[key.toLower()] = a[key]; + } +} + +void QxtMailMessage::removeExtraHeader(const QString& key) +{ + qxt_d->extraHeaders.remove(key.toLower()); +} + +QHash QxtMailMessage::attachments() const +{ + return qxt_d->attachments; +} + +QxtMailAttachment QxtMailMessage::attachment(const QString& filename) const +{ + return qxt_d->attachments[filename]; +} + +void QxtMailMessage::addAttachment(const QString& filename, const QxtMailAttachment& attach) +{ + if (qxt_d->attachments.contains(filename)) + { + qWarning() << "QxtMailMessage::addAttachment: " << filename << " already in use"; + int i = 1; + while (qxt_d->attachments.contains(filename + "." + QString::number(i))) + { + i++; + } + qxt_d->attachments[filename+"."+QString::number(i)] = attach; + } + else + { + qxt_d->attachments[filename] = attach; + } +} + +void QxtMailMessage::removeAttachment(const QString& filename) +{ + qxt_d->attachments.remove(filename); +} + +QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix) +{ + QByteArray rv = ""; + QByteArray line = key.toLatin1() + ": "; + if (!prefix.isEmpty()) line += prefix; + if (!value.contains("=?") && latin1->canEncode(value)) + { + bool firstWord = true; + foreach(const QByteArray& word, value.toLatin1().split(' ')) + { + if (line.size() > 78) + { + rv = rv + line + "\r\n"; + line.clear(); + } + if (firstWord) + line += word; + else + line += " " + word; + firstWord = false; + } + } + else + { + // The text cannot be losslessly encoded as Latin-1. Therefore, we + // must use quoted-printable or base64 encoding. This is a quick + // heuristic based on the first 100 characters to see which + // encoding to use. + QByteArray utf8 = value.toUtf8(); + int ct = utf8.length(); + int nonAscii = 0; + for (int i = 0; i < ct && i < 100; i++) + { + if (QXT_MUST_QP(utf8[i])) nonAscii++; + } + if (nonAscii > 20) + { + // more than 20%-ish non-ASCII characters: use base64 + QByteArray base64 = utf8.toBase64(); + ct = base64.length(); + line += "=?utf-8?b?"; + for (int i = 0; i < ct; i += 4) + { + if (line.length() > 72) + { + rv += line + "?\r\n"; + line = " =?utf-8?b?"; + } + line = line + base64.mid(i, 4); + } + } + else + { + // otherwise use Q-encoding + line += "=?utf-8?q?"; + for (int i = 0; i < ct; i++) + { + if (line.length() > 73) + { + rv += line + "?\r\n"; + line = " =?utf-8?q?"; + } + if (QXT_MUST_QP(utf8[i]) || utf8[i] == ' ') + { + line += "=" + utf8.mid(i, 1).toHex().toUpper(); + } + else + { + line += utf8[i]; + } + } + } + line += "?="; // end encoded-word atom + } + return rv + line + "\r\n"; +} + +QByteArray QxtMailMessage::rfc2822() const +{ + // Use quoted-printable if requested + bool useQuotedPrintable = (extraHeader("Content-Transfer-Encoding").toLower() == "quoted-printable"); + // Use base64 if requested + bool useBase64 = (extraHeader("Content-Transfer-Encoding").toLower() == "base64"); + // Check to see if plain text is ASCII-clean; assume it isn't if QP or base64 was requested + QTextCodec* latin1 = QTextCodec::codecForName("latin1"); + bool bodyIsAscii = latin1->canEncode(body()) && !useQuotedPrintable && !useBase64; + + QHash attach = attachments(); + QByteArray rv; + + if (!sender().isEmpty() && !hasExtraHeader("From")) + { + rv += qxt_fold_mime_header("From", sender(), latin1); + } + + if (!qxt_d->rcptTo.isEmpty()) + { + rv += qxt_fold_mime_header("To", qxt_d->rcptTo.join(", "), latin1); + } + + if (!qxt_d->rcptCc.isEmpty()) + { + rv += qxt_fold_mime_header("Cc", qxt_d->rcptCc.join(", "), latin1); + } + + if (!subject().isEmpty()) + { + rv += qxt_fold_mime_header("Subject", subject(), latin1); + } + + if (!bodyIsAscii) + { + if (!hasExtraHeader("MIME-Version") && !attach.count()) + rv += "MIME-Version: 1.0\r\n"; + + // If no transfer encoding has been requested, guess. + // Heuristic: If >20% of the first 100 characters aren't + // 7-bit clean, use base64, otherwise use Q-P. + if(!bodyIsAscii && !useQuotedPrintable && !useBase64) + { + QString b = body(); + int nonAscii = 0; + int ct = b.length(); + for (int i = 0; i < ct && i < 100; i++) + { + if (QXT_MUST_QP(b[i])) nonAscii++; + } + useQuotedPrintable = !(nonAscii > 20); + useBase64 = !useQuotedPrintable; + } + } + + if (attach.count()) + { + if (qxt_d->boundary.isEmpty()) + qxt_d->boundary = QUuid::createUuid().toString().toLatin1().replace("{", "").replace("}", ""); + if (!hasExtraHeader("MIME-Version")) + rv += "MIME-Version: 1.0\r\n"; + if (!hasExtraHeader("Content-Type")) + rv += "Content-Type: multipart/mixed; boundary=" + qxt_d->boundary + "\r\n"; + } + else if (!bodyIsAscii && !hasExtraHeader("Content-Transfer-Encoding")) + { + if (!useQuotedPrintable) + { + // base64 + rv += "Content-Transfer-Encoding: base64\r\n"; + } + else + { + // quoted-printable + rv += "Content-Transfer-Encoding: quoted-printable\r\n"; + } + } + + foreach(const QString& r, qxt_d->extraHeaders.keys()) + { + if ((r.toLower() == "content-type" || r.toLower() == "content-transfer-encoding") && attach.count()) + { + // Since we're in multipart mode, we'll be outputting this later + continue; + } + rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r), latin1); + } + + rv += "\r\n"; + + if (attach.count()) + { + // we're going to have attachments, so output the lead-in for the message body + rv += "This is a message with multiple parts in MIME format.\r\n"; + rv += "--" + qxt_d->boundary + "\r\nContent-Type: "; + if (hasExtraHeader("Content-Type")) + rv += extraHeader("Content-Type") + "\r\n"; + else + rv += "text/plain; charset=UTF-8\r\n"; + if (hasExtraHeader("Content-Transfer-Encoding")) + { + rv += "Content-Transfer-Encoding: " + extraHeader("Content-Transfer-Encoding") + "\r\n"; + } + else if (!bodyIsAscii) + { + if (!useQuotedPrintable) + { + // base64 + rv += "Content-Transfer-Encoding: base64\r\n"; + } + else + { + // quoted-printable + rv += "Content-Transfer-Encoding: quoted-printable\r\n"; + } + } + rv += "\r\n"; + } + + if (bodyIsAscii) + { + QByteArray b = latin1->fromUnicode(body()); + int len = b.length(); + QByteArray line = ""; + QByteArray word = ""; + for (int i = 0; i < len; i++) + { + if (b[i] == '\n' || b[i] == '\r') + { + if (line.isEmpty()) + { + line = word; + word = ""; + } + else if (line.length() + word.length() + 1 <= 78) + { + line = line + ' ' + word; + word = ""; + } + if(line[0] == '.') + rv += "."; + rv += line + "\r\n"; + if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1]) + { + // If we're looking at a CRLF pair, skip the second half + i++; + } + line = word; + } + else if (b[i] == ' ') + { + if (line.length() + word.length() + 1 > 78) + { + if(line[0] == '.') + rv += "."; + rv += line + "\r\n"; + line = word; + } + else if (line.isEmpty()) + { + line = word; + } + else + { + line = line + ' ' + word; + } + word = ""; + } + else + { + word += b[i]; + } + } + if (line.length() + word.length() + 1 > 78) + { + if(line[0] == '.') + rv += "."; + rv += line + "\r\n"; + line = word; + } + else if (!word.isEmpty()) + { + line += ' ' + word; + } + if(!line.isEmpty()) { + if(line[0] == '.') + rv += "."; + rv += line + "\r\n"; + } + } + else if (useQuotedPrintable) + { + QByteArray b = body().toUtf8(); + int ct = b.length(); + QByteArray line; + for (int i = 0; i < ct; i++) + { + if(b[i] == '\n' || b[i] == '\r') + { + if(line[0] == '.') + rv += "."; + rv += line + "\r\n"; + line = ""; + if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1]) + { + // If we're looking at a CRLF pair, skip the second half + i++; + } + } + else if (line.length() > 74) + { + rv += line + "=\r\n"; + line = ""; + } + if (QXT_MUST_QP(b[i])) + { + line += "=" + b.mid(i, 1).toHex().toUpper(); + } + else + { + line += b[i]; + } + } + if(!line.isEmpty()) { + if(line[0] == '.') + rv += "."; + rv += line + "\r\n"; + } + } + else /* base64 */ + { + QByteArray b = body().toUtf8().toBase64(); + int ct = b.length(); + for (int i = 0; i < ct; i += 78) + { + rv += b.mid(i, 78) + "\r\n"; + } + } + + if (attach.count()) + { + foreach(const QString& filename, attach.keys()) + { + rv += "--" + qxt_d->boundary + "\r\n"; + rv += qxt_fold_mime_header("Content-Disposition", QDir(filename).dirName(), latin1, "attachment; filename="); + rv += attach[filename].mimeData(); + } + rv += "--" + qxt_d->boundary + "--\r\n"; + } + + return rv; +} diff --git a/servatrice/src/smtp/qxtmailmessage.h b/servatrice/src/smtp/qxtmailmessage.h new file mode 100644 index 00000000..5fd71507 --- /dev/null +++ b/servatrice/src/smtp/qxtmailmessage.h @@ -0,0 +1,85 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTMAILMESSAGE_H +#define QXTMAILMESSAGE_H + +#include "qxtglobal.h" +#include "qxtmailattachment.h" + +#include +#include +#include +#include + +struct QxtMailMessagePrivate; +class QXT_NETWORK_EXPORT QxtMailMessage +{ +public: + enum RecipientType + { + To, + Cc, + Bcc + }; + + QxtMailMessage(); + QxtMailMessage(const QxtMailMessage& other); + QxtMailMessage(const QString& sender, const QString& recipient); + QxtMailMessage& operator=(const QxtMailMessage& other); + ~QxtMailMessage(); + + QString sender() const; + void setSender(const QString&); + + QString subject() const; + void setSubject(const QString&); + + QString body() const; + void setBody(const QString&); + + QStringList recipients(RecipientType type = To) const; + void addRecipient(const QString&, RecipientType type = To); + void removeRecipient(const QString&); + + QHash extraHeaders() const; + QString extraHeader(const QString&) const; + bool hasExtraHeader(const QString&) const; + void setExtraHeader(const QString& key, const QString& value); + void setExtraHeaders(const QHash&); + void removeExtraHeader(const QString& key); + + QHash attachments() const; + QxtMailAttachment attachment(const QString& filename) const; + void addAttachment(const QString& filename, const QxtMailAttachment& attach); + void removeAttachment(const QString& filename); + + QByteArray rfc2822() const; + +private: + QSharedDataPointer qxt_d; +}; +Q_DECLARE_TYPEINFO(QxtMailMessage, Q_MOVABLE_TYPE); + +#endif // QXTMAIL_H diff --git a/servatrice/src/smtp/qxtsmtp.cpp b/servatrice/src/smtp/qxtsmtp.cpp new file mode 100644 index 00000000..5ec59f0c --- /dev/null +++ b/servatrice/src/smtp/qxtsmtp.cpp @@ -0,0 +1,617 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + + +/*! + * \class QxtSmtp + * \inmodule QxtNetwork + * \brief The QxtSmtp class implements the SMTP protocol for sending email + */ + + + +#include "qxtsmtp.h" +#include "qxtsmtp_p.h" +#include "qxthmac.h" +#include +#include +#include +#include + +QxtSmtpPrivate::QxtSmtpPrivate() : QObject(0) +{ + // empty ctor +} + +QxtSmtp::QxtSmtp(QObject* parent) : QObject(parent) +{ + QXT_INIT_PRIVATE(QxtSmtp); + qxt_d().state = QxtSmtpPrivate::Disconnected; + qxt_d().nextID = 0; + qxt_d().socket = new QSslSocket(this); + QObject::connect(socket(), SIGNAL(encrypted()), this, SIGNAL(encrypted())); + //QObject::connect(socket(), SIGNAL(encrypted()), &qxt_d(), SLOT(ehlo())); + QObject::connect(socket(), SIGNAL(connected()), this, SIGNAL(connected())); + QObject::connect(socket(), SIGNAL(disconnected()), this, SIGNAL(disconnected())); + QObject::connect(socket(), SIGNAL(error(QAbstractSocket::SocketError)), &qxt_d(), SLOT(socketError(QAbstractSocket::SocketError))); + QObject::connect(this, SIGNAL(authenticated()), &qxt_d(), SLOT(sendNext())); + QObject::connect(socket(), SIGNAL(readyRead()), &qxt_d(), SLOT(socketRead())); +} + +QByteArray QxtSmtp::username() const +{ + return qxt_d().username; +} + +void QxtSmtp::setUsername(const QByteArray& username) +{ + qxt_d().username = username; +} + +QByteArray QxtSmtp::password() const +{ + return qxt_d().password; +} + +void QxtSmtp::setPassword(const QByteArray& password) +{ + qxt_d().password = password; +} + +int QxtSmtp::send(const QxtMailMessage& message) +{ + int messageID = ++qxt_d().nextID; + qxt_d().pending.append(qMakePair(messageID, message)); + if (qxt_d().state == QxtSmtpPrivate::Waiting) + qxt_d().sendNext(); + return messageID; +} + +int QxtSmtp::pendingMessages() const +{ + return qxt_d().pending.count(); +} + +QTcpSocket* QxtSmtp::socket() const +{ + return qxt_d().socket; +} + +void QxtSmtp::connectToHost(const QString& hostName, quint16 port) +{ + qxt_d().useSecure = false; + qxt_d().state = QxtSmtpPrivate::StartState; + socket()->connectToHost(hostName, port); +} + +void QxtSmtp::connectToHost(const QHostAddress& address, quint16 port) +{ + connectToHost(address.toString(), port); +} + +void QxtSmtp::disconnectFromHost() +{ + socket()->disconnectFromHost(); +} + +bool QxtSmtp::startTlsDisabled() const +{ + return qxt_d().disableStartTLS; +} + +void QxtSmtp::setStartTlsDisabled(bool disable) +{ + qxt_d().disableStartTLS = disable; +} + +QSslSocket* QxtSmtp::sslSocket() const +{ + return qxt_d().socket; +} + +void QxtSmtp::connectToSecureHost(const QString& hostName, quint16 port) +{ + qxt_d().useSecure = true; + qxt_d().state = QxtSmtpPrivate::StartState; + sslSocket()->connectToHostEncrypted(hostName, port); +} + +void QxtSmtp::connectToSecureHost(const QHostAddress& address, quint16 port) +{ + connectToSecureHost(address.toString(), port); +} + +bool QxtSmtp::hasExtension(const QString& extension) +{ + return qxt_d().extensions.contains(extension); +} + +QString QxtSmtp::extensionData(const QString& extension) +{ + return qxt_d().extensions[extension]; +} + +void QxtSmtpPrivate::socketError(QAbstractSocket::SocketError err) +{ + if (err == QAbstractSocket::SslHandshakeFailedError) + { + emit qxt_p().encryptionFailed(); + emit qxt_p().encryptionFailed( socket->errorString().toLatin1() ); + } + else if (state == StartState) + { + emit qxt_p().connectionFailed(); + emit qxt_p().connectionFailed( socket->errorString().toLatin1() ); + } +} + +void QxtSmtpPrivate::socketRead() +{ + buffer += socket->readAll(); + while (true) + { + int pos = buffer.indexOf("\r\n"); + if (pos < 0) return; + QByteArray line = buffer.left(pos); + buffer = buffer.mid(pos + 2); + QByteArray code = line.left(3); + switch (state) + { + case StartState: + if (code[0] != '2') + { + socket->disconnectFromHost(); + } + else + { + ehlo(); + } + break; + case HeloSent: + case EhloSent: + case EhloGreetReceived: + parseEhlo(code, (line[3] != ' '), line.mid(4)); + break; + case StartTLSSent: + if (code == "220") + { + socket->startClientEncryption(); + ehlo(); + } + else + { + authenticate(); + } + break; + case AuthRequestSent: + case AuthUsernameSent: + if (authType == AuthPlain) authPlain(); + else if (authType == AuthLogin) authLogin(); + else authCramMD5(line.mid(4)); + break; + case AuthSent: + if (code[0] == '2') + { + state = Authenticated; + emit qxt_p().authenticated(); + } + else + { + state = Disconnected; + emit qxt_p().authenticationFailed(); + emit qxt_p().authenticationFailed( line ); + emit socket->disconnectFromHost(); + } + break; + case MailToSent: + case RcptAckPending: + if (code[0] != '2') { + emit qxt_p().mailFailed( pending.first().first, code.toInt() ); + emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); + // pending.removeFirst(); + // DO NOT remove it, the body sent state needs this message to assigned the next mail failed message that will + // the sendNext + // a reset will be sent to clear things out + sendNext(); + state = BodySent; + } + else + sendNextRcpt(code, line); + break; + case SendingBody: + sendBody(code, line); + break; + case BodySent: + if ( pending.count() ) + { + // if you removeFirst in RcpActpending/MailToSent on an error, and the queue is now empty, + // you will get into this state and then crash because no check is done. CHeck added but shouldnt + // be necessary since I commented out the removeFirst + if (code[0] != '2') + { + emit qxt_p().mailFailed(pending.first().first, code.toInt() ); + emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); + } + else + emit qxt_p().mailSent(pending.first().first); + pending.removeFirst(); + } + sendNext(); + break; + case Resetting: + if (code[0] != '2') { + emit qxt_p().connectionFailed(); + emit qxt_p().connectionFailed( line ); + } + else { + state = Waiting; + sendNext(); + } + break; + case Disconnected: + case EhloExtensionsReceived: + case EhloDone: + case Authenticated: + case Waiting: + // only to make compiler happy + break; + } + } +} + +void QxtSmtpPrivate::ehlo() +{ + QByteArray address = "127.0.0.1"; + foreach(const QHostAddress& addr, QNetworkInterface::allAddresses()) + { + if (addr == QHostAddress::LocalHost || addr == QHostAddress::LocalHostIPv6) + continue; + address = addr.toString().toLatin1(); + break; + } + socket->write("ehlo " + address + "\r\n"); + extensions.clear(); + state = EhloSent; +} + +void QxtSmtpPrivate::parseEhlo(const QByteArray& code, bool cont, const QString& line) +{ + if (code != "250") + { + // error! + if (state != HeloSent) + { + // maybe let's try HELO + socket->write("helo\r\n"); + state = HeloSent; + } + else + { + // nope + socket->write("QUIT\r\n"); + socket->flush(); + socket->disconnectFromHost(); + } + return; + } + else if (state != EhloGreetReceived) + { + if (!cont) + { + // greeting only, no extensions + state = EhloDone; + } + else + { + // greeting followed by extensions + state = EhloGreetReceived; + return; + } + } + else + { + extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1); + if (!cont) + state = EhloDone; + } + if (state != EhloDone) return; + if (extensions.contains("STARTTLS") && !disableStartTLS) + { + startTLS(); + } + else + { + authenticate(); + } +} + +void QxtSmtpPrivate::startTLS() +{ + socket->write("starttls\r\n"); + state = StartTLSSent; +} + +void QxtSmtpPrivate::authenticate() +{ + if (!extensions.contains("AUTH") || username.isEmpty() || password.isEmpty()) + { + state = Authenticated; + emit qxt_p().authenticated(); + } + else + { + QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); + if (auth.contains("CRAM-MD5")) + { + authCramMD5(); + } + else if (auth.contains("PLAIN")) + { + authPlain(); + } + else if (auth.contains("LOGIN")) + { + authLogin(); + } + else + { + state = Authenticated; + emit qxt_p().authenticated(); + } + } +} + +void QxtSmtpPrivate::authCramMD5(const QByteArray& challenge) +{ + if (state != AuthRequestSent) + { + socket->write("auth cram-md5\r\n"); + authType = AuthCramMD5; + state = AuthRequestSent; + } + else + { + QxtHmac hmac(QCryptographicHash::Md5); + hmac.setKey(password); + hmac.addData(QByteArray::fromBase64(challenge)); + QByteArray response = username + ' ' + hmac.result().toHex(); + socket->write(response.toBase64() + "\r\n"); + state = AuthSent; + } +} + +void QxtSmtpPrivate::authPlain() +{ + if (state != AuthRequestSent) + { + socket->write("auth plain\r\n"); + authType = AuthPlain; + state = AuthRequestSent; + } + else + { + QByteArray auth; + auth += '\0'; + auth += username; + auth += '\0'; + auth += password; + socket->write(auth.toBase64() + "\r\n"); + state = AuthSent; + } +} + +void QxtSmtpPrivate::authLogin() +{ + if (state != AuthRequestSent && state != AuthUsernameSent) + { + socket->write("auth login\r\n"); + authType = AuthLogin; + state = AuthRequestSent; + } + else if (state == AuthRequestSent) + { + socket->write(username.toBase64() + "\r\n"); + state = AuthUsernameSent; + } + else + { + socket->write(password.toBase64() + "\r\n"); + state = AuthSent; + } +} + +static QByteArray qxt_extract_address(const QString& address) +{ + int parenDepth = 0; + int addrStart = -1; + bool inQuote = false; + int ct = address.length(); + + for (int i = 0; i < ct; i++) + { + QChar ch = address[i]; + if (inQuote) + { + if (ch == '"') + inQuote = false; + } + else if (addrStart != -1) + { + if (ch == '>') + return address.mid(addrStart, (i - addrStart)).toLatin1(); + } + else if (ch == '(') + { + parenDepth++; + } + else if (ch == ')') + { + parenDepth--; + if (parenDepth < 0) parenDepth = 0; + } + else if (ch == '"') + { + if (parenDepth == 0) + inQuote = true; + } + else if (ch == '<') + { + if (!inQuote && parenDepth == 0) + addrStart = i + 1; + } + } + return address.toLatin1(); +} + +void QxtSmtpPrivate::sendNext() +{ + if (state == Disconnected) + { + // leave the mail in the queue if not ready to send + return; + } + + if (pending.isEmpty()) + { + // if there are no additional mails to send, finish up + state = Waiting; + emit qxt_p().finished(); + return; + } + + if(state != Waiting) { + state = Resetting; + socket->write("rset\r\n"); + return; + } + const QxtMailMessage& msg = pending.first().second; + rcptNumber = rcptAck = mailAck = 0; + recipients = msg.recipients(QxtMailMessage::To) + + msg.recipients(QxtMailMessage::Cc) + + msg.recipients(QxtMailMessage::Bcc); + if (recipients.count() == 0) + { + // can't send an e-mail with no recipients + emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients ); + emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients, QByteArray( "e-mail has no recipients" ) ); + pending.removeFirst(); + sendNext(); + return; + } + // We explicitly use lowercase keywords because for some reason gmail + // interprets any string starting with an uppercase R as a request + // to renegotiate the SSL connection. + socket->write("mail from:<" + qxt_extract_address(msg.sender()) + ">\r\n"); + if (extensions.contains("PIPELINING")) // almost all do nowadays + { + foreach(const QString& rcpt, recipients) + { + socket->write("rcpt to:<" + qxt_extract_address(rcpt) + ">\r\n"); + } + state = RcptAckPending; + } + else + { + state = MailToSent; + } +} + +void QxtSmtpPrivate::sendNextRcpt(const QByteArray& code, const QByteArray&line) +{ + int messageID = pending.first().first; + const QxtMailMessage& msg = pending.first().second; + + if (code[0] != '2') + { + // on failure, emit a warning signal + if (!mailAck) + { + emit qxt_p().senderRejected(messageID, msg.sender()); + emit qxt_p().senderRejected(messageID, msg.sender(), line ); + } + else + { + emit qxt_p().recipientRejected(messageID, msg.sender()); + emit qxt_p().recipientRejected(messageID, msg.sender(), line); + } + } + else if (!mailAck) + { + mailAck = true; + } + else + { + rcptAck++; + } + + if (rcptNumber == recipients.count()) + { + // all recipients have been sent + if (rcptAck == 0) + { + // no recipients were considered valid + emit qxt_p().mailFailed(messageID, code.toInt() ); + emit qxt_p().mailFailed(messageID, code.toInt(), line); + pending.removeFirst(); + sendNext(); + } + else + { + // at least one recipient was acknowledged, send mail body + socket->write("data\r\n"); + state = SendingBody; + } + } + else if (state != RcptAckPending) + { + // send the next recipient unless we're only waiting on acks + socket->write("rcpt to:<" + qxt_extract_address(recipients[rcptNumber]) + ">\r\n"); + rcptNumber++; + } + else + { + // If we're only waiting on acks, just count them + rcptNumber++; + } +} + +void QxtSmtpPrivate::sendBody(const QByteArray& code, const QByteArray & line) +{ + int messageID = pending.first().first; + const QxtMailMessage& msg = pending.first().second; + + if (code[0] != '3') + { + emit qxt_p().mailFailed(messageID, code.toInt() ); + emit qxt_p().mailFailed(messageID, code.toInt(), line); + pending.removeFirst(); + sendNext(); + return; + } + + socket->write(msg.rfc2822()); + socket->write(".\r\n"); + state = BodySent; +} diff --git a/servatrice/src/smtp/qxtsmtp.h b/servatrice/src/smtp/qxtsmtp.h new file mode 100644 index 00000000..747e22df --- /dev/null +++ b/servatrice/src/smtp/qxtsmtp.h @@ -0,0 +1,111 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSMTP_H +#define QXTSMTP_H + +#include +#include +#include + +#include "qxtglobal.h" +#include "qxtmailmessage.h" + +class QTcpSocket; +class QSslSocket; + +class QxtSmtpPrivate; +class QXT_NETWORK_EXPORT QxtSmtp : public QObject +{ + Q_OBJECT +public: + enum SmtpError + { + NoError, + NoRecipients, + CommandUnrecognized = 500, + SyntaxError, + CommandNotImplemented, + BadSequence, + ParameterNotImplemented, + MailboxUnavailable = 550, + UserNotLocal, + MessageTooLarge, + InvalidMailboxName, + TransactionFailed + }; + + QxtSmtp(QObject* parent = 0); + + QByteArray username() const; + void setUsername(const QByteArray& name); + + QByteArray password() const; + void setPassword(const QByteArray& password); + + int send(const QxtMailMessage& message); + int pendingMessages() const; + + QTcpSocket* socket() const; + void connectToHost(const QString& hostName, quint16 port = 25); + void connectToHost(const QHostAddress& address, quint16 port = 25); + void disconnectFromHost(); + + bool startTlsDisabled() const; + void setStartTlsDisabled(bool disable); + + QSslSocket* sslSocket() const; + void connectToSecureHost(const QString& hostName, quint16 port = 465); + void connectToSecureHost(const QHostAddress& address, quint16 port = 465); + + bool hasExtension(const QString& extension); + QString extensionData(const QString& extension); + +Q_SIGNALS: + void connected(); + void connectionFailed(); + void connectionFailed( const QByteArray & msg ); + void encrypted(); + void encryptionFailed(); + void encryptionFailed( const QByteArray & msg ); + void authenticated(); + void authenticationFailed(); + void authenticationFailed( const QByteArray & msg ); + + void senderRejected(int mailID, const QString& address ); + void senderRejected(int mailID, const QString& address, const QByteArray & msg ); + void recipientRejected(int mailID, const QString& address ); + void recipientRejected(int mailID, const QString& address, const QByteArray & msg ); + void mailFailed(int mailID, int errorCode); + void mailFailed(int mailID, int errorCode, const QByteArray & msg); + void mailSent(int mailID); + + void finished(); + void disconnected(); + +private: + QXT_DECLARE_PRIVATE(QxtSmtp) +}; + +#endif // QXTSMTP_H diff --git a/servatrice/src/smtp/qxtsmtp_p.h b/servatrice/src/smtp/qxtsmtp_p.h new file mode 100644 index 00000000..a8e234b1 --- /dev/null +++ b/servatrice/src/smtp/qxtsmtp_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtWeb module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSMTP_P_H +#define QXTSMTP_P_H + +#include "qxtsmtp.h" +#include +#include +#include +#include + +class QxtSmtpPrivate : public QObject, public QxtPrivate +{ + Q_OBJECT +public: + QxtSmtpPrivate(); + + QXT_DECLARE_PUBLIC(QxtSmtp) + + enum SmtpState + { + Disconnected, + StartState, + EhloSent, + EhloGreetReceived, + EhloExtensionsReceived, + EhloDone, + HeloSent, + StartTLSSent, + AuthRequestSent, + AuthUsernameSent, + AuthSent, + Authenticated, + MailToSent, + RcptAckPending, + SendingBody, + BodySent, + Waiting, + Resetting + }; + + enum AuthType + { + AuthPlain, + AuthLogin, + AuthCramMD5 + }; + + bool useSecure, disableStartTLS; + SmtpState state;// rather then an int use the enum. makes sure invalid states are entered at compile time, and makes debugging easier + AuthType authType; + QByteArray buffer, username, password; + QHash extensions; + QList > pending; + QStringList recipients; + int nextID, rcptNumber, rcptAck; + bool mailAck; + + QSslSocket* socket; + + void parseEhlo(const QByteArray& code, bool cont, const QString& line); + void startTLS(); + void authenticate(); + + void authCramMD5(const QByteArray& challenge = QByteArray()); + void authPlain(); + void authLogin(); + + void sendNextRcpt(const QByteArray& code, const QByteArray & line); + void sendBody(const QByteArray& code, const QByteArray & line); + +public slots: + void socketError(QAbstractSocket::SocketError err); + void socketRead(); + + void ehlo(); + void sendNext(); +}; + +#endif // QXTSMTP_P_H diff --git a/servatrice/src/smtp/smtpclient.cpp b/servatrice/src/smtp/smtpclient.cpp deleted file mode 100644 index 37988871..00000000 --- a/servatrice/src/smtp/smtpclient.cpp +++ /dev/null @@ -1,482 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#include "smtpclient.h" - -#include -#include - - -/* [1] Constructors and destructors */ - -SmtpClient::SmtpClient(const QString & host, int port, ConnectionType connectionType) : - name("localhost"), - authMethod(AuthPlain), - connectionTimeout(5000), - responseTimeout(5000), - sendMessageTimeout(60000) -{ - setConnectionType(connectionType); - - this->host = host; - this->port = port; - - connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); - connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(socketError(QAbstractSocket::SocketError))); - connect(socket, SIGNAL(readyRead()), - this, SLOT(socketReadyRead())); -} - -SmtpClient::~SmtpClient() {} - -/* [1] --- */ - - -/* [2] Getters and Setters */ - -void SmtpClient::setUser(const QString &user) -{ - this->user = user; -} - -void SmtpClient::setPassword(const QString &password) -{ - this->password = password; -} - -void SmtpClient::setAuthMethod(AuthMethod method) -{ - this->authMethod = method; -} - -void SmtpClient::setHost(const QString &host) -{ - this->host = host; -} - -void SmtpClient::setPort(int port) -{ - this->port = port; -} - -void SmtpClient::setConnectionType(ConnectionType ct) -{ - this->connectionType = ct; - - switch (connectionType) - { - case TcpConnection: - socket = new QTcpSocket(this); - break; - case SslConnection: - case TlsConnection: - socket = new QSslSocket(this); - } -} - -const QString& SmtpClient::getHost() const -{ - return this->host; -} - -const QString& SmtpClient::getUser() const -{ - return this->user; -} - -const QString& SmtpClient::getPassword() const -{ - return this->password; -} - -SmtpClient::AuthMethod SmtpClient::getAuthMethod() const -{ - return this->authMethod; -} - -int SmtpClient::getPort() const -{ - return this->port; -} - -SmtpClient::ConnectionType SmtpClient::getConnectionType() const -{ - return connectionType; -} - -const QString& SmtpClient::getName() const -{ - return this->name; -} - -void SmtpClient::setName(const QString &name) -{ - this->name = name; -} - -const QString & SmtpClient::getResponseText() const -{ - return responseText; -} - -int SmtpClient::getResponseCode() const -{ - return responseCode; -} - -QTcpSocket* SmtpClient::getSocket() { - return socket; -} - -int SmtpClient::getConnectionTimeout() const -{ - return connectionTimeout; -} - -void SmtpClient::setConnectionTimeout(int msec) -{ - connectionTimeout = msec; -} - -int SmtpClient::getResponseTimeout() const -{ - return responseTimeout; -} - -void SmtpClient::setResponseTimeout(int msec) -{ - responseTimeout = msec; -} -int SmtpClient::getSendMessageTimeout() const -{ - return sendMessageTimeout; -} -void SmtpClient::setSendMessageTimeout(int msec) -{ - sendMessageTimeout = msec; -} - -/* [2] --- */ - - -/* [3] Public methods */ - -bool SmtpClient::connectToHost() -{ - switch (connectionType) - { - case TlsConnection: - case TcpConnection: - socket->connectToHost(host, port); - break; - case SslConnection: - ((QSslSocket*) socket)->connectToHostEncrypted(host, port); - break; - - } - - // Tries to connect to server - if (!socket->waitForConnected(connectionTimeout)) - { - emit smtpError(ConnectionTimeoutError); - return false; - } - - try - { - // Wait for the server's response - waitForResponse(); - - // If the response code is not 220 (Service ready) - // means that is something wrong with the server - if (responseCode != 220) - { - emit smtpError(ServerError); - return false; - } - - // Send a EHLO/HELO message to the server - // The client's first command must be EHLO/HELO - sendMessage("EHLO " + name); - - // Wait for the server's response - waitForResponse(); - - // The response code needs to be 250. - if (responseCode != 250) { - emit smtpError(ServerError); - return false; - } - - if (connectionType == TlsConnection) { - // send a request to start TLS handshake - sendMessage("STARTTLS"); - - // Wait for the server's response - waitForResponse(); - - // The response code needs to be 220. - if (responseCode != 220) { - emit smtpError(ServerError); - return false; - }; - - ((QSslSocket*) socket)->startClientEncryption(); - - if (!((QSslSocket*) socket)->waitForEncrypted(connectionTimeout)) { - qDebug() << ((QSslSocket*) socket)->errorString(); - emit smtpError(ConnectionTimeoutError); - return false; - } - - // Send ELHO one more time - sendMessage("EHLO " + name); - - // Wait for the server's response - waitForResponse(); - - // The response code needs to be 250. - if (responseCode != 250) { - emit smtpError(ServerError); - return false; - } - } - } - catch (ResponseTimeoutException) - { - return false; - } - catch (SendMessageTimeoutException) - { - return false; - } - - // If no errors occured the function returns true. - return true; -} - -bool SmtpClient::login() -{ - return login(user, password, authMethod); -} - -bool SmtpClient::login(const QString &user, const QString &password, AuthMethod method) -{ - try { - if (method == AuthPlain) - { - // Sending command: AUTH PLAIN base64('\0' + username + '\0' + password) - sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user).append((char) 0).append(password).toBase64()); - - // Wait for the server's response - waitForResponse(); - - // If the response is not 235 then the authentication was faild - if (responseCode != 235) - { - emit smtpError(AuthenticationFailedError); - return false; - } - } - else if (method == AuthLogin) - { - // Sending command: AUTH LOGIN - sendMessage("AUTH LOGIN"); - - // Wait for 334 response code - waitForResponse(); - if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; } - - // Send the username in base64 - sendMessage(QByteArray().append(user).toBase64()); - - // Wait for 334 - waitForResponse(); - if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; } - - // Send the password in base64 - sendMessage(QByteArray().append(password).toBase64()); - - // Wait for the server's responce - waitForResponse(); - - // If the response is not 235 then the authentication was faild - if (responseCode != 235) - { - emit smtpError(AuthenticationFailedError); - return false; - } - } - } - catch (ResponseTimeoutException e) - { - // Responce Timeout exceeded - emit smtpError(AuthenticationFailedError); - return false; - } - catch (SendMessageTimeoutException) - { - // Send Timeout exceeded - emit smtpError(AuthenticationFailedError); - return false; - } - - return true; -} - -bool SmtpClient::sendMail(MimeMessage& email) -{ - try - { - // Send the MAIL command with the sender - sendMessage("MAIL FROM: <" + email.getSender().getAddress() + ">"); - - waitForResponse(); - - if (responseCode != 250) return false; - - // Send RCPT command for each recipient - QList::const_iterator it, itEnd; - // To (primary recipients) - for (it = email.getRecipients().begin(), itEnd = email.getRecipients().end(); - it != itEnd; ++it) - { - sendMessage("RCPT TO: <" + (*it)->getAddress() + ">"); - waitForResponse(); - - if (responseCode != 250) return false; - } - - // Cc (carbon copy) - for (it = email.getRecipients(MimeMessage::Cc).begin(), itEnd = email.getRecipients(MimeMessage::Cc).end(); - it != itEnd; ++it) - { - sendMessage("RCPT TO: <" + (*it)->getAddress() + ">"); - waitForResponse(); - - if (responseCode != 250) return false; - } - - // Bcc (blind carbon copy) - for (it = email.getRecipients(MimeMessage::Bcc).begin(), itEnd = email.getRecipients(MimeMessage::Bcc).end(); - it != itEnd; ++it) - { - sendMessage("RCPT TO: <" + (*it)->getAddress() + ">"); - waitForResponse(); - - if (responseCode != 250) return false; - } - - // Send DATA command - sendMessage("DATA"); - waitForResponse(); - - if (responseCode != 354) return false; - - sendMessage(email.toString()); - - // Send \r\n.\r\n to end the mail data - sendMessage("."); - - waitForResponse(); - - if (responseCode != 250) return false; - } - catch (ResponseTimeoutException) - { - return false; - } - catch (SendMessageTimeoutException) - { - return false; - } - - return true; -} - -void SmtpClient::quit() -{ - sendMessage("QUIT"); -} - -/* [3] --- */ - - -/* [4] Protected methods */ - -void SmtpClient::waitForResponse() throw (ResponseTimeoutException) -{ - do { - if (!socket->waitForReadyRead(responseTimeout)) - { - emit smtpError(ResponseTimeoutError); - throw ResponseTimeoutException(); - } - - while (socket->canReadLine()) { - // Save the server's response - responseText = socket->readLine(); - - // Extract the respose code from the server's responce (first 3 digits) - responseCode = responseText.left(3).toInt(); - - if (responseCode / 100 == 4) - emit smtpError(ServerError); - - if (responseCode / 100 == 5) - emit smtpError(ClientError); - - if (responseText[3] == ' ') { return; } - } - } while (true); -} - -void SmtpClient::sendMessage(const QString &text) throw (SendMessageTimeoutException) -{ - socket->write(text.toUtf8() + "\r\n"); - if (! socket->waitForBytesWritten(sendMessageTimeout)) - { - emit smtpError(SendDataTimeoutError); - throw SendMessageTimeoutException(); - } -} - -/* [4] --- */ - - -/* [5] Slots for the socket's signals */ - -void SmtpClient::socketStateChanged(QAbstractSocket::SocketState /* state */) -{ -} - -void SmtpClient::socketError(QAbstractSocket::SocketError /* socketError */) -{ -} - -void SmtpClient::socketReadyRead() -{ -} - -/* [5] --- */ - - - - diff --git a/servatrice/src/smtp/smtpclient.h b/servatrice/src/smtp/smtpclient.h deleted file mode 100644 index f734aad6..00000000 --- a/servatrice/src/smtp/smtpclient.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - Copyright (c) 2011-2012 - Tőkés Attila - - This file is part of SmtpClient for Qt. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - See the LICENSE file for more details. -*/ - -#ifndef SMTPCLIENT_H -#define SMTPCLIENT_H - -#include -#include - -#include "mimemessage.h" -#include "smtpexports.h" - -class SMTP_EXPORT SmtpClient : public QObject -{ - Q_OBJECT -public: - - /* [0] Enumerations */ - - enum AuthMethod - { - AuthPlain, - AuthLogin - }; - - enum SmtpError - { - ConnectionTimeoutError, - ResponseTimeoutError, - SendDataTimeoutError, - AuthenticationFailedError, - ServerError, // 4xx smtp error - ClientError // 5xx smtp error - }; - - enum ConnectionType - { - TcpConnection, - SslConnection, - TlsConnection // STARTTLS - }; - - /* [0] --- */ - - - /* [1] Constructors and Destructors */ - - SmtpClient(const QString & host = "localhost", int port = 25, ConnectionType ct = TcpConnection); - - ~SmtpClient(); - - /* [1] --- */ - - - /* [2] Getters and Setters */ - - const QString& getHost() const; - void setHost(const QString &host); - - int getPort() const; - void setPort(int port); - - const QString& getName() const; - void setName(const QString &name); - - ConnectionType getConnectionType() const; - void setConnectionType(ConnectionType ct); - - const QString & getUser() const; - void setUser(const QString &user); - - const QString & getPassword() const; - void setPassword(const QString &password); - - SmtpClient::AuthMethod getAuthMethod() const; - void setAuthMethod(AuthMethod method); - - const QString & getResponseText() const; - int getResponseCode() const; - - int getConnectionTimeout() const; - void setConnectionTimeout(int msec); - - int getResponseTimeout() const; - void setResponseTimeout(int msec); - - int getSendMessageTimeout() const; - void setSendMessageTimeout(int msec); - - QTcpSocket* getSocket(); - - - /* [2] --- */ - - - /* [3] Public methods */ - - bool connectToHost(); - - bool login(); - bool login(const QString &user, const QString &password, AuthMethod method = AuthLogin); - - bool sendMail(MimeMessage& email); - - void quit(); - - - /* [3] --- */ - -protected: - - /* [4] Protected members */ - - QTcpSocket *socket; - - QString host; - int port; - ConnectionType connectionType; - QString name; - - QString user; - QString password; - AuthMethod authMethod; - - int connectionTimeout; - int responseTimeout; - int sendMessageTimeout; - - - QString responseText; - int responseCode; - - - class ResponseTimeoutException {}; - class SendMessageTimeoutException {}; - - /* [4] --- */ - - - /* [5] Protected methods */ - - void waitForResponse() throw (ResponseTimeoutException); - - void sendMessage(const QString &text) throw (SendMessageTimeoutException); - - /* [5] --- */ - -protected slots: - - /* [6] Protected slots */ - - void socketStateChanged(QAbstractSocket::SocketState state); - void socketError(QAbstractSocket::SocketError error); - void socketReadyRead(); - - /* [6] --- */ - - -signals: - - /* [7] Signals */ - - void smtpError(SmtpClient::SmtpError e); - - /* [7] --- */ - -}; - -#endif // SMTPCLIENT_H diff --git a/servatrice/src/smtp/smtpexports.h b/servatrice/src/smtp/smtpexports.h deleted file mode 100644 index c9234be5..00000000 --- a/servatrice/src/smtp/smtpexports.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef SMTPEXPORTS_H -#define SMTPEXPORTS_H - -/* -#ifdef SMTP_BUILD -#define SMTP_EXPORT Q_DECL_EXPORT -#else -#define SMTP_EXPORT Q_DECL_IMPORT -#endif -*/ - -// Servatrice compiles this statically in, so there's no need to declare exports -#define SMTP_EXPORT - -#endif // SMTPEXPORTS_H diff --git a/servatrice/src/smtpclient.cpp b/servatrice/src/smtpclient.cpp new file mode 100644 index 00000000..aa2f9198 --- /dev/null +++ b/servatrice/src/smtpclient.cpp @@ -0,0 +1,175 @@ +#include "smtpclient.h" +#include "settingscache.h" +#include "smtp/qxtsmtp.h" + +#include +#include + +SmtpClient::SmtpClient(QObject *parent) +: QObject(parent) +{ + smtp = new QxtSmtp(this); + + connect(smtp, SIGNAL(authenticated()), this, SLOT(authenticated())); + connect(smtp, SIGNAL(authenticationFailed(const QByteArray &)), this, SLOT(authenticationFailed(const QByteArray &))); + connect(smtp, SIGNAL(connected()), this, SLOT(connected())); + connect(smtp, SIGNAL(connectionFailed(const QByteArray &)), this, SLOT(connectionFailed(const QByteArray &))); + connect(smtp, SIGNAL(disconnected()), this, SLOT(disconnected())); + connect(smtp, SIGNAL(encrypted()), this, SLOT(encrypted())); + connect(smtp, SIGNAL(encryptionFailed(const QByteArray &)), this, SLOT(encryptionFailed(const QByteArray &))); + connect(smtp, SIGNAL(finished()), this, SLOT(finished())); + connect(smtp, SIGNAL(mailFailed(int, int, const QByteArray &)), this, SLOT(mailFailed(int, int, const QByteArray &))); + connect(smtp, SIGNAL(mailSent(int)), this, SLOT(mailSent(int))); + connect(smtp, SIGNAL(recipientRejected(int, const QString &, const QByteArray &)), this, SLOT(recipientRejected(int, const QString &, const QByteArray &))); + connect(smtp, SIGNAL(senderRejected(int, const QString &, const QByteArray &)), this, SLOT(senderRejected(int, const QString &, const QByteArray &))); +} + +SmtpClient::~SmtpClient() +{ + if(smtp) + { + delete smtp; + smtp = 0; + } + +} + +bool SmtpClient::enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token) +{ + QString email = settingsCache->value("smtp/email", "").toString(); + QString name = settingsCache->value("smtp/name", "").toString(); + QString subject = settingsCache->value("smtp/subject", "").toString(); + QString body = settingsCache->value("smtp/body", "").toString(); + + if(email.isEmpty()) + { + qDebug() << "[MAIL] Missing sender email in configuration"; + return false; + } + + if(subject.isEmpty()) + { + qDebug() << "[MAIL] Missing subject field in configuration"; + return false; + } + + if(body.isEmpty()) + { + qDebug() << "[MAIL] Missing body field in configuration"; + return false; + } + + if(recipient.isEmpty()) + { + qDebug() << "[MAIL] Missing recipient field for user " << nickname; + return false; + } + + if(token.isEmpty()) + { + qDebug() << "[MAIL] Missing token field for user " << nickname; + return false; + } + + QxtMailMessage message; + message.setSender(name + " <" + email + ">"); + message.addRecipient(recipient); + message.setSubject(subject); + message.setBody(body.replace("%username", nickname).replace("%token", token)); + + int id = smtp->send(message); + qDebug() << "[MAIL] Enqueued mail to" << recipient << "as" << id; + return true; +} + +void SmtpClient::sendAllEmails() +{ + // still connected from the previous round + if(smtp->socket()->state() == QAbstractSocket::ConnectedState) + return; + + if(smtp->pendingMessages() == 0) + return; + + QString connectionType = settingsCache->value("smtp/connection", "tcp").toString(); + QString host = settingsCache->value("smtp/host", "localhost").toString(); + int port = settingsCache->value("smtp/port", 25).toInt(); + QByteArray username = settingsCache->value("smtp/username", "").toByteArray(); + QByteArray password = settingsCache->value("smtp/password", "").toByteArray(); + bool acceptAllCerts = settingsCache->value("smtp/acceptallcerts", false).toBool(); + + smtp->setUsername(username); + smtp->setPassword(password); + + // Connect + if(connectionType == "ssl") + { + if(acceptAllCerts) + smtp->sslSocket()->setPeerVerifyMode(QSslSocket::QueryPeer); + smtp->connectToSecureHost(host, port); + } else { + smtp->connectToHost(host, port); + } +} + +void SmtpClient::authenticated() +{ + qDebug() << "[MAIL] authenticated"; +} + +void SmtpClient::authenticationFailed(const QByteArray & msg) +{ + qDebug() << "[MAIL] authenticationFailed" << QString(msg); +} + +void SmtpClient::connected() +{ + qDebug() << "[MAIL] connected"; +} + +void SmtpClient::connectionFailed(const QByteArray & msg) +{ + qDebug() << "[MAIL] connectionFailed" << QString(msg); +} + +void SmtpClient::disconnected() +{ + qDebug() << "[MAIL] disconnected"; +} + +void SmtpClient::encrypted() +{ + qDebug() << "[MAIL] encrypted"; +} + +void SmtpClient::encryptionFailed(const QByteArray & msg) +{ + qDebug() << "[MAIL] encryptionFailed" << QString(msg); + qDebug() << "[MAIL] Try enabling the \"acceptallcerts\" option in servatrice.ini"; +} + +void SmtpClient::finished() +{ + qDebug() << "[MAIL] finished"; + smtp->disconnectFromHost(); +} + +void SmtpClient::mailFailed(int mailID, int errorCode, const QByteArray & msg) +{ + qDebug() << "[MAIL] mailFailed id=" << mailID << " errorCode=" << errorCode << "msg=" << QString(msg); +} + +void SmtpClient::mailSent(int mailID) +{ + qDebug() << "[MAIL] mailSent" << mailID; +} + +void SmtpClient::recipientRejected(int mailID, const QString & address, const QByteArray & msg) +{ + qDebug() << "[MAIL] recipientRejected id=" << mailID << " address=" << address << "msg=" << QString(msg); +} + +void SmtpClient::senderRejected(int mailID, const QString & address, const QByteArray & msg) +{ + qDebug() << "[MAIL] senderRejected id=" << mailID << " address=" << address << "msg=" << QString(msg); +} diff --git a/servatrice/src/smtpclient.h b/servatrice/src/smtpclient.h new file mode 100644 index 00000000..b2b1ad00 --- /dev/null +++ b/servatrice/src/smtpclient.h @@ -0,0 +1,34 @@ +#ifndef SMTPCLIENT_H +#define SMTPCLIENT_H + +#include + +class QxtSmtp; +class QxtMailMessage; + +class SmtpClient : public QObject { + Q_OBJECT +public: + SmtpClient(QObject *parent = 0); + ~SmtpClient(); +protected: + QxtSmtp *smtp; +public slots: + bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); + void sendAllEmails(); +protected slots: + void authenticated(); + void authenticationFailed(const QByteArray & msg); + void connected(); + void connectionFailed(const QByteArray & msg); + void disconnected(); + void encrypted(); + void encryptionFailed(const QByteArray & msg); + void finished(); + void mailFailed(int mailID, int errorCode, const QByteArray & msg); + void mailSent(int mailID); + void recipientRejected(int mailID, const QString & address, const QByteArray & msg); + void senderRejected(int mailID, const QString & address, const QByteArray & msg); +}; + +#endif \ No newline at end of file diff --git a/travis-compile.sh b/travis-compile.sh index 8ccb02ed..65f48b2c 100755 --- a/travis-compile.sh +++ b/travis-compile.sh @@ -6,7 +6,7 @@ mkdir build cd build prefix="" if [[ $TRAVIS_OS_NAME == "osx" && $QT4 == 0 ]]; then - prefix="-DCMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.4.2/" + prefix="-DCMAKE_PREFIX_PATH=`echo /usr/local/Cellar/qt5/5.*/`" fi if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then prefix="-DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake/"