Merge remote-tracking branch 'origin/master' into editor-sorting-v5
This commit is contained in:
commit
e99c1bbe6d
88 changed files with 4775 additions and 2615 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
|||
tags
|
||||
build
|
||||
build*
|
||||
*.qm
|
||||
.directory
|
||||
|
|
116
CMakeLists.txt
116
CMakeLists.txt
|
@ -1,17 +1,107 @@
|
|||
# Cockatrice's main CMakeLists.txt
|
||||
#
|
||||
# This is basically a wrapper to enable/disable the compilation
|
||||
# of the different projects: servatrice, cockatrice, test
|
||||
# This file sets all the variables shared between the projects
|
||||
# like the installation path, compilation flags etc..
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
set(PROJECT_NAME "Cockatrice")
|
||||
set(PROJECT_VERSION_MAJOR 0)
|
||||
set(PROJECT_VERSION_MINOR 0)
|
||||
set(PROJECT_VERSION_PATCH 1)
|
||||
set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} )
|
||||
|
||||
# Defualt to "Release" build type
|
||||
# User-provided value for CMAKE_BUILD_TYPE must be checked before the PROJECT() call
|
||||
IF(DEFINED CMAKE_BUILD_TYPE)
|
||||
SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Type of build")
|
||||
ELSE()
|
||||
SET(CMAKE_BUILD_TYPE Release CACHE STRING "Type of build")
|
||||
ENDIF()
|
||||
|
||||
# A project name is needed for CPack
|
||||
PROJECT("${PROJECT_NAME}")
|
||||
|
||||
# Set conventional loops
|
||||
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true)
|
||||
|
||||
# Define a proper install path
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
# MacOS X
|
||||
# Due to the special bundle structure ignore
|
||||
# the prefix eventually set by the user.
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/release)
|
||||
else()
|
||||
# Linux / BSD
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
#fix package build
|
||||
if(PREFIX)
|
||||
set(CMAKE_INSTALL_PREFIX ${PREFIX})
|
||||
else()
|
||||
set(CMAKE_INSTALL_PREFIX /usr/local)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/release)
|
||||
endif()
|
||||
|
||||
# Define proper compilation flags
|
||||
IF(MSVC)
|
||||
# Visual Studio:
|
||||
# Maximum optimization
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox")
|
||||
# Generate complete debugging information
|
||||
#set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
|
||||
ELSEIF (CMAKE_COMPILER_IS_GNUCXX)
|
||||
# linux/gcc, bsd/gcc, windows/mingw
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
|
||||
ELSE()
|
||||
# other: osx/llvm, bsd/llvm
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
|
||||
ENDIF()
|
||||
|
||||
# GNU systems need to define the Mersenne exponent for the RNG to compile w/o warning
|
||||
IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
ADD_DEFINITIONS("-DSFMT_MEXP=19937")
|
||||
ENDIF()
|
||||
|
||||
#Find Qt4 and enable the needed features
|
||||
FIND_PACKAGE(Qt4 REQUIRED)
|
||||
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
# Find other needed libraries
|
||||
FIND_PACKAGE(Protobuf REQUIRED)
|
||||
|
||||
# Compile servatrice (default off)
|
||||
option(WITH_SERVER "build servatrice" OFF)
|
||||
add_subdirectory(common)
|
||||
if(WITH_SERVER)
|
||||
add_subdirectory(servatrice)
|
||||
endif(WITH_SERVER)
|
||||
if (NOT WITHOUT_CLIENT)
|
||||
add_subdirectory(cockatrice)
|
||||
add_subdirectory(oracle)
|
||||
endif(NOT WITHOUT_CLIENT)
|
||||
if (WITH_TESTCLIENT)
|
||||
add_subdirectory(testclient)
|
||||
endif(WITH_TESTCLIENT)
|
||||
add_subdirectory(servatrice)
|
||||
endif()
|
||||
|
||||
FILE(GLOB sounds "${CMAKE_CURRENT_SOURCE_DIR}/sounds/*.raw")
|
||||
INSTALL(FILES ${sounds} DESTINATION share/cockatrice/sounds)
|
||||
FILE(GLOB zonebg "${CMAKE_CURRENT_SOURCE_DIR}/zonebg/*.*")
|
||||
INSTALL(FILES ${zonebg} DESTINATION share/cockatrice/zonebg)
|
||||
# Compile cockatrice (default on)
|
||||
option(WITH_CLIENT "build cockatrice" ON)
|
||||
if(WITH_CLIENT)
|
||||
add_subdirectory(cockatrice)
|
||||
add_subdirectory(sounds)
|
||||
add_subdirectory(zonebg)
|
||||
endif()
|
||||
|
||||
# Compile oracle (default on)
|
||||
option(WITH_ORACLE "build oracle" ON)
|
||||
if(WITH_ORACLE)
|
||||
add_subdirectory(oracle)
|
||||
endif()
|
||||
|
||||
# Compile testclient (default off)
|
||||
option(WITH_TESTCLIENT "build testclient" OFF)
|
||||
if (WITH_TESTCLIENT)
|
||||
add_subdirectory(testclient)
|
||||
endif()
|
|
@ -1,216 +1,149 @@
|
|||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# CMakeLists for cockatrice directory
|
||||
#
|
||||
# provides the cockatrice binary
|
||||
|
||||
PROJECT(cockatrice)
|
||||
|
||||
SET(cockatrice_SOURCES
|
||||
src/abstractcounter.cpp
|
||||
src/counter_general.cpp
|
||||
src/dlg_creategame.cpp
|
||||
src/dlg_filter_games.cpp
|
||||
src/dlg_connect.cpp
|
||||
src/dlg_create_token.cpp
|
||||
src/dlg_edit_tokens.cpp
|
||||
src/abstractclient.cpp
|
||||
src/remoteclient.cpp
|
||||
src/main.cpp
|
||||
src/window_main.cpp
|
||||
src/gamesmodel.cpp
|
||||
src/player.cpp
|
||||
src/playertarget.cpp
|
||||
src/cardzone.cpp
|
||||
src/selectzone.cpp
|
||||
src/cardlist.cpp
|
||||
src/abstractcarditem.cpp
|
||||
src/carditem.cpp
|
||||
src/tablezone.cpp
|
||||
src/handzone.cpp
|
||||
src/handcounter.cpp
|
||||
src/carddatabase.cpp
|
||||
src/gameview.cpp
|
||||
src/gameselector.cpp
|
||||
src/decklistmodel.cpp
|
||||
src/deck_loader.cpp
|
||||
src/dlg_load_deck_from_clipboard.cpp
|
||||
src/dlg_load_remote_deck.cpp
|
||||
src/cardinfowidget.cpp
|
||||
src/messagelogwidget.cpp
|
||||
src/zoneviewzone.cpp
|
||||
src/zoneviewwidget.cpp
|
||||
src/pilezone.cpp
|
||||
src/stackzone.cpp
|
||||
src/carddragitem.cpp
|
||||
src/carddatabasemodel.cpp
|
||||
src/setsmodel.cpp
|
||||
src/window_sets.cpp
|
||||
src/abstractgraphicsitem.cpp
|
||||
src/abstractcarddragitem.cpp
|
||||
src/dlg_settings.cpp
|
||||
src/dlg_cardsearch.cpp
|
||||
src/phasestoolbar.cpp
|
||||
src/gamescene.cpp
|
||||
src/arrowitem.cpp
|
||||
src/arrowtarget.cpp
|
||||
src/tab.cpp
|
||||
src/tab_server.cpp
|
||||
src/tab_room.cpp
|
||||
src/tab_message.cpp
|
||||
src/tab_game.cpp
|
||||
src/tab_deck_storage.cpp
|
||||
src/tab_replays.cpp
|
||||
src/tab_supervisor.cpp
|
||||
src/tab_admin.cpp
|
||||
src/tab_userlists.cpp
|
||||
src/tab_deck_editor.cpp
|
||||
src/replay_timeline_widget.cpp
|
||||
src/deckstats_interface.cpp
|
||||
src/chatview.cpp
|
||||
src/userlist.cpp
|
||||
src/userinfobox.cpp
|
||||
src/user_context_menu.cpp
|
||||
src/remotedecklist_treewidget.cpp
|
||||
src/remotereplaylist_treewidget.cpp
|
||||
src/deckview.cpp
|
||||
src/playerlistwidget.cpp
|
||||
src/pixmapgenerator.cpp
|
||||
src/settingscache.cpp
|
||||
src/localserver.cpp
|
||||
src/localserverinterface.cpp
|
||||
src/localclient.cpp
|
||||
src/priceupdater.cpp
|
||||
src/qt-json/json.cpp
|
||||
src/soundengine.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp
|
||||
)
|
||||
SET(cockatrice_HEADERS
|
||||
src/abstractcounter.h
|
||||
src/counter_general.h
|
||||
src/dlg_creategame.h
|
||||
src/dlg_filter_games.h
|
||||
src/dlg_connect.h
|
||||
src/dlg_create_token.h
|
||||
src/dlg_edit_tokens.h
|
||||
src/gamesmodel.h
|
||||
src/abstractclient.h
|
||||
src/remoteclient.h
|
||||
src/window_main.h
|
||||
src/cardzone.h
|
||||
src/selectzone.h
|
||||
src/player.h
|
||||
src/playertarget.h
|
||||
src/abstractcarditem.h
|
||||
src/carditem.h
|
||||
src/tablezone.h
|
||||
src/handzone.h
|
||||
src/handcounter.h
|
||||
src/carddatabase.h
|
||||
src/gameview.h
|
||||
src/gameselector.h
|
||||
src/decklistmodel.h
|
||||
src/deck_loader.h
|
||||
src/dlg_load_deck_from_clipboard.h
|
||||
src/dlg_load_remote_deck.h
|
||||
src/cardinfowidget.h
|
||||
src/messagelogwidget.h
|
||||
src/zoneviewzone.h
|
||||
src/zoneviewwidget.h
|
||||
src/pilezone.h
|
||||
src/stackzone.h
|
||||
src/carddragitem.h
|
||||
src/carddatabasemodel.h
|
||||
src/setsmodel.h
|
||||
src/window_sets.h
|
||||
src/abstractgraphicsitem.h
|
||||
src/abstractcarddragitem.h
|
||||
src/dlg_settings.h
|
||||
src/dlg_cardsearch.h
|
||||
src/phasestoolbar.h
|
||||
src/gamescene.h
|
||||
src/arrowitem.h
|
||||
src/arrowtarget.h
|
||||
src/tab.h
|
||||
src/tab_server.h
|
||||
src/tab_room.h
|
||||
src/tab_message.h
|
||||
src/tab_game.h
|
||||
src/tab_deck_storage.h
|
||||
src/tab_replays.h
|
||||
src/tab_supervisor.h
|
||||
src/tab_admin.h
|
||||
src/tab_userlists.h
|
||||
src/tab_deck_editor.h
|
||||
src/replay_timeline_widget.h
|
||||
src/deckstats_interface.h
|
||||
src/chatview.h
|
||||
src/userlist.h
|
||||
src/userinfobox.h
|
||||
src/user_context_menu.h
|
||||
src/remotedecklist_treewidget.h
|
||||
src/remotereplaylist_treewidget.h
|
||||
src/deckview.h
|
||||
src/playerlistwidget.h
|
||||
src/settingscache.h
|
||||
src/localserver.h
|
||||
src/localserverinterface.h
|
||||
src/localclient.h
|
||||
src/priceupdater.h
|
||||
src/soundengine.h
|
||||
src/pending_command.h
|
||||
src/abstractcounter.cpp
|
||||
src/counter_general.cpp
|
||||
src/dlg_creategame.cpp
|
||||
src/dlg_filter_games.cpp
|
||||
src/dlg_connect.cpp
|
||||
src/dlg_create_token.cpp
|
||||
src/dlg_edit_tokens.cpp
|
||||
src/abstractclient.cpp
|
||||
src/remoteclient.cpp
|
||||
src/main.cpp
|
||||
src/window_main.cpp
|
||||
src/gamesmodel.cpp
|
||||
src/player.cpp
|
||||
src/playertarget.cpp
|
||||
src/cardzone.cpp
|
||||
src/selectzone.cpp
|
||||
src/cardlist.cpp
|
||||
src/abstractcarditem.cpp
|
||||
src/carditem.cpp
|
||||
src/tablezone.cpp
|
||||
src/handzone.cpp
|
||||
src/handcounter.cpp
|
||||
src/carddatabase.cpp
|
||||
src/keysignals.cpp
|
||||
src/gameview.cpp
|
||||
src/gameselector.cpp
|
||||
src/decklistmodel.cpp
|
||||
src/deck_loader.cpp
|
||||
src/dlg_load_deck_from_clipboard.cpp
|
||||
src/dlg_load_remote_deck.cpp
|
||||
src/cardinfowidget.cpp
|
||||
src/cardframe.cpp
|
||||
src/cardinfopicture.cpp
|
||||
src/cardinfotext.cpp
|
||||
src/filterbuilder.cpp
|
||||
src/cardfilter.cpp
|
||||
src/filtertreemodel.cpp
|
||||
src/filtertree.cpp
|
||||
src/messagelogwidget.cpp
|
||||
src/zoneviewzone.cpp
|
||||
src/zoneviewwidget.cpp
|
||||
src/pilezone.cpp
|
||||
src/stackzone.cpp
|
||||
src/carddragitem.cpp
|
||||
src/carddatabasemodel.cpp
|
||||
src/setsmodel.cpp
|
||||
src/window_sets.cpp
|
||||
src/abstractgraphicsitem.cpp
|
||||
src/abstractcarddragitem.cpp
|
||||
src/dlg_settings.cpp
|
||||
src/dlg_cardsearch.cpp
|
||||
src/phasestoolbar.cpp
|
||||
src/gamescene.cpp
|
||||
src/arrowitem.cpp
|
||||
src/arrowtarget.cpp
|
||||
src/tab.cpp
|
||||
src/tab_server.cpp
|
||||
src/tab_room.cpp
|
||||
src/tab_message.cpp
|
||||
src/tab_game.cpp
|
||||
src/tab_deck_storage.cpp
|
||||
src/tab_replays.cpp
|
||||
src/tab_supervisor.cpp
|
||||
src/tab_admin.cpp
|
||||
src/tab_userlists.cpp
|
||||
src/tab_deck_editor.cpp
|
||||
src/replay_timeline_widget.cpp
|
||||
src/deckstats_interface.cpp
|
||||
src/chatview.cpp
|
||||
src/userlist.cpp
|
||||
src/userinfobox.cpp
|
||||
src/user_context_menu.cpp
|
||||
src/remotedecklist_treewidget.cpp
|
||||
src/remotereplaylist_treewidget.cpp
|
||||
src/deckview.cpp
|
||||
src/playerlistwidget.cpp
|
||||
src/pixmapgenerator.cpp
|
||||
src/settingscache.cpp
|
||||
src/localserver.cpp
|
||||
src/localserverinterface.cpp
|
||||
src/localclient.cpp
|
||||
src/priceupdater.cpp
|
||||
src/qt-json/json.cpp
|
||||
src/soundengine.cpp
|
||||
src/pending_command.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp
|
||||
)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
set_source_files_properties(src/main.cpp PROPERTIES COMPILE_FLAGS -DTRANSLATION_PATH=\\"${CMAKE_INSTALL_PREFIX}/share/cockatrice/translations\\")
|
||||
set_source_files_properties(src/main.cpp PROPERTIES COMPILE_FLAGS -DTRANSLATION_PATH=\\"${CMAKE_INSTALL_PREFIX}/share/cockatrice/translations\\")
|
||||
endif (UNIX AND NOT APPLE)
|
||||
|
||||
set(cockatrice_RESOURCES cockatrice.qrc)
|
||||
set(cockatrice_TS
|
||||
# translations/cockatrice_cs.ts
|
||||
translations/cockatrice_de.ts
|
||||
translations/cockatrice_en.ts
|
||||
translations/cockatrice_es.ts
|
||||
# translations/cockatrice_fr.ts
|
||||
translations/cockatrice_it.ts
|
||||
translations/cockatrice_ja.ts
|
||||
# translations/cockatrice_pl.ts
|
||||
# translations/cockatrice_pt-br.ts
|
||||
translations/cockatrice_pt.ts
|
||||
# translations/cockatrice_ru.ts
|
||||
# translations/cockatrice_sk.ts
|
||||
translations/cockatrice_sv.ts
|
||||
# translations/cockatrice_zh_CN.ts
|
||||
# translations/cockatrice_cs.ts
|
||||
translations/cockatrice_de.ts
|
||||
translations/cockatrice_en.ts
|
||||
translations/cockatrice_es.ts
|
||||
# translations/cockatrice_fr.ts
|
||||
translations/cockatrice_it.ts
|
||||
translations/cockatrice_ja.ts
|
||||
# translations/cockatrice_pl.ts
|
||||
# translations/cockatrice_pt-br.ts
|
||||
translations/cockatrice_pt.ts
|
||||
# translations/cockatrice_ru.ts
|
||||
# translations/cockatrice_sk.ts
|
||||
translations/cockatrice_sv.ts
|
||||
# translations/cockatrice_zh_CN.ts
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
|
||||
set(cockatrice_SOURCES ${cockatrice_SOURCES} cockatrice.rc)
|
||||
endif(WIN32)
|
||||
|
||||
if(APPLE)
|
||||
set(MACOSX_BUNDLE_ICON_FILE appicon.icns)
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE appicon.icns)
|
||||
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set(cockatrice_SOURCES ${cockatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns)
|
||||
ENDIF(APPLE)
|
||||
|
||||
if (NOT QT_QTMULTIMEDIA_FOUND)
|
||||
FIND_PACKAGE(QtMobility REQUIRED)
|
||||
endif (NOT QT_QTMULTIMEDIA_FOUND)
|
||||
|
||||
SET(QT_USE_QTNETWORK TRUE)
|
||||
SET(QT_USE_QTMULTIMEDIA TRUE)
|
||||
SET(QT_USE_QTXML TRUE)
|
||||
SET(QT_USE_QTSVG TRUE)
|
||||
FIND_PACKAGE(Qt4 REQUIRED)
|
||||
if (NOT QT_QTMULTIMEDIA_FOUND)
|
||||
FIND_PACKAGE(QtMobility REQUIRED)
|
||||
endif (NOT QT_QTMULTIMEDIA_FOUND)
|
||||
FIND_PACKAGE(Protobuf REQUIRED)
|
||||
FIND_PACKAGE(Threads)
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
|
||||
|
||||
# paths
|
||||
# Declare path variables
|
||||
set(ICONDIR share/icons CACHE STRING "icon dir")
|
||||
set(DESKTOPDIR share/applications CACHE STRING "desktop file destination")
|
||||
|
||||
|
||||
QT4_WRAP_CPP(cockatrice_HEADERS_MOC ${cockatrice_HEADERS})
|
||||
# Let cmake chew Qt4's translations and resource files
|
||||
# Note: header files are MOC-ed automatically by cmake
|
||||
QT4_ADD_TRANSLATION(cockatrice_QM ${cockatrice_TS})
|
||||
QT4_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
||||
|
||||
# Include directories
|
||||
INCLUDE(${QT_USE_FILE})
|
||||
INCLUDE_DIRECTORIES(../common)
|
||||
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
|
||||
|
@ -219,23 +152,61 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
|
|||
INCLUDE_DIRECTORIES(${QT_MOBILITY_INCLUDE_DIR})
|
||||
INCLUDE_DIRECTORIES(${QT_MOBILITY_MULTIMEDIAKIT_INCLUDE_DIR})
|
||||
|
||||
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_HEADERS_MOC})
|
||||
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common ${QT_LIBRARIES} ${QT_MOBILITY_MULTIMEDIAKIT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||
# Build cockatrice binary and link it
|
||||
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_MOC_SRCS})
|
||||
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common ${QT_QTMAIN_LIBRARY} ${QT_LIBRARIES} ${QT_MOBILITY_MULTIMEDIAKIT_LIBRARY})
|
||||
|
||||
IF (NOT APPLE)
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/cockatrice DESTINATION bin)
|
||||
ELSE (APPLE)
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/cockatrice.app DESTINATION bin)
|
||||
ENDIF (NOT APPLE)
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR})
|
||||
INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations)
|
||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
||||
if(MSVC)
|
||||
set_target_properties(cockatrice PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
|
||||
endif(MSVC)
|
||||
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
INSTALL(TARGETS cockatrice BUNDLE DESTINATION ./)
|
||||
INSTALL(FILES ${cockatrice_QM} DESTINATION ./cockatrice.app/Contents/Resources/translations)
|
||||
else()
|
||||
# Assume linux
|
||||
INSTALL(TARGETS cockatrice RUNTIME DESTINATION bin/)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR})
|
||||
INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
INSTALL(TARGETS cockatrice RUNTIME DESTINATION ./)
|
||||
INSTALL(FILES ${cockatrice_QM} DESTINATION ./translations)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp ${CMAKE_CURRENT_BINARY_DIR}/version_string.h
|
||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/../common/getversion.cmake
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp ${CMAKE_CURRENT_BINARY_DIR}/version_string.h
|
||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/../common/getversion.cmake
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set(plugin_dest_dir ./cockatrice.app/Contents/Plugins)
|
||||
set(qtconf_dest_dir ./cockatrice.app/Contents/Resources)
|
||||
|
||||
# note: no codecs in qt5
|
||||
# note: phonon_backend => mediaservice
|
||||
# note: needs platform on osx
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(codecs|iconengines|imageformats|mediaservice|phonon_backend|platforms)/.*_debug\\.dylib")
|
||||
else()
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(codecs|iconengines|imageformats|mediaservice|phonon_backend|platforms)/[^_]*\\.dylib")
|
||||
endif()
|
||||
|
||||
install(CODE "
|
||||
file(WRITE \"${qtconf_dest_dir}/qt.conf\" \"[Paths]
|
||||
Plugins = Plugins\")
|
||||
" COMPONENT Runtime)
|
||||
|
||||
install(CODE "
|
||||
file(GLOB_RECURSE QTPLUGINS
|
||||
\"${plugin_dest_dir}/*.dylib\")
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/cockatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
|
||||
" COMPONENT Runtime)
|
||||
endif()
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
#include <QGraphicsScene>
|
||||
#include <QCursor>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
#include "carddatabase.h"
|
||||
#include "cardinfowidget.h"
|
||||
#include "abstractcarditem.h"
|
||||
|
@ -143,7 +146,7 @@ void AbstractCardItem::paintPicture(QPainter *painter, const QSizeF &translatedS
|
|||
painter->restore();
|
||||
}
|
||||
|
||||
void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void AbstractCardItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
painter->save();
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
|
||||
#include "arrowitem.h"
|
||||
#include "playertarget.h"
|
||||
#include "carditem.h"
|
||||
#include "cardzone.h"
|
||||
#include "player.h"
|
||||
#include "math.h"
|
||||
#include <QPainter>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QGraphicsScene>
|
||||
|
@ -64,7 +66,7 @@ void ArrowItem::updatePath(const QPointF &endPoint)
|
|||
{
|
||||
const double arrowWidth = 15.0;
|
||||
const double headWidth = 40.0;
|
||||
const double headLength = headWidth / sqrt(2);
|
||||
const double headLength = headWidth / pow(2, 0.5); // aka headWidth / sqrt (2) but this produces a compile error with MSVC++
|
||||
const double phi = 15;
|
||||
|
||||
if (!startItem)
|
||||
|
|
|
@ -80,11 +80,13 @@ bool PictureToLoad::nextSet()
|
|||
return true;
|
||||
}
|
||||
|
||||
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent)
|
||||
: QObject(parent), _picsPath(__picsPath), picDownload(_picDownload), downloadRunning(false), loadQueueRunning(false)
|
||||
PictureLoader::PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent)
|
||||
: QObject(parent),
|
||||
_picsPath(__picsPath), picDownload(_picDownload), picDownloadHq(_picDownloadHq),
|
||||
downloadRunning(false), loadQueueRunning(false)
|
||||
{
|
||||
connect(this, SIGNAL(startLoadQueue()), this, SLOT(processLoadQueue()), Qt::QueuedConnection);
|
||||
|
||||
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
connect(networkManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(picDownloadFinished(QNetworkReply *)));
|
||||
}
|
||||
|
@ -99,7 +101,7 @@ void PictureLoader::processLoadQueue()
|
|||
{
|
||||
if (loadQueueRunning)
|
||||
return;
|
||||
|
||||
|
||||
loadQueueRunning = true;
|
||||
forever {
|
||||
mutex.lock();
|
||||
|
@ -113,7 +115,7 @@ void PictureLoader::processLoadQueue()
|
|||
QString correctedName = ptl.getCard()->getCorrectedName();
|
||||
QString picsPath = _picsPath;
|
||||
QString setName = ptl.getSetName();
|
||||
|
||||
|
||||
QImage image;
|
||||
if (!image.load(QString("%1/%2/%3.full.jpg").arg(picsPath).arg(setName).arg(correctedName)))
|
||||
if (!image.load(QString("%1/%2/%3%4.full.jpg").arg(picsPath).arg(setName).arg(correctedName).arg(1)))
|
||||
|
@ -135,6 +137,20 @@ void PictureLoader::processLoadQueue()
|
|||
}
|
||||
}
|
||||
|
||||
QString PictureLoader::getPicUrl(CardInfo *card)
|
||||
{
|
||||
if (!picDownload) return 0;
|
||||
|
||||
QString picUrl = picDownloadHq ? settingsCache->getPicUrlHq() : settingsCache->getPicUrl();
|
||||
picUrl.replace("!name!", QUrl::toPercentEncoding(card->getCorrectedName()));
|
||||
CardSet *set = card->getPreferredSet();
|
||||
picUrl.replace("!setcode!", QUrl::toPercentEncoding(set->getShortName()));
|
||||
picUrl.replace("!setname!", QUrl::toPercentEncoding(set->getLongName()));
|
||||
picUrl.replace("!cardid!", QUrl::toPercentEncoding(QString::number(card->getPreferredMuId())));
|
||||
|
||||
return picUrl;
|
||||
}
|
||||
|
||||
void PictureLoader::startNextPicDownload()
|
||||
{
|
||||
if (cardsToDownload.isEmpty()) {
|
||||
|
@ -142,23 +158,16 @@ void PictureLoader::startNextPicDownload()
|
|||
downloadRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
downloadRunning = true;
|
||||
|
||||
|
||||
cardBeingDownloaded = cardsToDownload.takeFirst();
|
||||
QString picUrl;
|
||||
if (cardBeingDownloaded.getStripped())
|
||||
picUrl = cardBeingDownloaded.getCard()->getPicURLSt(cardBeingDownloaded.getSetName());
|
||||
else if (cardBeingDownloaded.getHq()) {
|
||||
picUrl = cardBeingDownloaded.getCard()->getPicURLHq(cardBeingDownloaded.getSetName());
|
||||
if (picUrl.isEmpty()) {
|
||||
picUrl = cardBeingDownloaded.getCard()->getPicURL(cardBeingDownloaded.getSetName());
|
||||
cardBeingDownloaded.setHq(false);
|
||||
}
|
||||
} else
|
||||
picUrl = cardBeingDownloaded.getCard()->getPicURL(cardBeingDownloaded.getSetName());
|
||||
|
||||
// TODO: Do something useful when picUrl is 0 or empty, etc
|
||||
QString picUrl = getPicUrl(cardBeingDownloaded.getCard());
|
||||
|
||||
QUrl url(picUrl);
|
||||
|
||||
|
||||
QNetworkRequest req(url);
|
||||
qDebug() << "starting picture download:" << req.url();
|
||||
networkManager->get(req);
|
||||
|
@ -232,6 +241,12 @@ void PictureLoader::setPicDownload(bool _picDownload)
|
|||
picDownload = _picDownload;
|
||||
}
|
||||
|
||||
void PictureLoader::setPicDownloadHq(bool _picDownloadHq)
|
||||
{
|
||||
QMutexLocker locker(&mutex);
|
||||
picDownloadHq = _picDownloadHq;
|
||||
}
|
||||
|
||||
CardInfo::CardInfo(CardDatabase *_db,
|
||||
const QString &_name,
|
||||
bool _isToken,
|
||||
|
@ -244,22 +259,18 @@ CardInfo::CardInfo(CardDatabase *_db,
|
|||
bool _cipt,
|
||||
int _tableRow,
|
||||
const SetList &_sets,
|
||||
const QMap<QString, QString> &_picURLs,
|
||||
const QMap<QString, QString> &_picURLsHq,
|
||||
const QMap<QString, QString> &_picURLsSt)
|
||||
QMap<QString, int> _muIds)
|
||||
: db(_db),
|
||||
name(_name),
|
||||
isToken(_isToken),
|
||||
sets(_sets),
|
||||
muIds(_muIds),
|
||||
manacost(_manacost),
|
||||
cardtype(_cardtype),
|
||||
powtough(_powtough),
|
||||
text(_text),
|
||||
colors(_colors),
|
||||
loyalty(_loyalty),
|
||||
picURLs(_picURLs),
|
||||
picURLsHq(_picURLsHq),
|
||||
picURLsSt(_picURLsSt),
|
||||
cipt(_cipt),
|
||||
tableRow(_tableRow),
|
||||
pixmap(NULL)
|
||||
|
@ -284,6 +295,8 @@ QString CardInfo::getMainCardType() const
|
|||
int pos;
|
||||
if ((pos = result.indexOf('-')) != -1)
|
||||
result.remove(pos, result.length());
|
||||
if ((pos = result.indexOf("—")) != -1)
|
||||
result.remove(pos, result.length());
|
||||
if ((pos = result.indexOf("//")) != -1)
|
||||
result.remove(pos, result.length());
|
||||
result = result.simplified();
|
||||
|
@ -315,19 +328,12 @@ void CardInfo::addToSet(CardSet *set)
|
|||
sets << set;
|
||||
}
|
||||
|
||||
QString CardInfo::getPicURL() const
|
||||
{
|
||||
SetList sortedSets = sets;
|
||||
sortedSets.sortByKey();
|
||||
return picURLs.value(sortedSets.first()->getShortName());
|
||||
}
|
||||
|
||||
QPixmap *CardInfo::loadPixmap()
|
||||
{
|
||||
if (pixmap)
|
||||
return pixmap;
|
||||
pixmap = new QPixmap();
|
||||
|
||||
|
||||
if (getName().isEmpty()) {
|
||||
pixmap->load(settingsCache->getCardBackPicturePath());
|
||||
return pixmap;
|
||||
|
@ -400,18 +406,33 @@ void CardInfo::updatePixmapCache()
|
|||
emit pixmapUpdated();
|
||||
}
|
||||
|
||||
CardSet* CardInfo::getPreferredSet()
|
||||
{
|
||||
SetList sortedSets = sets;
|
||||
sortedSets.sortByKey();
|
||||
return sortedSets.first();
|
||||
}
|
||||
|
||||
int CardInfo::getPreferredMuId()
|
||||
{
|
||||
return muIds[getPreferredSet()->getShortName()];
|
||||
}
|
||||
|
||||
QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfo *info)
|
||||
{
|
||||
xml.writeStartElement("card");
|
||||
xml.writeTextElement("name", info->getName());
|
||||
|
||||
const SetList &sets = info->getSets();
|
||||
QString tmpString;
|
||||
QString tmpSet;
|
||||
for (int i = 0; i < sets.size(); i++) {
|
||||
xml.writeStartElement("set");
|
||||
xml.writeAttribute("picURL", info->getPicURL(sets[i]->getShortName()));
|
||||
xml.writeAttribute("picURLHq", info->getPicURLHq(sets[i]->getShortName()));
|
||||
xml.writeAttribute("picURLSt", info->getPicURLSt(sets[i]->getShortName()));
|
||||
xml.writeCharacters(sets[i]->getShortName());
|
||||
|
||||
tmpSet=sets[i]->getShortName();
|
||||
xml.writeAttribute("muId", QString::number(info->getMuId(tmpSet)));
|
||||
|
||||
xml.writeCharacters(tmpSet);
|
||||
xml.writeEndElement();
|
||||
}
|
||||
const QStringList &colors = info->getColors();
|
||||
|
@ -442,12 +463,13 @@ CardDatabase::CardDatabase(QObject *parent)
|
|||
connect(settingsCache, SIGNAL(cardDatabasePathChanged()), this, SLOT(loadCardDatabase()));
|
||||
connect(settingsCache, SIGNAL(tokenDatabasePathChanged()), this, SLOT(loadTokenDatabase()));
|
||||
connect(settingsCache, SIGNAL(picDownloadChanged()), this, SLOT(picDownloadChanged()));
|
||||
|
||||
connect(settingsCache, SIGNAL(picDownloadHqChanged()), this, SLOT(picDownloadHqChanged()));
|
||||
|
||||
loadCardDatabase();
|
||||
loadTokenDatabase();
|
||||
|
||||
|
||||
pictureLoaderThread = new QThread;
|
||||
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload());
|
||||
pictureLoader = new PictureLoader(settingsCache->getPicsPath(), settingsCache->getPicDownload(), settingsCache->getPicDownloadHq());
|
||||
pictureLoader->moveToThread(pictureLoaderThread);
|
||||
connect(pictureLoader, SIGNAL(imageLoaded(CardInfo *, const QImage &)), this, SLOT(imageLoaded(CardInfo *, const QImage &)));
|
||||
pictureLoaderThread->start(QThread::LowPriority);
|
||||
|
@ -572,7 +594,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
if (xml.name() == "card") {
|
||||
QString name, manacost, type, pt, text;
|
||||
QStringList colors;
|
||||
QMap<QString, QString> picURLs, picURLsHq, picURLsSt;
|
||||
QMap<QString, int> muids;
|
||||
SetList sets;
|
||||
int tableRow = 0;
|
||||
int loyalty = 0;
|
||||
|
@ -592,14 +614,12 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
else if (xml.name() == "text")
|
||||
text = xml.readElementText();
|
||||
else if (xml.name() == "set") {
|
||||
QString picURL = xml.attributes().value("picURL").toString();
|
||||
QString picURLHq = xml.attributes().value("picURLHq").toString();
|
||||
QString picURLSt = xml.attributes().value("picURLSt").toString();
|
||||
QXmlStreamAttributes attrs = xml.attributes();
|
||||
QString setName = xml.readElementText();
|
||||
sets.append(getSet(setName));
|
||||
picURLs.insert(setName, picURL);
|
||||
picURLsHq.insert(setName, picURLHq);
|
||||
picURLsSt.insert(setName, picURLSt);
|
||||
if (attrs.hasAttribute("muId")) {
|
||||
muids[setName] = attrs.value("muId").toString().toInt();
|
||||
}
|
||||
} else if (xml.name() == "color")
|
||||
colors << xml.readElementText();
|
||||
else if (xml.name() == "tablerow")
|
||||
|
@ -611,7 +631,7 @@ void CardDatabase::loadCardsFromXml(QXmlStreamReader &xml)
|
|||
else if (xml.name() == "token")
|
||||
isToken = xml.readElementText().toInt();
|
||||
}
|
||||
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, picURLs, picURLsHq, picURLsSt));
|
||||
cardHash.insert(name, new CardInfo(this, name, isToken, manacost, type, pt, text, colors, loyalty, cipt, tableRow, sets, muids));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -717,6 +737,16 @@ void CardDatabase::picDownloadChanged()
|
|||
}
|
||||
}
|
||||
|
||||
void CardDatabase::picDownloadHqChanged()
|
||||
{
|
||||
pictureLoader->setPicDownloadHq(settingsCache->getPicDownloadHq());
|
||||
if (settingsCache->getPicDownloadHq()) {
|
||||
QHashIterator<QString, CardInfo *> cardIterator(cardHash);
|
||||
while (cardIterator.hasNext())
|
||||
cardIterator.next().value()->clearPixmapCacheMiss();
|
||||
}
|
||||
}
|
||||
|
||||
bool CardDatabase::loadCardDatabase(const QString &path, bool tokens)
|
||||
{
|
||||
bool tempLoadSuccess = false;
|
||||
|
|
|
@ -68,13 +68,15 @@ private:
|
|||
QNetworkAccessManager *networkManager;
|
||||
QList<PictureToLoad> cardsToDownload;
|
||||
PictureToLoad cardBeingDownloaded;
|
||||
bool picDownload, downloadRunning, loadQueueRunning;
|
||||
bool picDownload, picDownloadHq, downloadRunning, loadQueueRunning;
|
||||
void startNextPicDownload();
|
||||
QString getPicUrl(CardInfo* card);
|
||||
public:
|
||||
PictureLoader(const QString &__picsPath, bool _picDownload, QObject *parent = 0);
|
||||
PictureLoader(const QString &__picsPath, bool _picDownload, bool _picDownloadHq, QObject *parent = 0);
|
||||
~PictureLoader();
|
||||
void setPicsPath(const QString &path);
|
||||
void setPicDownload(bool _picDownload);
|
||||
void setPicDownloadHq(bool _picDownloadHq);
|
||||
void loadImage(CardInfo *card, bool stripped);
|
||||
private slots:
|
||||
void picDownloadFinished(QNetworkReply *reply);
|
||||
|
@ -99,7 +101,7 @@ private:
|
|||
QString text;
|
||||
QStringList colors;
|
||||
int loyalty;
|
||||
QMap<QString, QString> picURLs, picURLsHq, picURLsSt;
|
||||
QMap<QString, int> muIds;
|
||||
bool cipt;
|
||||
int tableRow;
|
||||
QPixmap *pixmap;
|
||||
|
@ -117,9 +119,7 @@ public:
|
|||
bool _cipt = false,
|
||||
int _tableRow = 0,
|
||||
const SetList &_sets = SetList(),
|
||||
const QStringMap &_picURLs = QStringMap(),
|
||||
const QStringMap &_picURLsHq = QStringMap(),
|
||||
const QStringMap &_picURLsSt = QStringMap());
|
||||
QMap<QString, int> muids = QMap<QString, int>());
|
||||
~CardInfo();
|
||||
const QString &getName() const { return name; }
|
||||
bool getIsToken() const { return isToken; }
|
||||
|
@ -136,25 +136,21 @@ 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; }
|
||||
QString getPicURL(const QString &set) const { return picURLs.value(set); }
|
||||
QString getPicURLHq(const QString &set) const { return picURLsHq.value(set); }
|
||||
QString getPicURLSt(const QString &set) const { return picURLsSt.value(set); }
|
||||
QString getPicURL() const;
|
||||
const QMap<QString, QString> &getPicURLs() const { return picURLs; }
|
||||
int getMuId(const QString &set) const { return muIds.value(set); }
|
||||
QString getMainCardType() const;
|
||||
QString getCorrectedName() const;
|
||||
int getTableRow() const { return tableRow; }
|
||||
void setTableRow(int _tableRow) { tableRow = _tableRow; }
|
||||
void setLoyalty(int _loyalty) { loyalty = _loyalty; emit cardInfoChanged(this); }
|
||||
void setPicURL(const QString &_set, const QString &_picURL) { picURLs.insert(_set, _picURL); }
|
||||
void setPicURLHq(const QString &_set, const QString &_picURL) { picURLsHq.insert(_set, _picURL); }
|
||||
void setPicURLSt(const QString &_set, const QString &_picURL) { picURLsSt.insert(_set, _picURL); }
|
||||
void setMuId(const QString &_set, const int &_muId) { muIds.insert(_set, _muId); }
|
||||
void addToSet(CardSet *set);
|
||||
QPixmap *loadPixmap();
|
||||
QPixmap *getPixmap(QSize size);
|
||||
void clearPixmapCache();
|
||||
void clearPixmapCacheMiss();
|
||||
void imageLoaded(const QImage &image);
|
||||
CardSet *getPreferredSet();
|
||||
int getPreferredMuId();
|
||||
public slots:
|
||||
void updatePixmapCache();
|
||||
signals:
|
||||
|
@ -169,7 +165,7 @@ protected:
|
|||
QHash<QString, CardSet *> setHash;
|
||||
bool loadSuccess;
|
||||
CardInfo *noCard;
|
||||
|
||||
|
||||
QThread *pictureLoaderThread;
|
||||
PictureLoader *pictureLoader;
|
||||
private:
|
||||
|
@ -199,6 +195,7 @@ public slots:
|
|||
private slots:
|
||||
void imageLoaded(CardInfo *card, QImage image);
|
||||
void picDownloadChanged();
|
||||
void picDownloadHqChanged();
|
||||
void picsPathChanged();
|
||||
|
||||
void loadCardDatabase();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "carddatabasemodel.h"
|
||||
#include "filtertree.h"
|
||||
|
||||
CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, QObject *parent)
|
||||
: QAbstractListModel(parent), db(_db)
|
||||
|
@ -109,6 +110,7 @@ CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent)
|
|||
: QSortFilterProxyModel(parent),
|
||||
isToken(ShowAll)
|
||||
{
|
||||
filterTree = NULL;
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
}
|
||||
|
@ -116,7 +118,6 @@ CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent)
|
|||
bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const
|
||||
{
|
||||
CardInfo const *info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
|
||||
|
||||
if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken()))
|
||||
return false;
|
||||
|
||||
|
@ -144,6 +145,9 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
|||
if (!cardTypes.contains(info->getMainCardType()))
|
||||
return false;
|
||||
|
||||
if (filterTree != NULL)
|
||||
return filterTree->acceptsCard(info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -153,5 +157,22 @@ void CardDatabaseDisplayModel::clearSearch()
|
|||
cardText.clear();
|
||||
cardTypes.clear();
|
||||
cardColors.clear();
|
||||
if (filterTree != NULL)
|
||||
filterTree->clear();
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void CardDatabaseDisplayModel::setFilterTree(FilterTree *filterTree)
|
||||
{
|
||||
if (this->filterTree != NULL)
|
||||
disconnect(this->filterTree, 0, this, 0);
|
||||
|
||||
this->filterTree = filterTree;
|
||||
connect(this->filterTree, SIGNAL(changed()), this, SLOT(filterTreeChanged()));
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void CardDatabaseDisplayModel::filterTreeChanged()
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <QSet>
|
||||
#include "carddatabase.h"
|
||||
|
||||
class FilterTree;
|
||||
|
||||
class CardDatabaseModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -36,8 +38,10 @@ private:
|
|||
FilterBool isToken;
|
||||
QString cardNameBeginning, cardName, cardText;
|
||||
QSet<QString> cardNameSet, cardTypes, cardColors;
|
||||
FilterTree *filterTree;
|
||||
public:
|
||||
CardDatabaseDisplayModel(QObject *parent = 0);
|
||||
void setFilterTree(FilterTree *filterTree);
|
||||
void setIsToken(FilterBool _isToken) { isToken = _isToken; invalidate(); }
|
||||
void setCardNameBeginning(const QString &_beginning) { cardNameBeginning = _beginning; invalidate(); }
|
||||
void setCardName(const QString &_cardName) { cardName = _cardName; invalidate(); }
|
||||
|
@ -48,6 +52,8 @@ public:
|
|||
void clearSearch();
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
private slots:
|
||||
void filterTreeChanged();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
37
cockatrice/src/cardfilter.cpp
Normal file
37
cockatrice/src/cardfilter.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "cardfilter.h"
|
||||
|
||||
const char *CardFilter::typeName(Type t)
|
||||
{
|
||||
switch (t) {
|
||||
case TypeAnd:
|
||||
return "and";
|
||||
case TypeOr:
|
||||
return "or";
|
||||
case TypeAndNot:
|
||||
return "and not";
|
||||
case TypeOrNot:
|
||||
return "or not";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const char *CardFilter::attrName(Attr a)
|
||||
{
|
||||
switch (a) {
|
||||
case AttrName:
|
||||
return "name";
|
||||
case AttrType:
|
||||
return "type";
|
||||
case AttrColor:
|
||||
return "color";
|
||||
case AttrText:
|
||||
return "text";
|
||||
case AttrSet:
|
||||
return "set";
|
||||
case AttrManaCost:
|
||||
return "mana cost";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
44
cockatrice/src/cardfilter.h
Normal file
44
cockatrice/src/cardfilter.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef CARDFILTER_H
|
||||
#define CARDFILTER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class CardFilter {
|
||||
public:
|
||||
enum Type {
|
||||
TypeAnd = 0,
|
||||
TypeOr,
|
||||
TypeAndNot,
|
||||
TypeOrNot,
|
||||
TypeEnd
|
||||
};
|
||||
|
||||
/* if you add an atribute here you also need to
|
||||
* add its string representation in attrName */
|
||||
enum Attr {
|
||||
AttrName = 0,
|
||||
AttrType,
|
||||
AttrColor,
|
||||
AttrText,
|
||||
AttrSet,
|
||||
AttrManaCost,
|
||||
AttrEnd
|
||||
};
|
||||
|
||||
private:
|
||||
enum Type t;
|
||||
enum Attr a;
|
||||
QString trm;
|
||||
|
||||
public:
|
||||
CardFilter(QString term, Type type, Attr attr) : trm(term), t(type), a(attr) {};
|
||||
|
||||
Type type() const { return t; }
|
||||
const QString &term() const { return trm; }
|
||||
Attr attr() const { return a; }
|
||||
|
||||
static const char *typeName(Type t);
|
||||
static const char *attrName(Attr a);
|
||||
};
|
||||
|
||||
#endif
|
59
cockatrice/src/cardframe.cpp
Normal file
59
cockatrice/src/cardframe.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "cardframe.h"
|
||||
|
||||
#include "carditem.h"
|
||||
#include "carddatabase.h"
|
||||
#include "main.h"
|
||||
#include "cardinfopicture.h"
|
||||
#include "cardinfotext.h"
|
||||
|
||||
CardFrame::CardFrame(int width, int height,
|
||||
const QString &cardName, QWidget *parent)
|
||||
: QStackedWidget(parent)
|
||||
, info(0)
|
||||
, cardTextOnly(false)
|
||||
{
|
||||
setFrameStyle(QFrame::Panel | QFrame::Raised);
|
||||
setMaximumWidth(width);
|
||||
setMinimumWidth(width);
|
||||
setMaximumHeight(height);
|
||||
setMinimumHeight(height);
|
||||
pic = new CardInfoPicture(width);
|
||||
addWidget(pic);
|
||||
text = new CardInfoText();
|
||||
addWidget(text);
|
||||
connect(pic, SIGNAL(hasPictureChanged()), this, SLOT(hasPictureChanged()));
|
||||
setCard(db->getCard(cardName));
|
||||
}
|
||||
|
||||
void CardFrame::setCard(CardInfo *card)
|
||||
{
|
||||
if (info)
|
||||
disconnect(info, 0, this, 0);
|
||||
info = card;
|
||||
connect(info, SIGNAL(destroyed()), this, SLOT(clear()));
|
||||
text->setCard(info);
|
||||
pic->setCard(info);
|
||||
}
|
||||
|
||||
void CardFrame::setCard(const QString &cardName)
|
||||
{
|
||||
setCard(db->getCard(cardName));
|
||||
}
|
||||
|
||||
void CardFrame::setCard(AbstractCardItem *card)
|
||||
{
|
||||
setCard(card->getInfo());
|
||||
}
|
||||
|
||||
void CardFrame::clear()
|
||||
{
|
||||
setCard(db->getCard());
|
||||
}
|
||||
|
||||
void CardFrame::hasPictureChanged()
|
||||
{
|
||||
if (pic->hasPicture() && !cardTextOnly)
|
||||
setCurrentWidget(pic);
|
||||
else
|
||||
setCurrentWidget(text);
|
||||
}
|
36
cockatrice/src/cardframe.h
Normal file
36
cockatrice/src/cardframe.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef CARDFRAME_H
|
||||
#define CARDFRAME_H
|
||||
|
||||
#include <QStackedWidget>
|
||||
|
||||
class AbstractCardItem;
|
||||
class CardInfo;
|
||||
class CardInfoPicture;
|
||||
class CardInfoText;
|
||||
|
||||
class CardFrame : public QStackedWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
CardInfo *info;
|
||||
CardInfoPicture *pic;
|
||||
CardInfoText *text;
|
||||
bool cardTextOnly;
|
||||
|
||||
public:
|
||||
CardFrame(int width, int height, const QString &cardName = QString(),
|
||||
QWidget *parent = 0);
|
||||
void setCardTextOnly(bool status) { cardTextOnly = status; hasPictureChanged(); }
|
||||
|
||||
public slots:
|
||||
void setCard(CardInfo *card);
|
||||
void setCard(const QString &cardName);
|
||||
void setCard(AbstractCardItem *card);
|
||||
void clear();
|
||||
|
||||
private slots:
|
||||
void hasPictureChanged();
|
||||
void toggleCardTextOnly() { setCardTextOnly(!cardTextOnly); }
|
||||
};
|
||||
|
||||
#endif
|
53
cockatrice/src/cardinfopicture.cpp
Normal file
53
cockatrice/src/cardinfopicture.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#include "cardinfopicture.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include "carditem.h"
|
||||
#include "carddatabase.h"
|
||||
#include "main.h"
|
||||
|
||||
CardInfoPicture::CardInfoPicture(int maximumWidth, QWidget *parent)
|
||||
: QLabel(parent)
|
||||
, info(0)
|
||||
, noPicture(true)
|
||||
{
|
||||
setMaximumWidth(maximumWidth);
|
||||
}
|
||||
|
||||
void CardInfoPicture::setNoPicture(bool status)
|
||||
{
|
||||
if (noPicture != status) {
|
||||
noPicture = status;
|
||||
emit hasPictureChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void CardInfoPicture::setCard(CardInfo *card)
|
||||
{
|
||||
if (info)
|
||||
disconnect(info, 0, this, 0);
|
||||
info = card;
|
||||
connect(info, SIGNAL(pixmapUpdated()), this, SLOT(updatePixmap()));
|
||||
|
||||
updatePixmap();
|
||||
}
|
||||
|
||||
void CardInfoPicture::updatePixmap()
|
||||
{
|
||||
qreal aspectRatio = (qreal) CARD_HEIGHT / (qreal) CARD_WIDTH;
|
||||
qreal pixmapWidth = this->width();
|
||||
|
||||
if (pixmapWidth == 0) {
|
||||
setNoPicture(true);
|
||||
return;
|
||||
}
|
||||
|
||||
QPixmap *resizedPixmap = info->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio));
|
||||
if (resizedPixmap) {
|
||||
setNoPicture(false);
|
||||
this->setPixmap(*resizedPixmap);
|
||||
}
|
||||
else {
|
||||
setNoPicture(true);
|
||||
this->setPixmap(*(db->getCard()->getPixmap(QSize(pixmapWidth, pixmapWidth * aspectRatio))));
|
||||
}
|
||||
}
|
34
cockatrice/src/cardinfopicture.h
Normal file
34
cockatrice/src/cardinfopicture.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef CARDINFOPICTURE_H
|
||||
#define CARDINFOPICTURE_H
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
class AbstractCardItem;
|
||||
class CardInfo;
|
||||
|
||||
class CardInfoPicture : public QLabel {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void hasPictureChanged();
|
||||
|
||||
private:
|
||||
CardInfo *info;
|
||||
bool noPicture;
|
||||
|
||||
public:
|
||||
CardInfoPicture(int maximumWidth, QWidget *parent = 0);
|
||||
bool hasPicture() const { return !noPicture; }
|
||||
|
||||
private:
|
||||
void setNoPicture(bool status);
|
||||
|
||||
public slots:
|
||||
void setCard(CardInfo *card);
|
||||
|
||||
private slots:
|
||||
void updatePixmap();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
67
cockatrice/src/cardinfotext.cpp
Normal file
67
cockatrice/src/cardinfotext.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "cardinfotext.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
#include <QGridLayout>
|
||||
#include "carditem.h"
|
||||
#include "carddatabase.h"
|
||||
#include "main.h"
|
||||
|
||||
CardInfoText::CardInfoText(QWidget *parent)
|
||||
: QFrame(parent)
|
||||
, info(0)
|
||||
{
|
||||
nameLabel1 = new QLabel;
|
||||
nameLabel2 = new QLabel;
|
||||
nameLabel2->setWordWrap(true);
|
||||
manacostLabel1 = new QLabel;
|
||||
manacostLabel2 = new QLabel;
|
||||
manacostLabel2->setWordWrap(true);
|
||||
cardtypeLabel1 = new QLabel;
|
||||
cardtypeLabel2 = new QLabel;
|
||||
cardtypeLabel2->setWordWrap(true);
|
||||
powtoughLabel1 = new QLabel;
|
||||
powtoughLabel2 = new QLabel;
|
||||
loyaltyLabel1 = new QLabel;
|
||||
loyaltyLabel2 = new QLabel;
|
||||
|
||||
textLabel = new QTextEdit();
|
||||
textLabel->setReadOnly(true);
|
||||
|
||||
QGridLayout *grid = new QGridLayout(this);
|
||||
int row = 0;
|
||||
grid->addWidget(nameLabel1, row, 0);
|
||||
grid->addWidget(nameLabel2, row++, 1);
|
||||
grid->addWidget(manacostLabel1, row, 0);
|
||||
grid->addWidget(manacostLabel2, row++, 1);
|
||||
grid->addWidget(cardtypeLabel1, row, 0);
|
||||
grid->addWidget(cardtypeLabel2, row++, 1);
|
||||
grid->addWidget(powtoughLabel1, row, 0);
|
||||
grid->addWidget(powtoughLabel2, row++, 1);
|
||||
grid->addWidget(loyaltyLabel1, row, 0);
|
||||
grid->addWidget(loyaltyLabel2, row++, 1);
|
||||
grid->addWidget(textLabel, row, 0, -1, 2);
|
||||
grid->setRowStretch(row, 1);
|
||||
grid->setColumnStretch(1, 1);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
void CardInfoText::setCard(CardInfo *card)
|
||||
{
|
||||
nameLabel2->setText(card->getName());
|
||||
manacostLabel2->setText(card->getManaCost());
|
||||
cardtypeLabel2->setText(card->getCardType());
|
||||
powtoughLabel2->setText(card->getPowTough());
|
||||
loyaltyLabel2->setText(card->getLoyalty() > 0 ? QString::number(card->getLoyalty()) : QString());
|
||||
textLabel->setText(card->getText());
|
||||
}
|
||||
|
||||
void CardInfoText::retranslateUi()
|
||||
{
|
||||
nameLabel1->setText(tr("Name:"));
|
||||
manacostLabel1->setText(tr("Mana cost:"));
|
||||
cardtypeLabel1->setText(tr("Card type:"));
|
||||
powtoughLabel1->setText(tr("P / T:"));
|
||||
loyaltyLabel1->setText(tr("Loyalty:"));
|
||||
}
|
31
cockatrice/src/cardinfotext.h
Normal file
31
cockatrice/src/cardinfotext.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef CARDINFOTEXT_H
|
||||
#define CARDINFOTEXT_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
class QLabel;
|
||||
class QTextEdit;
|
||||
class CardInfo;
|
||||
|
||||
class CardInfoText : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QLabel *nameLabel1, *nameLabel2;
|
||||
QLabel *manacostLabel1, *manacostLabel2;
|
||||
QLabel *cardtypeLabel1, *cardtypeLabel2;
|
||||
QLabel *powtoughLabel1, *powtoughLabel2;
|
||||
QLabel *loyaltyLabel1, *loyaltyLabel2;
|
||||
QTextEdit *textLabel;
|
||||
|
||||
CardInfo *info;
|
||||
|
||||
public:
|
||||
CardInfoText(QWidget *parent = 0);
|
||||
void retranslateUi();
|
||||
|
||||
public slots:
|
||||
void setCard(CardInfo *card);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -148,7 +148,7 @@ QString CardZone::getTranslatedName(bool hisOwn, GrammaticalCase gc) const
|
|||
return QString();
|
||||
}
|
||||
|
||||
void CardZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent */*event*/)
|
||||
void CardZone::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
if (doubleClickAction)
|
||||
doubleClickAction->trigger();
|
||||
|
|
|
@ -13,7 +13,7 @@ QRectF GeneralCounter::boundingRect() const
|
|||
return QRectF(0, 0, radius * 2, radius * 2);
|
||||
}
|
||||
|
||||
void GeneralCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void GeneralCounter::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
QRectF mapRect = painter->combinedTransform().mapRect(boundingRect());
|
||||
int translatedHeight = mapRect.size().height();
|
||||
|
|
|
@ -169,8 +169,7 @@ Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const
|
|||
return 0;
|
||||
|
||||
Qt::ItemFlags result = Qt::ItemIsEnabled;
|
||||
if (getNode<DecklistModelCardNode *>(index))
|
||||
result |= Qt::ItemIsSelectable;
|
||||
result |= Qt::ItemIsSelectable;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -236,6 +235,38 @@ InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerD
|
|||
return newNode;
|
||||
}
|
||||
|
||||
DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, const QString &zoneName) const
|
||||
{
|
||||
InnerDecklistNode *zoneNode, *typeNode;
|
||||
CardInfo *info;
|
||||
QString cardType;
|
||||
|
||||
zoneNode = dynamic_cast<InnerDecklistNode *>(root->findChild(zoneName));
|
||||
if(!zoneNode)
|
||||
return NULL;
|
||||
|
||||
info = db->getCard(cardName);
|
||||
if(!info)
|
||||
return NULL;
|
||||
|
||||
cardType = info->getMainCardType();
|
||||
typeNode = dynamic_cast<InnerDecklistNode *>(zoneNode->findChild(cardType));
|
||||
if(!typeNode)
|
||||
return NULL;
|
||||
|
||||
return dynamic_cast<DecklistModelCardNode *>(typeNode->findChild(cardName));
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::findCard(const QString &cardName, const QString &zoneName) const
|
||||
{
|
||||
DecklistModelCardNode *cardNode;
|
||||
|
||||
cardNode = findCardNode(cardName, zoneName);
|
||||
if(!cardNode)
|
||||
return QModelIndex();
|
||||
return nodeToIndex(cardNode);
|
||||
}
|
||||
|
||||
QModelIndex DeckListModel::addCard(const QString &cardName, const QString &zoneName)
|
||||
{
|
||||
InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root);
|
||||
|
|
|
@ -19,7 +19,7 @@ public:
|
|||
int getNumber() const { return dataNode->getNumber(); }
|
||||
void setNumber(int _number) { dataNode->setNumber(_number); }
|
||||
float getPrice() const { return dataNode->getPrice(); }
|
||||
void setPrice(float _price) { dataNode->setPrice(_price); }
|
||||
void setPrice(const float _price) { dataNode->setPrice(_price); }
|
||||
QString getName() const { return dataNode->getName(); }
|
||||
void setName(const QString &_name) { dataNode->setName(_name); }
|
||||
DecklistCardNode *getDataNode() const { return dataNode; }
|
||||
|
@ -45,6 +45,7 @@ public:
|
|||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
|
||||
QModelIndex findCard(const QString &cardName, const QString &zoneName) const;
|
||||
QModelIndex addCard(const QString &cardName, const QString &zoneName);
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
||||
void cleanList();
|
||||
|
@ -58,6 +59,7 @@ private:
|
|||
Qt::SortOrder lastKnownOrder;
|
||||
InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent);
|
||||
QModelIndex nodeToIndex(AbstractDecklistNode *node) const;
|
||||
DecklistModelCardNode *findCardNode(const QString &cardName, const QString &zoneName) const;
|
||||
void emitRecursiveUpdates(const QModelIndex &index);
|
||||
void sortHelper(InnerDecklistNode *node, Qt::SortOrder order);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
{
|
||||
languageLabel = new QLabel;
|
||||
languageBox = new QComboBox;
|
||||
|
||||
|
||||
QString setLanguage = settingsCache->getLang();
|
||||
QStringList qmFiles = findQmFiles();
|
||||
for (int i = 0; i < qmFiles.size(); i++) {
|
||||
|
@ -36,27 +36,32 @@ GeneralSettingsPage::GeneralSettingsPage()
|
|||
if ((qmFiles[i] == setLanguage) || (setLanguage.isEmpty() && langName == tr("English")))
|
||||
languageBox->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
|
||||
picDownloadCheckBox = new QCheckBox;
|
||||
picDownloadCheckBox->setChecked(settingsCache->getPicDownload());
|
||||
|
||||
|
||||
picDownloadHqCheckBox = new QCheckBox;
|
||||
picDownloadHqCheckBox->setChecked(settingsCache->getPicDownloadHq());
|
||||
|
||||
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)));
|
||||
|
||||
QGridLayout *personalGrid = new QGridLayout;
|
||||
personalGrid->addWidget(languageLabel, 0, 0);
|
||||
personalGrid->addWidget(languageBox, 0, 1);
|
||||
personalGrid->addWidget(picDownloadCheckBox, 1, 0, 1, 2);
|
||||
|
||||
personalGrid->addWidget(picDownloadHqCheckBox, 2, 0, 1, 2);
|
||||
|
||||
personalGroupBox = new QGroupBox;
|
||||
personalGroupBox->setLayout(personalGrid);
|
||||
|
||||
|
||||
deckPathLabel = new QLabel;
|
||||
deckPathEdit = new QLineEdit(settingsCache->getDeckPath());
|
||||
deckPathEdit->setReadOnly(true);
|
||||
QPushButton *deckPathButton = new QPushButton("...");
|
||||
connect(deckPathButton, SIGNAL(clicked()), this, SLOT(deckPathButtonClicked()));
|
||||
|
||||
|
||||
replaysPathLabel = new QLabel;
|
||||
replaysPathEdit = new QLineEdit(settingsCache->getReplaysPath());
|
||||
replaysPathEdit->setReadOnly(true);
|
||||
|
@ -183,6 +188,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"));
|
||||
pathsGroupBox->setTitle(tr("Paths"));
|
||||
deckPathLabel->setText(tr("Decks directory:"));
|
||||
replaysPathLabel->setText(tr("Replays directory:"));
|
||||
|
|
|
@ -40,6 +40,7 @@ private:
|
|||
QGroupBox *personalGroupBox, *pathsGroupBox;
|
||||
QComboBox *languageBox;
|
||||
QCheckBox *picDownloadCheckBox;
|
||||
QCheckBox *picDownloadHqCheckBox;
|
||||
QLabel *languageLabel, *deckPathLabel, *replaysPathLabel, *picsPathLabel, *cardDatabasePathLabel, *tokenDatabasePathLabel;
|
||||
};
|
||||
|
||||
|
|
84
cockatrice/src/filterbuilder.cpp
Normal file
84
cockatrice/src/filterbuilder.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#include "filterbuilder.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "cardfilter.h"
|
||||
|
||||
FilterBuilder::FilterBuilder(QWidget *parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
QHBoxLayout *addFilter = new QHBoxLayout;
|
||||
|
||||
filterCombo = new QComboBox;
|
||||
for (i = 0; i < CardFilter::AttrEnd; i++)
|
||||
filterCombo->addItem(
|
||||
tr(CardFilter::attrName(static_cast<CardFilter::Attr>(i))),
|
||||
QVariant(i)
|
||||
);
|
||||
|
||||
typeCombo = new QComboBox;
|
||||
for (i = 0; i < CardFilter::TypeEnd; i++)
|
||||
typeCombo->addItem(
|
||||
tr(CardFilter::typeName(static_cast<CardFilter::Type>(i))),
|
||||
QVariant(i)
|
||||
);
|
||||
|
||||
QPushButton *ok = new QPushButton("+");
|
||||
ok->setMaximumSize(20, 20);
|
||||
|
||||
addFilter->addWidget(ok);
|
||||
addFilter->addWidget(typeCombo);
|
||||
addFilter->addWidget(filterCombo, Qt::AlignLeft);
|
||||
|
||||
edit = new QLineEdit;
|
||||
edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
layout->addLayout(addFilter);
|
||||
layout->addWidget(edit);
|
||||
|
||||
setFrameStyle(QFrame::Panel | QFrame::Raised);
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(layout);
|
||||
|
||||
connect(edit, SIGNAL(returnPressed()), this, SLOT(emit_add()));
|
||||
connect(ok, SIGNAL(released()), this, SLOT(emit_add()));
|
||||
connect(filterCombo, SIGNAL(currentIndexChanged(int)), edit, SLOT(clear()));
|
||||
fltr = NULL;
|
||||
}
|
||||
|
||||
FilterBuilder::~FilterBuilder()
|
||||
{
|
||||
destroyFilter();
|
||||
}
|
||||
|
||||
void FilterBuilder::destroyFilter()
|
||||
{
|
||||
if (fltr)
|
||||
delete fltr;
|
||||
}
|
||||
|
||||
static int comboCurrentIntData(const QComboBox *combo)
|
||||
{
|
||||
return combo->itemData(combo->currentIndex()).toInt();
|
||||
}
|
||||
|
||||
void FilterBuilder::emit_add()
|
||||
{
|
||||
QString txt;
|
||||
|
||||
txt = edit->text();
|
||||
if (txt.length() < 1)
|
||||
return;
|
||||
|
||||
destroyFilter();
|
||||
fltr = new CardFilter(txt,
|
||||
static_cast<CardFilter::Type>(comboCurrentIntData(typeCombo)),
|
||||
static_cast<CardFilter::Attr>(comboCurrentIntData(filterCombo)));
|
||||
emit add(fltr);
|
||||
edit->clear();
|
||||
}
|
35
cockatrice/src/filterbuilder.h
Normal file
35
cockatrice/src/filterbuilder.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#ifndef FILTERBUILDER_H
|
||||
#define FILTERBUILDER_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLineEdit;
|
||||
class CardFilter;
|
||||
|
||||
class FilterBuilder : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QComboBox *typeCombo;
|
||||
QComboBox *filterCombo;
|
||||
QLineEdit *edit;
|
||||
CardFilter *fltr;
|
||||
|
||||
void destroyFilter();
|
||||
|
||||
public:
|
||||
FilterBuilder(QWidget *parent = 0);
|
||||
~FilterBuilder();
|
||||
|
||||
signals:
|
||||
void add(const CardFilter *f);
|
||||
|
||||
public slots:
|
||||
private slots:
|
||||
void emit_add();
|
||||
protected:
|
||||
};
|
||||
|
||||
#endif
|
339
cockatrice/src/filtertree.cpp
Normal file
339
cockatrice/src/filtertree.cpp
Normal file
|
@ -0,0 +1,339 @@
|
|||
#include "filtertree.h"
|
||||
#include "cardfilter.h"
|
||||
#include "carddatabase.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
template <class T>
|
||||
FilterTreeNode *FilterTreeBranch<T>::nodeAt(int i) const
|
||||
{
|
||||
return ((childNodes.size() > i)? childNodes.at(i) : NULL);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void FilterTreeBranch<T>::deleteAt(int i)
|
||||
{
|
||||
preRemoveChild(this, i);
|
||||
delete childNodes.takeAt(i);
|
||||
postRemoveChild(this, i);
|
||||
nodeChanged();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
int FilterTreeBranch<T>::childIndex(const FilterTreeNode *node) const
|
||||
{
|
||||
FilterTreeNode *unconst;
|
||||
T downcasted;
|
||||
|
||||
/* to do the dynamic cast to T we will lose const'ness, but we can
|
||||
* trust QList::indexOf */
|
||||
unconst = (FilterTreeNode *) node;
|
||||
downcasted = dynamic_cast<T>(unconst);
|
||||
if (downcasted == NULL)
|
||||
return -1;
|
||||
return childNodes.indexOf(downcasted);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FilterTreeBranch<T>::~FilterTreeBranch()
|
||||
{
|
||||
while (!childNodes.isEmpty())
|
||||
delete childNodes.takeFirst();
|
||||
}
|
||||
|
||||
const FilterItemList *LogicMap::findTypeList(CardFilter::Type type) const
|
||||
{
|
||||
QList<FilterItemList *>::const_iterator i;
|
||||
|
||||
for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++)
|
||||
if ((*i)->type == type)
|
||||
return *i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FilterItemList *LogicMap::typeList(CardFilter::Type type)
|
||||
{
|
||||
QList<FilterItemList *>::iterator i;
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
for (i = childNodes.begin(); i != childNodes.end(); i++) {
|
||||
if ((*i)->type == type)
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
if (i == childNodes.end()) {
|
||||
preInsertChild(this, count);
|
||||
i = childNodes.insert(i, new FilterItemList(type, this));
|
||||
postInsertChild(this, count);
|
||||
nodeChanged();
|
||||
}
|
||||
|
||||
return *i;
|
||||
}
|
||||
|
||||
FilterTreeNode *LogicMap::parent() const
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
int FilterItemList::termIndex(const QString &term) const
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < childNodes.count(); i++)
|
||||
if ((childNodes.at(i))->term == term)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
FilterTreeNode *FilterItemList::termNode(const QString &term)
|
||||
{
|
||||
int i, count;
|
||||
FilterItem *fi;
|
||||
|
||||
i = termIndex(term);
|
||||
if (i < 0) {
|
||||
fi = new FilterItem(term, this);
|
||||
count = childNodes.count();
|
||||
preInsertChild(this, count);
|
||||
childNodes.append(fi);
|
||||
postInsertChild(this, count);
|
||||
nodeChanged();
|
||||
return fi;
|
||||
}
|
||||
|
||||
return childNodes.at(i);
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeAnd(const CardInfo *info, CardFilter::Attr attr) const
|
||||
{
|
||||
QList<FilterItem *>::const_iterator i;
|
||||
|
||||
for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++) {
|
||||
if (!(*i)->isEnabled())
|
||||
continue;
|
||||
if (!(*i)->acceptCardAttr(info, attr))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeAndNot(const CardInfo *info, CardFilter::Attr attr) const
|
||||
{
|
||||
// if any one in the list is true, return false
|
||||
return !testTypeOr(info, attr);
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeOr(const CardInfo *info, CardFilter::Attr attr) const
|
||||
{
|
||||
QList<FilterItem *>::const_iterator i;
|
||||
|
||||
for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++) {
|
||||
if (!(*i)->isEnabled())
|
||||
continue;
|
||||
if ((*i)->acceptCardAttr(info, attr))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FilterItemList::testTypeOrNot(const CardInfo *info, CardFilter::Attr attr) const
|
||||
{
|
||||
// if any one in the list is false, return true
|
||||
return !testTypeAnd(info, attr);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptName(const CardInfo *info) const
|
||||
{
|
||||
return info->getName().contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptType(const CardInfo *info) const
|
||||
{
|
||||
return info->getCardType().contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptColor(const CardInfo *info) const
|
||||
{
|
||||
QStringList::const_iterator i;
|
||||
bool status;
|
||||
|
||||
status = false;
|
||||
for (i = info->getColors().constBegin(); i != info->getColors().constEnd(); i++)
|
||||
if ((*i).contains(term, Qt::CaseInsensitive)) {
|
||||
status = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptText(const CardInfo *info) const
|
||||
{
|
||||
return info->getText().contains(term, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptSet(const CardInfo *info) const
|
||||
{
|
||||
SetList::const_iterator i;
|
||||
bool status;
|
||||
|
||||
status = false;
|
||||
for (i = info->getSets().constBegin(); i != info->getSets().constEnd(); i++)
|
||||
if ((*i)->getShortName() == term
|
||||
|| (*i)->getLongName().contains(term, Qt::CaseInsensitive)) {
|
||||
status = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool FilterItem::acceptManaCost(const CardInfo *info) const
|
||||
{
|
||||
return (info->getManaCost() == term);
|
||||
}
|
||||
|
||||
bool FilterItem::acceptCardAttr(const CardInfo *info, CardFilter::Attr attr) const
|
||||
{
|
||||
bool status;
|
||||
|
||||
switch (attr) {
|
||||
case CardFilter::AttrName:
|
||||
status = acceptName(info);
|
||||
break;
|
||||
case CardFilter::AttrType:
|
||||
status = acceptType(info);
|
||||
break;
|
||||
case CardFilter::AttrColor:
|
||||
status = acceptColor(info);
|
||||
break;
|
||||
case CardFilter::AttrText:
|
||||
status = acceptText(info);
|
||||
break;
|
||||
case CardFilter::AttrSet:
|
||||
status = acceptSet(info);
|
||||
break;
|
||||
case CardFilter::AttrManaCost:
|
||||
status = acceptManaCost(info);
|
||||
break;
|
||||
default:
|
||||
status = true; /* ignore this attribute */
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* need to define these here to make QT happy, otherwise
|
||||
* moc doesnt find some of the FilterTreeBranch symbols.
|
||||
*/
|
||||
FilterTree::FilterTree() {}
|
||||
FilterTree::~FilterTree() {}
|
||||
|
||||
LogicMap *FilterTree::attrLogicMap(CardFilter::Attr attr)
|
||||
{
|
||||
QList<LogicMap *>::iterator i;
|
||||
int count;
|
||||
|
||||
count = 0;
|
||||
for (i = childNodes.begin(); i != childNodes.end(); i++) {
|
||||
if ((*i)->attr == attr)
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (i == childNodes.end()) {
|
||||
preInsertChild(this, count);
|
||||
i = childNodes.insert(i, new LogicMap(attr, this));
|
||||
postInsertChild(this, count);
|
||||
nodeChanged();
|
||||
}
|
||||
|
||||
return *i;
|
||||
}
|
||||
|
||||
FilterItemList *FilterTree::attrTypeList(CardFilter::Attr attr,
|
||||
CardFilter::Type type)
|
||||
{
|
||||
return attrLogicMap(attr)->typeList(type);
|
||||
}
|
||||
|
||||
int FilterTree::findTermIndex(CardFilter::Attr attr, CardFilter::Type type,
|
||||
const QString &term)
|
||||
{
|
||||
return attrTypeList(attr, type)->termIndex(term);
|
||||
}
|
||||
|
||||
int FilterTree::findTermIndex(const CardFilter *f)
|
||||
{
|
||||
return findTermIndex(f->attr(), f->type(), f->term());
|
||||
}
|
||||
|
||||
FilterTreeNode *FilterTree::termNode(CardFilter::Attr attr, CardFilter::Type type,
|
||||
const QString &term)
|
||||
{
|
||||
return attrTypeList(attr, type)->termNode(term);
|
||||
}
|
||||
|
||||
FilterTreeNode *FilterTree::termNode(const CardFilter *f)
|
||||
{
|
||||
return termNode(f->attr(), f->type(), f->term());
|
||||
}
|
||||
|
||||
FilterTreeNode *FilterTree::attrTypeNode(CardFilter::Attr attr,
|
||||
CardFilter::Type type)
|
||||
{
|
||||
return attrTypeList(attr, type);
|
||||
}
|
||||
|
||||
bool FilterTree::testAttr(const CardInfo *info, const LogicMap *lm) const
|
||||
{
|
||||
const FilterItemList *fil;
|
||||
bool status;
|
||||
|
||||
status = true;
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeAnd);
|
||||
if (fil != NULL && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr))
|
||||
return false;
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeAndNot);
|
||||
if (fil != NULL && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr))
|
||||
return false;
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeOr);
|
||||
if (fil != NULL && fil->isEnabled()) {
|
||||
status = false;
|
||||
// if this is true we can return because it is OR'd with the OrNot list
|
||||
if (fil->testTypeOr(info, lm->attr))
|
||||
return true;
|
||||
}
|
||||
|
||||
fil = lm->findTypeList(CardFilter::TypeOrNot);
|
||||
if (fil != NULL && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr))
|
||||
return true;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool FilterTree::acceptsCard(const CardInfo *info) const
|
||||
{
|
||||
QList<LogicMap *>::const_iterator i;
|
||||
|
||||
for (i = childNodes.constBegin(); i != childNodes.constEnd(); i++)
|
||||
if ((*i)->isEnabled() && !testAttr(info, *i))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FilterTree::clear()
|
||||
{
|
||||
while(childCount() > 0)
|
||||
deleteAt(0);
|
||||
}
|
163
cockatrice/src/filtertree.h
Normal file
163
cockatrice/src/filtertree.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
#ifndef FILTERTREE_H
|
||||
#define FILTERTREE_H
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
#include "cardfilter.h"
|
||||
|
||||
class CardInfo;
|
||||
|
||||
class FilterTreeNode {
|
||||
private:
|
||||
bool enabled;
|
||||
public:
|
||||
FilterTreeNode() : enabled(true) {}
|
||||
virtual bool isEnabled() const { return enabled; }
|
||||
virtual void enable() { enabled = true; nodeChanged(); }
|
||||
virtual void disable() { enabled = false; nodeChanged(); }
|
||||
virtual FilterTreeNode *parent() const { return NULL; }
|
||||
virtual FilterTreeNode *nodeAt(int i) const { return NULL; }
|
||||
virtual void deleteAt(int i) {}
|
||||
virtual int childCount() const { return 0; }
|
||||
virtual int childIndex(const FilterTreeNode *node) const { return -1; }
|
||||
virtual int index() const { return (parent() != NULL)? parent()->childIndex(this) : -1; }
|
||||
virtual QString text() const { return QString(textCStr()); }
|
||||
virtual bool isLeaf() const { return false; }
|
||||
virtual const char *textCStr() const { return ""; }
|
||||
virtual void nodeChanged() const {
|
||||
if (parent() != NULL) parent()->nodeChanged();
|
||||
}
|
||||
virtual void preInsertChild(const FilterTreeNode *p, int i) const {
|
||||
if (parent() != NULL) parent()->preInsertChild(p, i);
|
||||
}
|
||||
virtual void postInsertChild(const FilterTreeNode *p, int i) const {
|
||||
if (parent() != NULL) parent()->postInsertChild(p, i);
|
||||
}
|
||||
virtual void preRemoveChild(const FilterTreeNode *p, int i) const {
|
||||
if (parent() != NULL) parent()->preRemoveChild(p, i);
|
||||
}
|
||||
virtual void postRemoveChild(const FilterTreeNode *p, int i) const {
|
||||
if (parent() != NULL) parent()->postRemoveChild(p, i);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class FilterTreeBranch : public FilterTreeNode {
|
||||
protected:
|
||||
QList<T> childNodes;
|
||||
public:
|
||||
~FilterTreeBranch();
|
||||
FilterTreeNode *nodeAt(int i) const;
|
||||
void deleteAt(int i);
|
||||
int childCount() const { return childNodes.size(); }
|
||||
int childIndex(const FilterTreeNode *node) const;
|
||||
};
|
||||
|
||||
class FilterItemList;
|
||||
class FilterTree;
|
||||
class LogicMap : public FilterTreeBranch<FilterItemList *> {
|
||||
|
||||
private:
|
||||
FilterTree *const p;
|
||||
|
||||
public:
|
||||
const CardFilter::Attr attr;
|
||||
|
||||
LogicMap(CardFilter::Attr a, FilterTree *parent)
|
||||
: attr(a), p(parent) {}
|
||||
const FilterItemList *findTypeList(CardFilter::Type type) const;
|
||||
FilterItemList *typeList(CardFilter::Type type);
|
||||
FilterTreeNode *parent() const;
|
||||
const char* textCStr() const { return CardFilter::attrName(attr); }
|
||||
};
|
||||
|
||||
class FilterItem;
|
||||
class FilterItemList : public FilterTreeBranch<FilterItem *> {
|
||||
private:
|
||||
LogicMap *const p;
|
||||
public:
|
||||
const CardFilter::Type type;
|
||||
|
||||
FilterItemList(CardFilter::Type t, LogicMap *parent)
|
||||
: type(t), p(parent) {}
|
||||
CardFilter::Attr attr() const { return p->attr; }
|
||||
FilterTreeNode *parent() const { return p; }
|
||||
int termIndex(const QString &term) const;
|
||||
FilterTreeNode *termNode(const QString &term);
|
||||
const char *textCStr() const { return CardFilter::typeName(type); }
|
||||
|
||||
bool testTypeAnd(const CardInfo *info, CardFilter::Attr attr) const;
|
||||
bool testTypeAndNot(const CardInfo *info, CardFilter::Attr attr) const;
|
||||
bool testTypeOr(const CardInfo *info, CardFilter::Attr attr) const;
|
||||
bool testTypeOrNot(const CardInfo *info, CardFilter::Attr attr) const;
|
||||
};
|
||||
|
||||
class FilterItem : public FilterTreeNode {
|
||||
private:
|
||||
FilterItemList *const p;
|
||||
public:
|
||||
const QString term;
|
||||
|
||||
FilterItem(QString trm, FilterItemList *parent)
|
||||
: p(parent), term(trm) {}
|
||||
|
||||
CardFilter::Attr attr() const { return p->attr(); }
|
||||
CardFilter::Type type() const { return p->type; }
|
||||
FilterTreeNode *parent() const { return p; }
|
||||
QString text() const { return term; }
|
||||
const char *textCStr() const { return term.toStdString().c_str(); }
|
||||
bool isLeaf() const { return true; }
|
||||
|
||||
bool acceptName(const CardInfo *info) const;
|
||||
bool acceptType(const CardInfo *info) const;
|
||||
bool acceptColor(const CardInfo *info) const;
|
||||
bool acceptText(const CardInfo *info) const;
|
||||
bool acceptSet(const CardInfo *info) const;
|
||||
bool acceptManaCost(const CardInfo *info) const;
|
||||
bool acceptCardAttr(const CardInfo *info, CardFilter::Attr attr) const;
|
||||
};
|
||||
|
||||
class FilterTree : public QObject, public FilterTreeBranch<LogicMap *> {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void preInsertRow(const FilterTreeNode *parent, int i) const;
|
||||
void postInsertRow(const FilterTreeNode *parent, int i) const;
|
||||
void preRemoveRow(const FilterTreeNode *parent, int i) const;
|
||||
void postRemoveRow(const FilterTreeNode *parent, int i) const;
|
||||
void changed() const;
|
||||
|
||||
private:
|
||||
LogicMap *attrLogicMap(CardFilter::Attr attr);
|
||||
FilterItemList *attrTypeList(CardFilter::Attr attr,
|
||||
CardFilter::Type type);
|
||||
|
||||
bool testAttr(const CardInfo *info, const LogicMap *lm) const;
|
||||
|
||||
void nodeChanged() const { emit changed(); }
|
||||
void preInsertChild(const FilterTreeNode *p, int i) const { emit preInsertRow(p, i); }
|
||||
void postInsertChild(const FilterTreeNode *p, int i) const { emit postInsertRow(p, i); }
|
||||
void preRemoveChild(const FilterTreeNode *p, int i) const { emit preRemoveRow(p, i); }
|
||||
void postRemoveChild(const FilterTreeNode *p, int i) const { emit postRemoveRow(p, i); }
|
||||
|
||||
public:
|
||||
FilterTree();
|
||||
~FilterTree();
|
||||
int findTermIndex(CardFilter::Attr attr, CardFilter::Type type,
|
||||
const QString &term);
|
||||
int findTermIndex(const CardFilter *f);
|
||||
FilterTreeNode *termNode(CardFilter::Attr attr, CardFilter::Type type,
|
||||
const QString &term);
|
||||
FilterTreeNode *termNode(const CardFilter *f);
|
||||
FilterTreeNode *attrTypeNode(CardFilter::Attr attr,
|
||||
CardFilter::Type type);
|
||||
const char *textCStr() { return "root"; }
|
||||
int index() const { return 0; }
|
||||
|
||||
bool acceptsCard(const CardInfo *info) const;
|
||||
void clear();
|
||||
};
|
||||
|
||||
#endif
|
272
cockatrice/src/filtertreemodel.cpp
Normal file
272
cockatrice/src/filtertreemodel.cpp
Normal file
|
@ -0,0 +1,272 @@
|
|||
#include <QFont>
|
||||
#include "filtertreemodel.h"
|
||||
#include "filtertree.h"
|
||||
#include "cardfilter.h"
|
||||
|
||||
FilterTreeModel::FilterTreeModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
fTree = new FilterTree;
|
||||
connect(fTree,
|
||||
SIGNAL(preInsertRow(const FilterTreeNode *, int)),
|
||||
this, SLOT(proxyBeginInsertRow(const FilterTreeNode *, int)));
|
||||
connect(fTree,
|
||||
SIGNAL(postInsertRow(const FilterTreeNode *, int)),
|
||||
this, SLOT(proxyEndInsertRow(const FilterTreeNode *, int)));
|
||||
connect(fTree,
|
||||
SIGNAL(preRemoveRow(const FilterTreeNode *, int)),
|
||||
this, SLOT(proxyBeginRemoveRow(const FilterTreeNode *, int)));
|
||||
connect(fTree,
|
||||
SIGNAL(postRemoveRow(const FilterTreeNode *, int)),
|
||||
this, SLOT(proxyEndRemoveRow(const FilterTreeNode *, int)));
|
||||
}
|
||||
|
||||
FilterTreeModel::~FilterTreeModel()
|
||||
{
|
||||
delete fTree;
|
||||
}
|
||||
|
||||
void FilterTreeModel::proxyBeginInsertRow(const FilterTreeNode *node, int i)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = node->index();
|
||||
if (idx >= 0)
|
||||
beginInsertRows(createIndex(idx, 0, (void *) node), i, i);
|
||||
}
|
||||
|
||||
void FilterTreeModel::proxyEndInsertRow(const FilterTreeNode *node, int)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = node->index();
|
||||
if (idx >= 0)
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void FilterTreeModel::proxyBeginRemoveRow(const FilterTreeNode *node, int i)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = node->index();
|
||||
if (idx >= 0)
|
||||
beginRemoveRows(createIndex(idx, 0, (void *) node), i, i);
|
||||
}
|
||||
|
||||
void FilterTreeModel::proxyEndRemoveRow(const FilterTreeNode *node, int)
|
||||
{
|
||||
int idx;
|
||||
|
||||
idx = node->index();
|
||||
if (idx >= 0)
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
FilterTreeNode *FilterTreeModel::indexToNode(const QModelIndex &idx) const
|
||||
{
|
||||
void *ip;
|
||||
FilterTreeNode *node;
|
||||
|
||||
if (!idx.isValid())
|
||||
return fTree;
|
||||
|
||||
ip = idx.internalPointer();
|
||||
if (ip == NULL)
|
||||
return fTree;
|
||||
|
||||
node = static_cast<FilterTreeNode *>(ip);
|
||||
return node;
|
||||
}
|
||||
|
||||
void FilterTreeModel::addFilter(const CardFilter *f)
|
||||
{
|
||||
emit layoutAboutToBeChanged();
|
||||
fTree->termNode(f);
|
||||
emit layoutChanged();
|
||||
}
|
||||
|
||||
int FilterTreeModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
const FilterTreeNode *node;
|
||||
int result;
|
||||
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
node = indexToNode(parent);
|
||||
if (node)
|
||||
result = node->childCount();
|
||||
else
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int FilterTreeModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant FilterTreeModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
const FilterTreeNode *node;
|
||||
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
if (index.column() >= columnCount())
|
||||
return QVariant();
|
||||
|
||||
node = indexToNode(index);
|
||||
if (node == NULL)
|
||||
return QVariant();
|
||||
|
||||
switch (role) {
|
||||
case Qt::FontRole:
|
||||
if (!node->isLeaf()) {
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
return f;
|
||||
}
|
||||
break;
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::StatusTipRole:
|
||||
case Qt::WhatsThisRole:
|
||||
if(!node->isLeaf())
|
||||
return tr(node->textCStr());
|
||||
else
|
||||
return node->text();
|
||||
case Qt::CheckStateRole:
|
||||
if (node->isEnabled())
|
||||
return Qt::Checked;
|
||||
else
|
||||
return Qt::Unchecked;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool FilterTreeModel::setData(const QModelIndex &index,
|
||||
const QVariant &value, int role)
|
||||
{
|
||||
FilterTreeNode *node;
|
||||
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
if (index.column() >= columnCount())
|
||||
return false;
|
||||
if (role != Qt::CheckStateRole )
|
||||
return false;
|
||||
|
||||
node = indexToNode(index);
|
||||
if (node == NULL || node == fTree)
|
||||
return false;
|
||||
|
||||
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
|
||||
if (state == Qt::Checked)
|
||||
node->enable();
|
||||
else
|
||||
node->disable();
|
||||
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FilterTreeModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
const FilterTreeNode *node;
|
||||
Qt::ItemFlags result;
|
||||
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
|
||||
node = indexToNode(index);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
result = Qt::ItemIsEnabled;
|
||||
if (node == fTree)
|
||||
return result;
|
||||
|
||||
result |= Qt::ItemIsSelectable;
|
||||
result |= Qt::ItemIsUserCheckable;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QModelIndex FilterTreeModel::nodeIndex(const FilterTreeNode *node, int row, int column) const
|
||||
{
|
||||
FilterTreeNode *child;
|
||||
|
||||
if (column > 0 || row >= node->childCount())
|
||||
return QModelIndex();
|
||||
|
||||
child = node->nodeAt(row);
|
||||
return createIndex(row, column, child);
|
||||
}
|
||||
|
||||
QModelIndex FilterTreeModel::index(int row, int column,
|
||||
const QModelIndex &parent) const
|
||||
{
|
||||
const FilterTreeNode *node;
|
||||
|
||||
if (!hasIndex(row, column, parent))
|
||||
return QModelIndex();
|
||||
|
||||
node = indexToNode(parent);
|
||||
if (node == NULL)
|
||||
return QModelIndex();
|
||||
|
||||
return nodeIndex(node, row, column);
|
||||
}
|
||||
|
||||
QModelIndex FilterTreeModel::parent(const QModelIndex &ind) const
|
||||
{
|
||||
const FilterTreeNode *node;
|
||||
FilterTreeNode *parent;
|
||||
int row;
|
||||
QModelIndex idx;
|
||||
|
||||
if (!ind.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
node = indexToNode(ind);
|
||||
if (node == NULL || node == fTree)
|
||||
return QModelIndex();
|
||||
|
||||
parent = node->parent();
|
||||
if (parent) {
|
||||
row = parent->index();
|
||||
if (row < 0)
|
||||
return QModelIndex();
|
||||
idx = createIndex(row, 0, parent);
|
||||
return idx;
|
||||
}
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
bool FilterTreeModel::removeRows(int row, int count, const QModelIndex & parent)
|
||||
{
|
||||
FilterTreeNode *node;
|
||||
int i, last;
|
||||
|
||||
last = row+count-1;
|
||||
if (!parent.isValid() || count < 1 || row < 0)
|
||||
return false;
|
||||
|
||||
node = indexToNode(parent);
|
||||
if (node == NULL || last >= node->childCount())
|
||||
return false;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
node->deleteAt(row);
|
||||
|
||||
if (node != fTree && node->childCount() < 1)
|
||||
return removeRow(parent.row(), parent.parent());
|
||||
|
||||
return true;
|
||||
}
|
44
cockatrice/src/filtertreemodel.h
Normal file
44
cockatrice/src/filtertreemodel.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef FILTERTREEMODEL_H
|
||||
#define FILTERTREEMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
class FilterTree;
|
||||
class CardFilter;
|
||||
class FilterTreeNode;
|
||||
|
||||
class FilterTreeModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
private:
|
||||
FilterTree *fTree;
|
||||
|
||||
public slots:
|
||||
void addFilter(const CardFilter *f);
|
||||
|
||||
private slots:
|
||||
void proxyBeginInsertRow(const FilterTreeNode *, int);
|
||||
void proxyEndInsertRow(const FilterTreeNode *, int);
|
||||
void proxyBeginRemoveRow(const FilterTreeNode *, int);
|
||||
void proxyEndRemoveRow(const FilterTreeNode *, int);
|
||||
|
||||
private:
|
||||
FilterTreeNode *indexToNode(const QModelIndex &idx) const;
|
||||
QModelIndex nodeIndex(const FilterTreeNode *node, int row, int column) const;
|
||||
|
||||
public:
|
||||
FilterTreeModel(QObject *parent = 0);
|
||||
~FilterTreeModel();
|
||||
FilterTree *filterTree() const { return fTree; }
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value,
|
||||
int role = Qt::EditRole);
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
QModelIndex parent(const QModelIndex &ind) const;
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent) const;
|
||||
bool removeRows(int row, int count, const QModelIndex & parent);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -180,9 +180,9 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sourc
|
|||
if (!gameTypeFilter.isEmpty() && gameTypes.intersect(gameTypeFilter).isEmpty())
|
||||
return false;
|
||||
|
||||
if ((maxPlayersFilterMin != -1) && (game.max_players() < maxPlayersFilterMin))
|
||||
if ((maxPlayersFilterMin != -1) && ((int)game.max_players() < maxPlayersFilterMin))
|
||||
return false;
|
||||
if ((maxPlayersFilterMax != -1) && (game.max_players() > maxPlayersFilterMax))
|
||||
if ((maxPlayersFilterMax != -1) && ((int)game.max_players() > maxPlayersFilterMax))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -75,7 +75,7 @@ QRectF HandZone::boundingRect() const
|
|||
return QRectF(0, 0, 100, zoneHeight);
|
||||
}
|
||||
|
||||
void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void HandZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
if (bgPixmap.isNull())
|
||||
painter->fillRect(boundingRect(), Qt::darkGreen);
|
||||
|
|
65
cockatrice/src/keysignals.cpp
Normal file
65
cockatrice/src/keysignals.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "keysignals.h"
|
||||
#include <QKeyEvent>
|
||||
|
||||
bool KeySignals::eventFilter(QObject * /*object*/, QEvent *event) {
|
||||
QKeyEvent *kevent;
|
||||
|
||||
if(event->type() != QEvent::KeyPress)
|
||||
return false;
|
||||
|
||||
kevent = static_cast<QKeyEvent *>(event);
|
||||
switch(kevent->key()) {
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
if (kevent->modifiers().testFlag(Qt::AltModifier)
|
||||
&& kevent->modifiers().testFlag(Qt::ControlModifier) )
|
||||
emit onCtrlAltEnter();
|
||||
else if (kevent->modifiers() & Qt::ControlModifier)
|
||||
emit onCtrlEnter();
|
||||
else
|
||||
emit onEnter();
|
||||
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
emit onRight();
|
||||
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
emit onLeft();
|
||||
|
||||
break;
|
||||
case Qt::Key_Delete:
|
||||
case Qt::Key_Backspace:
|
||||
emit onDelete();
|
||||
|
||||
break;
|
||||
case Qt::Key_Minus:
|
||||
if (kevent->modifiers().testFlag(Qt::AltModifier)
|
||||
&& kevent->modifiers().testFlag(Qt::ControlModifier) )
|
||||
emit onCtrlAltMinus();
|
||||
|
||||
break;
|
||||
case Qt::Key_Equal:
|
||||
if (kevent->modifiers().testFlag(Qt::AltModifier)
|
||||
&& kevent->modifiers().testFlag(Qt::ControlModifier) )
|
||||
emit onCtrlAltEqual();
|
||||
|
||||
break;
|
||||
case Qt::Key_BracketLeft:
|
||||
if (kevent->modifiers().testFlag(Qt::AltModifier)
|
||||
&& kevent->modifiers().testFlag(Qt::ControlModifier) )
|
||||
emit onCtrlAltLBracket();
|
||||
|
||||
break;
|
||||
case Qt::Key_BracketRight:
|
||||
if (kevent->modifiers().testFlag(Qt::AltModifier)
|
||||
&& kevent->modifiers().testFlag(Qt::ControlModifier) )
|
||||
emit onCtrlAltRBracket();
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
26
cockatrice/src/keysignals.h
Normal file
26
cockatrice/src/keysignals.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef KEYSIGNALS_H
|
||||
#define KEYSIGNALS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QEvent>
|
||||
|
||||
class KeySignals : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void onEnter();
|
||||
void onCtrlEnter();
|
||||
void onCtrlAltEnter();
|
||||
void onLeft();
|
||||
void onRight();
|
||||
void onDelete();
|
||||
void onCtrlAltMinus();
|
||||
void onCtrlAltEqual();
|
||||
void onCtrlAltLBracket();
|
||||
void onCtrlAltRBracket();
|
||||
|
||||
protected:
|
||||
virtual bool eventFilter(QObject *, QEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -19,6 +19,8 @@
|
|||
***************************************************************************/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QTextCodec>
|
||||
#include <QtPlugin>
|
||||
#include <QTranslator>
|
||||
|
@ -28,7 +30,6 @@
|
|||
#include <QIcon>
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "window_main.h"
|
||||
|
@ -56,11 +57,11 @@ QString translationPath = QString();
|
|||
|
||||
void myMessageOutput(QtMsgType /*type*/, const char *msg)
|
||||
{
|
||||
static FILE *f = NULL;
|
||||
if (!f)
|
||||
f = fopen("qdebug.txt", "w");
|
||||
fprintf(f, "%s\n", msg);
|
||||
fflush(f);
|
||||
QFile file("qdebug.txt");
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
|
||||
QTextStream out(&file);
|
||||
out << msg << endl;
|
||||
file.close();
|
||||
}
|
||||
|
||||
void installNewTranslator()
|
||||
|
@ -115,9 +116,9 @@ int main(int argc, char *argv[])
|
|||
qtTranslator = new QTranslator;
|
||||
translator = new QTranslator;
|
||||
installNewTranslator();
|
||||
|
||||
|
||||
qsrand(QDateTime::currentDateTime().toTime_t());
|
||||
|
||||
|
||||
bool startMainProgram = true;
|
||||
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
if (!db->getLoadSuccess())
|
||||
|
|
31
cockatrice/src/pending_command.cpp
Normal file
31
cockatrice/src/pending_command.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "pending_command.h"
|
||||
|
||||
PendingCommand::PendingCommand(const CommandContainer &_commandContainer, QVariant _extraData)
|
||||
: commandContainer(_commandContainer), extraData(_extraData), ticks(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CommandContainer & PendingCommand::getCommandContainer()
|
||||
{
|
||||
return commandContainer;
|
||||
}
|
||||
|
||||
void PendingCommand::setExtraData(const QVariant &_extraData) {
|
||||
extraData = _extraData;
|
||||
}
|
||||
|
||||
QVariant PendingCommand::getExtraData() const {
|
||||
return extraData;
|
||||
}
|
||||
|
||||
void PendingCommand::processResponse(const Response &response)
|
||||
{
|
||||
emit finished(response, commandContainer, extraData);
|
||||
emit finished(response.response_code());
|
||||
}
|
||||
|
||||
int PendingCommand::tick()
|
||||
{
|
||||
return ++ticks;
|
||||
}
|
|
@ -15,17 +15,12 @@ private:
|
|||
QVariant extraData;
|
||||
int ticks;
|
||||
public:
|
||||
PendingCommand(const CommandContainer &_commandContainer, QVariant _extraData = QVariant())
|
||||
: commandContainer(_commandContainer), extraData(_extraData), ticks(0) { }
|
||||
CommandContainer &getCommandContainer() { return commandContainer; }
|
||||
void setExtraData(const QVariant &_extraData) { extraData = _extraData; }
|
||||
QVariant getExtraData() const { return extraData; }
|
||||
void processResponse(const Response &response)
|
||||
{
|
||||
emit finished(response, commandContainer, extraData);
|
||||
emit finished(response.response_code());
|
||||
}
|
||||
int tick() { return ++ticks; }
|
||||
PendingCommand(const CommandContainer &_commandContainer, QVariant _extraData = QVariant());
|
||||
CommandContainer &getCommandContainer();
|
||||
void setExtraData(const QVariant &_extraData);
|
||||
QVariant getExtraData() const;
|
||||
void processResponse(const Response &response);
|
||||
int tick();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
#include <QPen>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
#include "phasestoolbar.h"
|
||||
#include "pixmapgenerator.h"
|
||||
|
||||
|
@ -85,7 +88,7 @@ void PhaseButton::mousePressEvent(QGraphicsSceneMouseEvent * /*event*/)
|
|||
emit clicked();
|
||||
}
|
||||
|
||||
void PhaseButton::mouseDoubleClickEvent(QGraphicsSceneMouseEvent */*event*/)
|
||||
void PhaseButton::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
triggerDoubleClickAction();
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ void PileZone::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|||
setCursor(Qt::OpenHandCursor);
|
||||
}
|
||||
|
||||
void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent */*event*/)
|
||||
void PileZone::mouseReleaseEvent(QGraphicsSceneMouseEvent * /*event*/)
|
||||
{
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
#include "pb/serverinfo_user.pb.h"
|
||||
#include <QPainter>
|
||||
#include <QSvgRenderer>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
#include <QDebug>
|
||||
|
||||
QMap<QString, QPixmap> PhasePixmapGenerator::pmCache;
|
||||
|
|
|
@ -85,7 +85,7 @@ void PlayerArea::updateBgPixmap()
|
|||
update();
|
||||
}
|
||||
|
||||
void PlayerArea::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void PlayerArea::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
painter->fillRect(bRect, bgPixmapBrush);
|
||||
}
|
||||
|
@ -1430,7 +1430,7 @@ QRectF Player::boundingRect() const
|
|||
return bRect;
|
||||
}
|
||||
|
||||
void Player::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void Player::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#include <QPainter>
|
||||
#include <QPixmapCache>
|
||||
#include <QDebug>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
|
||||
PlayerCounter::PlayerCounter(Player *_player, int _id, const QString &_name, int _value, QGraphicsItem *parent)
|
||||
: AbstractCounter(_player, _id, _name, false, _value, parent)
|
||||
|
@ -87,7 +90,8 @@ void PlayerTarget::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*o
|
|||
cachedPixmap = QPixmap(translatedSize.width(), translatedSize.height());
|
||||
|
||||
QPainter tempPainter(&cachedPixmap);
|
||||
QRadialGradient grad(translatedRect.center(), sqrt(translatedSize.width() * translatedSize.width() + translatedSize.height() * translatedSize.height()) / 2);
|
||||
// pow(foo, 0.5) equals to sqrt(foo), but using sqrt(foo) in this context will produce a compile error with MSVC++
|
||||
QRadialGradient grad(translatedRect.center(), pow(translatedSize.width() * translatedSize.width() + translatedSize.height() * translatedSize.height(), 0.5) / 2);
|
||||
grad.setColorAt(1, Qt::black);
|
||||
grad.setColorAt(0, QColor(180, 180, 180));
|
||||
tempPainter.fillRect(QRectF(0, 0, translatedSize.width(), translatedSize.height()), grad);
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QTimer>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
|
||||
ReplayTimelineWidget::ReplayTimelineWidget(QWidget *parent)
|
||||
: QWidget(parent), maxBinValue(1), maxTime(1), timeScaleFactor(1.0), currentTime(0), currentEvent(0)
|
||||
|
|
12
cockatrice/src/round.h
Normal file
12
cockatrice/src/round.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef MSVC_ROUND_FIX
|
||||
#define MSVC_ROUND_FIX
|
||||
/**
|
||||
* This helper function exists only because MS VC++ 2010 does not support round() in
|
||||
* <cmath>. round() works with g++ and clang++ but is formally a C++11 extension.
|
||||
* So this file exists for MS VC++ only.
|
||||
*/
|
||||
inline double round(double val) {
|
||||
return floor(val + 0.5);
|
||||
}
|
||||
|
||||
#endif /* MSVC_ROUND_FIX */
|
|
@ -4,23 +4,27 @@
|
|||
SettingsCache::SettingsCache()
|
||||
{
|
||||
settings = new QSettings(this);
|
||||
|
||||
|
||||
lang = settings->value("personal/lang").toString();
|
||||
|
||||
|
||||
deckPath = settings->value("paths/decks").toString();
|
||||
replaysPath = settings->value("paths/replays").toString();
|
||||
picsPath = settings->value("paths/pics").toString();
|
||||
cardDatabasePath = settings->value("paths/carddatabase").toString();
|
||||
tokenDatabasePath = settings->value("paths/tokendatabase").toString();
|
||||
|
||||
|
||||
handBgPath = settings->value("zonebg/hand").toString();
|
||||
stackBgPath = settings->value("zonebg/stack").toString();
|
||||
tableBgPath = settings->value("zonebg/table").toString();
|
||||
playerBgPath = settings->value("zonebg/playerarea").toString();
|
||||
cardBackPicturePath = settings->value("paths/cardbackpicture").toString();
|
||||
|
||||
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
|
||||
|
||||
picDownload = settings->value("personal/picturedownload", true).toBool();
|
||||
picDownloadHq = settings->value("personal/picturedownloadhq", false).toBool();
|
||||
picUrl = settings->value("personal/picUrl", PIC_URL_DEFAULT).toString();
|
||||
picUrlHq = settings->value("personal/picUrlHq", PIC_URL_HQ_DEFAULT).toString();
|
||||
|
||||
mainWindowGeometry = settings->value("interface/main_window_geometry").toByteArray();
|
||||
notificationsEnabled = settings->value("interface/notificationsenabled", true).toBool();
|
||||
doubleClickToPlay = settings->value("interface/doubleclicktoplay", true).toBool();
|
||||
playToStack = settings->value("interface/playtostack", false).toBool();
|
||||
|
@ -31,15 +35,15 @@ SettingsCache::SettingsCache()
|
|||
invertVerticalCoordinate = settings->value("table/invert_vertical", false).toBool();
|
||||
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 5).toInt();
|
||||
tapAnimation = settings->value("cards/tapanimation", true).toBool();
|
||||
|
||||
|
||||
zoneViewSortByName = settings->value("zoneview/sortbyname", true).toBool();
|
||||
zoneViewSortByType = settings->value("zoneview/sortbytype", true).toBool();
|
||||
|
||||
|
||||
soundEnabled = settings->value("sound/enabled", false).toBool();
|
||||
soundPath = settings->value("sound/path").toString();
|
||||
|
||||
|
||||
priceTagFeature = settings->value("deckeditor/pricetags", false).toBool();
|
||||
|
||||
|
||||
ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool();
|
||||
}
|
||||
|
||||
|
@ -125,6 +129,25 @@ void SettingsCache::setPicDownload(int _picDownload)
|
|||
emit picDownloadChanged();
|
||||
}
|
||||
|
||||
void SettingsCache::setPicDownloadHq(int _picDownloadHq)
|
||||
{
|
||||
picDownloadHq = _picDownloadHq;
|
||||
settings->setValue("personal/picturedownloadhq", picDownloadHq);
|
||||
emit picDownloadHqChanged();
|
||||
}
|
||||
|
||||
void SettingsCache::setPicUrl(const QString &_picUrl)
|
||||
{
|
||||
picUrl = _picUrl;
|
||||
settings->setValue("personal/picUrl", picUrl);
|
||||
}
|
||||
|
||||
void SettingsCache::setPicUrlHq(const QString &_picUrlHq)
|
||||
{
|
||||
picUrlHq = _picUrlHq;
|
||||
settings->setValue("personal/picUrlHq", picUrlHq);
|
||||
}
|
||||
|
||||
void SettingsCache::setNotificationsEnabled(int _notificationsEnabled)
|
||||
{
|
||||
notificationsEnabled = _notificationsEnabled;
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#define PIC_URL_DEFAULT "http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card"
|
||||
#define PIC_URL_HQ_DEFAULT "http://mtgimage.com/multiverseid/!cardid!.jpg"
|
||||
|
||||
class QSettings;
|
||||
|
||||
class SettingsCache : public QObject {
|
||||
|
@ -18,6 +21,7 @@ signals:
|
|||
void playerBgPathChanged();
|
||||
void cardBackPicturePathChanged();
|
||||
void picDownloadChanged();
|
||||
void picDownloadHqChanged();
|
||||
void displayCardNamesChanged();
|
||||
void horizontalHandChanged();
|
||||
void invertVerticalCoordinateChanged();
|
||||
|
@ -33,6 +37,7 @@ private:
|
|||
QString deckPath, replaysPath, picsPath, cardDatabasePath, tokenDatabasePath;
|
||||
QString handBgPath, stackBgPath, tableBgPath, playerBgPath, cardBackPicturePath;
|
||||
bool picDownload;
|
||||
bool picDownloadHq;
|
||||
bool notificationsEnabled;
|
||||
bool doubleClickToPlay;
|
||||
bool playToStack;
|
||||
|
@ -48,6 +53,8 @@ private:
|
|||
QString soundPath;
|
||||
bool priceTagFeature;
|
||||
bool ignoreUnregisteredUsers;
|
||||
QString picUrl;
|
||||
QString picUrlHq;
|
||||
public:
|
||||
SettingsCache();
|
||||
const QByteArray &getMainWindowGeometry() const { return mainWindowGeometry; }
|
||||
|
@ -63,6 +70,7 @@ public:
|
|||
QString getPlayerBgPath() const { return playerBgPath; }
|
||||
QString getCardBackPicturePath() const { return cardBackPicturePath; }
|
||||
bool getPicDownload() const { return picDownload; }
|
||||
bool getPicDownloadHq() const { return picDownloadHq; }
|
||||
bool getNotificationsEnabled() const { return notificationsEnabled; }
|
||||
bool getDoubleClickToPlay() const { return doubleClickToPlay; }
|
||||
bool getPlayToStack() const { return playToStack; }
|
||||
|
@ -79,6 +87,8 @@ public:
|
|||
QString getSoundPath() const { return soundPath; }
|
||||
bool getPriceTagFeature() const { return priceTagFeature; }
|
||||
bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; }
|
||||
QString getPicUrl() const { return picUrl; }
|
||||
QString getPicUrlHq() const { return picUrlHq; }
|
||||
public slots:
|
||||
void setMainWindowGeometry(const QByteArray &_mainWindowGeometry);
|
||||
void setLang(const QString &_lang);
|
||||
|
@ -93,6 +103,7 @@ public slots:
|
|||
void setPlayerBgPath(const QString &_playerBgPath);
|
||||
void setCardBackPicturePath(const QString &_cardBackPicturePath);
|
||||
void setPicDownload(int _picDownload);
|
||||
void setPicDownloadHq(int _picDownloadHq);
|
||||
void setNotificationsEnabled(int _notificationsEnabled);
|
||||
void setDoubleClickToPlay(int _doubleClickToPlay);
|
||||
void setPlayToStack(int _playToStack);
|
||||
|
@ -109,6 +120,8 @@ public slots:
|
|||
void setSoundPath(const QString &_soundPath);
|
||||
void setPriceTagFeature(int _priceTagFeature);
|
||||
void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers);
|
||||
void setPicUrl(const QString &_picUrl);
|
||||
void setPicUrlHq(const QString &_picUrlHq);
|
||||
};
|
||||
|
||||
extern SettingsCache *settingsCache;
|
||||
|
|
|
@ -46,7 +46,7 @@ QRectF StackZone::boundingRect() const
|
|||
return QRectF(0, 0, 100, zoneHeight);
|
||||
}
|
||||
|
||||
void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void StackZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
if (bgPixmap.isNull())
|
||||
painter->fillRect(boundingRect(), QColor(113, 43, 43));
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "carddatabasemodel.h"
|
||||
#include "decklistmodel.h"
|
||||
#include "cardinfowidget.h"
|
||||
#include "dlg_cardsearch.h"
|
||||
#include "dlg_load_deck_from_clipboard.h"
|
||||
#include "dlg_edit_tokens.h"
|
||||
#include "main.h"
|
||||
|
@ -34,6 +33,9 @@
|
|||
#include "pending_command.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/command_deck_upload.pb.h"
|
||||
#include "filtertreemodel.h"
|
||||
#include "cardframe.h"
|
||||
#include "filterbuilder.h"
|
||||
|
||||
void SearchLineEdit::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
|
@ -45,9 +47,6 @@ void SearchLineEdit::keyPressEvent(QKeyEvent *event)
|
|||
TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
||||
: Tab(_tabSupervisor, parent), modified(false)
|
||||
{
|
||||
aSearch = new QAction(QString(), this);
|
||||
aSearch->setIcon(QIcon(":/resources/icon_search.svg"));
|
||||
connect(aSearch, SIGNAL(triggered()), this, SLOT(actSearch()));
|
||||
aClearSearch = new QAction(QString(), this);
|
||||
aClearSearch->setIcon(QIcon(":/resources/icon_clearsearch.svg"));
|
||||
connect(aClearSearch, SIGNAL(triggered()), this, SLOT(actClearSearch()));
|
||||
|
@ -55,25 +54,36 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
|||
searchLabel = new QLabel();
|
||||
searchEdit = new SearchLineEdit;
|
||||
searchLabel->setBuddy(searchEdit);
|
||||
setFocusProxy(searchEdit);
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
searchEdit->installEventFilter(&searchKeySignals);
|
||||
connect(searchEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateSearch(const QString &)));
|
||||
connect(searchEdit, SIGNAL(returnPressed()), this, SLOT(actAddCard()));
|
||||
QToolButton *searchButton = new QToolButton;
|
||||
searchButton->setDefaultAction(aSearch);
|
||||
QToolButton *clearSearchButton = new QToolButton;
|
||||
clearSearchButton->setDefaultAction(aClearSearch);
|
||||
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(searchLabel);
|
||||
searchLayout->addWidget(searchEdit);
|
||||
searchLayout->addWidget(searchButton);
|
||||
searchLayout->addWidget(clearSearchButton);
|
||||
|
||||
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);
|
||||
|
@ -89,21 +99,33 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
|||
leftFrame->addLayout(searchLayout);
|
||||
leftFrame->addWidget(databaseView);
|
||||
|
||||
cardInfo = new CardInfoWidget(CardInfoWidget::ModeDeckEditor);
|
||||
cardInfo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||
cardInfo = new CardFrame(250, 356);
|
||||
aCardTextOnly = new QAction(QString(), this);
|
||||
aCardTextOnly->setCheckable(true);
|
||||
connect(aCardTextOnly, SIGNAL(triggered()), cardInfo, SLOT(toggleCardTextOnly()));
|
||||
|
||||
QToolBar *verticalToolBar = new QToolBar;
|
||||
verticalToolBar->setOrientation(Qt::Vertical);
|
||||
verticalToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
verticalToolBar->setIconSize(QSize(24, 24));
|
||||
QHBoxLayout *verticalToolBarLayout = new QHBoxLayout;
|
||||
verticalToolBarLayout->addStretch();
|
||||
verticalToolBarLayout->addWidget(verticalToolBar);
|
||||
verticalToolBarLayout->addStretch();
|
||||
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;
|
||||
filterBuilder->setMaximumWidth(250);
|
||||
connect(filterBuilder, SIGNAL(add(const CardFilter *)), filterModel, SLOT(addFilter(const CardFilter *)));
|
||||
|
||||
QVBoxLayout *filter = new QVBoxLayout;
|
||||
filter->addWidget(filterBuilder, 0, Qt::AlignTop);
|
||||
filter->addWidget(filterView, 10);
|
||||
|
||||
QVBoxLayout *middleFrame = new QVBoxLayout;
|
||||
middleFrame->addWidget(cardInfo, 10);
|
||||
middleFrame->addLayout(verticalToolBarLayout);
|
||||
middleFrame->addWidget(cardInfo, 0, Qt::AlignTop);
|
||||
middleFrame->addLayout(filter, 10);
|
||||
|
||||
deckModel = new DeckListModel(this);
|
||||
connect(deckModel, SIGNAL(deckHashChanged()), this, SLOT(updateHash()));
|
||||
|
@ -113,7 +135,14 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
|||
deckView->setSortingEnabled(true);
|
||||
deckView->sortByColumn(1, Qt::AscendingOrder);
|
||||
deckView->header()->setResizeMode(QHeaderView::ResizeToContents);
|
||||
deckView->installEventFilter(&deckViewKeySignals);
|
||||
connect(deckView->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(updateCardInfoRight(const QModelIndex &, const QModelIndex &)));
|
||||
connect(&deckViewKeySignals, SIGNAL(onEnter()), this, SLOT(actIncrement()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onCtrlAltEqual()), this, SLOT(actIncrement()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onCtrlAltMinus()), this, SLOT(actDecrement()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onRight()), this, SLOT(actIncrement()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onLeft()), this, SLOT(actDecrement()));
|
||||
connect(&deckViewKeySignals, SIGNAL(onDelete()), this, SLOT(actRemoveCard()));
|
||||
|
||||
nameLabel = new QLabel();
|
||||
nameEdit = new QLineEdit;
|
||||
|
@ -133,36 +162,36 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
|||
|
||||
grid->addWidget(commentsLabel, 1, 0);
|
||||
grid->addWidget(commentsEdit, 1, 1);
|
||||
|
||||
|
||||
grid->addWidget(hashLabel1, 2, 0);
|
||||
grid->addWidget(hashLabel, 2, 1);
|
||||
|
||||
// Update price
|
||||
aUpdatePrices = new QAction(QString(), this);
|
||||
aUpdatePrices->setIcon(QIcon(":/resources/icon_update.png"));
|
||||
connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices()));
|
||||
// Update price
|
||||
aUpdatePrices = new QAction(QString(), this);
|
||||
aUpdatePrices->setIcon(QIcon(":/resources/icon_update.png"));
|
||||
connect(aUpdatePrices, SIGNAL(triggered()), this, SLOT(actUpdatePrices()));
|
||||
if (!settingsCache->getPriceTagFeature())
|
||||
aUpdatePrices->setVisible(false);
|
||||
|
||||
QToolBar *deckToolBar = new QToolBar;
|
||||
deckToolBar->setOrientation(Qt::Vertical);
|
||||
deckToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
deckToolBar->setIconSize(QSize(24, 24));
|
||||
deckToolBar->addAction(aUpdatePrices);
|
||||
QHBoxLayout *deckToolbarLayout = new QHBoxLayout;
|
||||
deckToolbarLayout->addStretch();
|
||||
deckToolbarLayout->addWidget(deckToolBar);
|
||||
deckToolbarLayout->addStretch();
|
||||
|
||||
QToolBar *deckToolBar = new QToolBar;
|
||||
deckToolBar->setOrientation(Qt::Vertical);
|
||||
deckToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
deckToolBar->setIconSize(QSize(24, 24));
|
||||
deckToolBar->addAction(aUpdatePrices);
|
||||
QHBoxLayout *deckToolbarLayout = new QHBoxLayout;
|
||||
deckToolbarLayout->addStretch();
|
||||
deckToolbarLayout->addWidget(deckToolBar);
|
||||
deckToolbarLayout->addStretch();
|
||||
|
||||
QVBoxLayout *rightFrame = new QVBoxLayout;
|
||||
rightFrame->addLayout(grid);
|
||||
rightFrame->addWidget(deckView, 10);
|
||||
rightFrame->addLayout(deckToolbarLayout);
|
||||
rightFrame->addLayout(deckToolbarLayout);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
mainLayout->addLayout(leftFrame, 10);
|
||||
mainLayout->addLayout(middleFrame);
|
||||
mainLayout->addLayout(rightFrame, 10);
|
||||
mainLayout->addLayout(rightFrame);
|
||||
setLayout(mainLayout);
|
||||
|
||||
aNewDeck = new QAction(QString(), this);
|
||||
|
@ -216,34 +245,32 @@ TabDeckEditor::TabDeckEditor(TabSupervisor *_tabSupervisor, QWidget *parent)
|
|||
dbMenu->addAction(aEditSets);
|
||||
dbMenu->addAction(aEditTokens);
|
||||
dbMenu->addSeparator();
|
||||
dbMenu->addAction(aSearch);
|
||||
dbMenu->addAction(aClearSearch);
|
||||
dbMenu->addAction(aCardTextOnly);
|
||||
addTabMenu(dbMenu);
|
||||
|
||||
aAddCard = new QAction(QString(), this);
|
||||
aAddCard->setIcon(QIcon(":/resources/arrow_right_green.svg"));
|
||||
connect(aAddCard, SIGNAL(triggered()), this, SLOT(actAddCard()));
|
||||
aAddCardToSideboard = new QAction(QString(), this);
|
||||
aAddCardToSideboard->setIcon(QIcon(":/resources/add_to_sideboard.svg"));
|
||||
aAddCardToSideboard->setIcon(QIcon(":/resources/add_to_sideboard.svg"));
|
||||
connect(aAddCardToSideboard, SIGNAL(triggered()), this, SLOT(actAddCardToSideboard()));
|
||||
aRemoveCard = new QAction(QString(), this);
|
||||
aRemoveCard->setIcon(QIcon(":/resources/remove_row.svg"));
|
||||
aRemoveCard->setIcon(QIcon(":/resources/remove_row.svg"));
|
||||
connect(aRemoveCard, SIGNAL(triggered()), this, SLOT(actRemoveCard()));
|
||||
aIncrement = new QAction(QString(), this);
|
||||
aIncrement->setIcon(QIcon(":/resources/increment.svg"));
|
||||
aIncrement->setIcon(QIcon(":/resources/increment.svg"));
|
||||
connect(aIncrement, SIGNAL(triggered()), this, SLOT(actIncrement()));
|
||||
aDecrement = new QAction(QString(), this);
|
||||
aDecrement->setIcon(QIcon(":/resources/decrement.svg"));
|
||||
aDecrement->setIcon(QIcon(":/resources/decrement.svg"));
|
||||
connect(aDecrement, SIGNAL(triggered()), this, SLOT(actDecrement()));
|
||||
|
||||
verticalToolBar->addAction(aAddCard);
|
||||
verticalToolBar->addAction(aAddCardToSideboard);
|
||||
verticalToolBar->addAction(aRemoveCard);
|
||||
verticalToolBar->addAction(aIncrement);
|
||||
verticalToolBar->addAction(aDecrement);
|
||||
verticalToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
dlgCardSearch = new DlgCardSearch(this);
|
||||
deckEditToolBar->addAction(aAddCard);
|
||||
deckEditToolBar->addAction(aAddCardToSideboard);
|
||||
deckEditToolBar->addAction(aRemoveCard);
|
||||
deckEditToolBar->addAction(aIncrement);
|
||||
deckEditToolBar->addAction(aDecrement);
|
||||
deckEditToolBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
retranslateUi();
|
||||
|
||||
|
@ -257,7 +284,7 @@ TabDeckEditor::~TabDeckEditor()
|
|||
|
||||
void TabDeckEditor::retranslateUi()
|
||||
{
|
||||
aSearch->setText(tr("&Search..."));
|
||||
aCardTextOnly->setText(tr("Show card text only"));
|
||||
aClearSearch->setText(tr("&Clear search"));
|
||||
searchLabel->setText(tr("&Search for:"));
|
||||
|
||||
|
@ -266,11 +293,11 @@ void TabDeckEditor::retranslateUi()
|
|||
hashLabel1->setText(tr("Hash:"));
|
||||
|
||||
aUpdatePrices->setText(tr("&Update prices"));
|
||||
aUpdatePrices->setShortcut(tr("Ctrl+U"));
|
||||
|
||||
aNewDeck->setText(tr("&New deck"));
|
||||
aLoadDeck->setText(tr("&Load deck..."));
|
||||
aSaveDeck->setText(tr("&Save deck"));
|
||||
aUpdatePrices->setShortcut(tr("Ctrl+U"));
|
||||
|
||||
aNewDeck->setText(tr("&New deck"));
|
||||
aLoadDeck->setText(tr("&Load deck..."));
|
||||
aSaveDeck->setText(tr("&Save deck"));
|
||||
aSaveDeckAs->setText(tr("Save deck &as..."));
|
||||
aLoadDeckFromClipboard->setText(tr("Load deck from cl&ipboard..."));
|
||||
aSaveDeckToClipboard->setText(tr("Save deck to clip&board"));
|
||||
|
@ -280,9 +307,8 @@ void TabDeckEditor::retranslateUi()
|
|||
aClose->setShortcut(tr("Ctrl+Q"));
|
||||
|
||||
aAddCard->setText(tr("Add card to &maindeck"));
|
||||
aAddCard->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Return")) << QKeySequence(tr("Enter")));
|
||||
aAddCardToSideboard->setText(tr("Add card to &sideboard"));
|
||||
aAddCardToSideboard->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Return")) << QKeySequence(tr("Ctrl+Enter")));
|
||||
|
||||
aRemoveCard->setText(tr("&Remove row"));
|
||||
aRemoveCard->setShortcut(tr("Del"));
|
||||
aIncrement->setText(tr("&Increment number"));
|
||||
|
@ -499,17 +525,6 @@ void TabDeckEditor::actEditTokens()
|
|||
db->saveToFile(settingsCache->getTokenDatabasePath(), true);
|
||||
}
|
||||
|
||||
void TabDeckEditor::actSearch()
|
||||
{
|
||||
if (dlgCardSearch->exec()) {
|
||||
searchEdit->clear();
|
||||
databaseDisplayModel->setCardName(dlgCardSearch->getCardName());
|
||||
databaseDisplayModel->setCardText(dlgCardSearch->getCardText());
|
||||
databaseDisplayModel->setCardTypes(dlgCardSearch->getCardTypes());
|
||||
databaseDisplayModel->setCardColors(dlgCardSearch->getCardColors());
|
||||
}
|
||||
}
|
||||
|
||||
void TabDeckEditor::actClearSearch()
|
||||
{
|
||||
databaseDisplayModel->clearSearch();
|
||||
|
@ -522,21 +537,29 @@ void TabDeckEditor::recursiveExpand(const QModelIndex &index)
|
|||
deckView->expand(index);
|
||||
}
|
||||
|
||||
void TabDeckEditor::addCardHelper(QString zoneName)
|
||||
CardInfo *TabDeckEditor::currentCardInfo() const
|
||||
{
|
||||
const QModelIndex currentIndex = databaseView->selectionModel()->currentIndex();
|
||||
if (!currentIndex.isValid())
|
||||
return;
|
||||
return NULL;
|
||||
const QString cardName = currentIndex.sibling(currentIndex.row(), 0).data().toString();
|
||||
|
||||
CardInfo *info = db->getCard(cardName);
|
||||
return db->getCard(cardName);
|
||||
}
|
||||
|
||||
void TabDeckEditor::addCardHelper(QString zoneName)
|
||||
{
|
||||
const CardInfo *info;
|
||||
|
||||
info = currentCardInfo();
|
||||
if(!info)
|
||||
return;
|
||||
if (info->getIsToken())
|
||||
zoneName = "tokens";
|
||||
|
||||
QModelIndex newCardIndex = deckModel->addCard(cardName, zoneName);
|
||||
|
||||
QModelIndex newCardIndex = deckModel->addCard(info->getName(), zoneName);
|
||||
recursiveExpand(newCardIndex);
|
||||
deckView->setCurrentIndex(newCardIndex);
|
||||
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
|
@ -559,31 +582,57 @@ void TabDeckEditor::actRemoveCard()
|
|||
setModified(true);
|
||||
}
|
||||
|
||||
void TabDeckEditor::offsetCountAtIndex(const QModelIndex &idx, int offset)
|
||||
{
|
||||
if (!idx.isValid() || offset == 0)
|
||||
return;
|
||||
|
||||
const QModelIndex numberIndex = idx.sibling(idx.row(), 0);
|
||||
const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
|
||||
const int new_count = count + offset;
|
||||
deckView->setCurrentIndex(numberIndex);
|
||||
if (new_count <= 0)
|
||||
deckModel->removeRow(idx.row(), idx.parent());
|
||||
else
|
||||
deckModel->setData(numberIndex, new_count, Qt::EditRole);
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
void TabDeckEditor::decrementCardHelper(QString zoneName)
|
||||
{
|
||||
const CardInfo *info;
|
||||
QModelIndex idx;
|
||||
|
||||
info = currentCardInfo();
|
||||
if(!info)
|
||||
return;
|
||||
if (info->getIsToken())
|
||||
zoneName = "tokens";
|
||||
|
||||
idx = deckModel->findCard(info->getName(), zoneName);
|
||||
offsetCountAtIndex(idx, -1);
|
||||
}
|
||||
|
||||
void TabDeckEditor::actDecrementCard()
|
||||
{
|
||||
decrementCardHelper("main");
|
||||
}
|
||||
|
||||
void TabDeckEditor::actDecrementCardFromSideboard()
|
||||
{
|
||||
decrementCardHelper("side");
|
||||
}
|
||||
|
||||
void TabDeckEditor::actIncrement()
|
||||
{
|
||||
const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex();
|
||||
if (!currentIndex.isValid())
|
||||
return;
|
||||
const QModelIndex numberIndex = currentIndex.sibling(currentIndex.row(), 0);
|
||||
const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
|
||||
deckView->setCurrentIndex(numberIndex);
|
||||
deckModel->setData(numberIndex, count + 1, Qt::EditRole);
|
||||
setModified(true);
|
||||
offsetCountAtIndex(currentIndex, 1);
|
||||
}
|
||||
|
||||
void TabDeckEditor::actDecrement()
|
||||
{
|
||||
const QModelIndex ¤tIndex = deckView->selectionModel()->currentIndex();
|
||||
if (!currentIndex.isValid())
|
||||
return;
|
||||
const QModelIndex numberIndex = currentIndex.sibling(currentIndex.row(), 0);
|
||||
const int count = deckModel->data(numberIndex, Qt::EditRole).toInt();
|
||||
deckView->setCurrentIndex(numberIndex);
|
||||
if (count == 1)
|
||||
deckModel->removeRow(currentIndex.row(), currentIndex.parent());
|
||||
else
|
||||
deckModel->setData(numberIndex, count - 1, Qt::EditRole);
|
||||
setModified(true);
|
||||
offsetCountAtIndex(currentIndex, -1);
|
||||
}
|
||||
|
||||
void TabDeckEditor::actUpdatePrices()
|
||||
|
@ -622,3 +671,31 @@ void TabDeckEditor::setModified(bool _modified)
|
|||
modified = _modified;
|
||||
emit tabTextChanged(this, getTabText());
|
||||
}
|
||||
|
||||
void TabDeckEditor::filterViewCustomContextMenu(const QPoint &point) {
|
||||
QMenu menu;
|
||||
QAction *action;
|
||||
QModelIndex idx;
|
||||
|
||||
idx = filterView->indexAt(point);
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
action = menu.addAction(QString("delete"));
|
||||
action->setData(point);
|
||||
connect(&menu, SIGNAL(triggered(QAction *)),
|
||||
this, SLOT(filterRemove(QAction *)));
|
||||
menu.exec(filterView->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void TabDeckEditor::filterRemove(QAction *action) {
|
||||
QPoint point;
|
||||
QModelIndex idx;
|
||||
|
||||
point = action->data().toPoint();
|
||||
idx = filterView->indexAt(point);
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
filterModel->removeRow(idx.row(), idx.parent());
|
||||
}
|
||||
|
|
|
@ -4,18 +4,20 @@
|
|||
#include "tab.h"
|
||||
#include <QAbstractItemModel>
|
||||
#include <QLineEdit>
|
||||
#include "keysignals.h"
|
||||
|
||||
class CardDatabaseModel;
|
||||
class CardDatabaseDisplayModel;
|
||||
class DeckListModel;
|
||||
class QTreeView;
|
||||
class QTableView;
|
||||
class CardInfoWidget;
|
||||
class CardFrame;
|
||||
class QTextEdit;
|
||||
class DlgCardSearch;
|
||||
class QLabel;
|
||||
class DeckLoader;
|
||||
class Response;
|
||||
class FilterTreeModel;
|
||||
class CardInfo;
|
||||
|
||||
class SearchLineEdit : public QLineEdit {
|
||||
private:
|
||||
|
@ -49,7 +51,6 @@ private slots:
|
|||
void actEditSets();
|
||||
void actEditTokens();
|
||||
|
||||
void actSearch();
|
||||
void actClearSearch();
|
||||
|
||||
void actAddCard();
|
||||
|
@ -57,12 +58,20 @@ private slots:
|
|||
void actRemoveCard();
|
||||
void actIncrement();
|
||||
void actDecrement();
|
||||
void actUpdatePrices();
|
||||
void actDecrementCard();
|
||||
void actDecrementCardFromSideboard();
|
||||
|
||||
void finishedUpdatingPrices();
|
||||
void actUpdatePrices();
|
||||
|
||||
void finishedUpdatingPrices();
|
||||
void saveDeckRemoteFinished(const Response &r);
|
||||
void filterViewCustomContextMenu(const QPoint &point);
|
||||
void filterRemove(QAction *action);
|
||||
private:
|
||||
CardInfo *currentCardInfo() const;
|
||||
void addCardHelper(QString zoneName);
|
||||
void offsetCountAtIndex(const QModelIndex &idx, int offset);
|
||||
void decrementCardHelper(QString zoneName);
|
||||
void recursiveExpand(const QModelIndex &index);
|
||||
bool confirmClose();
|
||||
|
||||
|
@ -70,22 +79,27 @@ private:
|
|||
CardDatabaseDisplayModel *databaseDisplayModel;
|
||||
DeckListModel *deckModel;
|
||||
QTreeView *databaseView;
|
||||
|
||||
QTreeView *deckView;
|
||||
CardInfoWidget *cardInfo;
|
||||
KeySignals deckViewKeySignals;
|
||||
CardFrame *cardInfo;
|
||||
QLabel *searchLabel;
|
||||
SearchLineEdit *searchEdit;
|
||||
KeySignals searchKeySignals;
|
||||
|
||||
QLabel *nameLabel;
|
||||
QLineEdit *nameEdit;
|
||||
QLabel *commentsLabel;
|
||||
QTextEdit *commentsEdit;
|
||||
QLabel *hashLabel1;
|
||||
QLabel *hashLabel;
|
||||
DlgCardSearch *dlgCardSearch;
|
||||
FilterTreeModel *filterModel;
|
||||
QTreeView *filterView;
|
||||
|
||||
QMenu *deckMenu, *dbMenu;
|
||||
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose;
|
||||
QAction *aEditSets, *aEditTokens, *aSearch, *aClearSearch;
|
||||
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement, *aUpdatePrices;
|
||||
QAction *aEditSets, *aEditTokens, *aClearSearch, *aCardTextOnly;
|
||||
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement, *aUpdatePrices;
|
||||
|
||||
bool modified;
|
||||
public:
|
||||
|
|
|
@ -532,7 +532,7 @@ void TabGame::retranslateUi()
|
|||
gameMenu->setTitle(tr("&Game"));
|
||||
if (aNextPhase) {
|
||||
aNextPhase->setText(tr("Next &phase"));
|
||||
aNextPhase->setShortcut(tr("Ctrl+Space"));
|
||||
aNextPhase->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Space")) << QKeySequence(tr("Tab")));
|
||||
}
|
||||
if (aNextTurn) {
|
||||
aNextTurn->setText(tr("Next &turn"));
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "abstractclient.h"
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "pending_command.h"
|
||||
#include "pb/session_commands.pb.h"
|
||||
|
@ -21,34 +23,90 @@ TabUserLists::TabUserLists(TabSupervisor *_tabSupervisor, AbstractClient *_clien
|
|||
ignoreList = new UserList(_tabSupervisor, client, UserList::IgnoreList);
|
||||
userInfoBox = new UserInfoBox(client, false);
|
||||
userInfoBox->updateInfo(userInfo);
|
||||
|
||||
|
||||
connect(allUsersList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(buddyList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
connect(ignoreList, SIGNAL(openMessageDialog(const QString &, bool)), this, SIGNAL(openMessageDialog(const QString &, bool)));
|
||||
|
||||
|
||||
connect(client, SIGNAL(userJoinedEventReceived(const Event_UserJoined &)), this, SLOT(processUserJoinedEvent(const Event_UserJoined &)));
|
||||
connect(client, SIGNAL(userLeftEventReceived(const Event_UserLeft &)), this, SLOT(processUserLeftEvent(const Event_UserLeft &)));
|
||||
connect(client, SIGNAL(buddyListReceived(const QList<ServerInfo_User> &)), this, SLOT(buddyListReceived(const QList<ServerInfo_User> &)));
|
||||
connect(client, SIGNAL(ignoreListReceived(const QList<ServerInfo_User> &)), this, SLOT(ignoreListReceived(const QList<ServerInfo_User> &)));
|
||||
connect(client, SIGNAL(addToListEventReceived(const Event_AddToList &)), this, SLOT(processAddToListEvent(const Event_AddToList &)));
|
||||
connect(client, SIGNAL(removeFromListEventReceived(const Event_RemoveFromList &)), this, SLOT(processRemoveFromListEvent(const Event_RemoveFromList &)));
|
||||
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(Command_ListUsers());
|
||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(processListUsersResponse(const Response &)));
|
||||
client->sendCommand(pend);
|
||||
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(userInfoBox);
|
||||
vbox->addWidget(allUsersList);
|
||||
|
||||
|
||||
QHBoxLayout *addToBuddyList = new QHBoxLayout;
|
||||
addBuddyEdit = new QLineEdit;
|
||||
addBuddyEdit->setPlaceholderText(tr("Add to Buddy List"));
|
||||
connect(addBuddyEdit, SIGNAL(returnPressed()), this, SLOT(addToBuddyList()));
|
||||
QPushButton *addBuddyButton = new QPushButton("Add");
|
||||
connect(addBuddyButton, SIGNAL(clicked()), this, SLOT(addToBuddyList()));
|
||||
addToBuddyList->addWidget(addBuddyEdit);
|
||||
addToBuddyList->addWidget(addBuddyButton);
|
||||
|
||||
QHBoxLayout *addToIgnoreList = new QHBoxLayout;
|
||||
addIgnoreEdit = new QLineEdit;
|
||||
addIgnoreEdit->setPlaceholderText(tr("Add to Ignore List"));
|
||||
connect(addIgnoreEdit, SIGNAL(returnPressed()), this, SLOT(addToIgnoreList()));
|
||||
QPushButton *addIgnoreButton = new QPushButton("Add");
|
||||
connect(addIgnoreButton, SIGNAL(clicked()), this, SLOT(addToIgnoreList()));
|
||||
addToIgnoreList->addWidget(addIgnoreEdit);
|
||||
addToIgnoreList->addWidget(addIgnoreButton);
|
||||
|
||||
QVBoxLayout *buddyPanel = new QVBoxLayout;
|
||||
buddyPanel->addWidget(buddyList);
|
||||
buddyPanel->addLayout(addToBuddyList);
|
||||
|
||||
QVBoxLayout *ignorePanel = new QVBoxLayout;
|
||||
ignorePanel->addWidget(ignoreList);
|
||||
ignorePanel->addLayout(addToIgnoreList);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
mainLayout->addWidget(buddyList);
|
||||
mainLayout->addWidget(ignoreList);
|
||||
mainLayout->addLayout(buddyPanel);
|
||||
mainLayout->addLayout(ignorePanel);
|
||||
mainLayout->addLayout(vbox);
|
||||
|
||||
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
|
||||
void TabUserLists::addToBuddyList()
|
||||
{
|
||||
QString userName = addBuddyEdit->text();
|
||||
if (userName.length() < 1) return;
|
||||
|
||||
std::string listName = "buddy";
|
||||
addToList(listName, userName);
|
||||
addBuddyEdit->clear();
|
||||
}
|
||||
|
||||
void TabUserLists::addToIgnoreList()
|
||||
{
|
||||
QString userName = addIgnoreEdit->text();
|
||||
if (userName.length() < 1) return;
|
||||
|
||||
std::string listName = "ignore";
|
||||
addToList(listName, userName);
|
||||
addIgnoreEdit->clear();
|
||||
}
|
||||
|
||||
void TabUserLists::addToList(const std::string &listName, const QString &userName)
|
||||
{
|
||||
Command_AddToList cmd;
|
||||
cmd.set_list(listName);
|
||||
cmd.set_user_name(userName.toStdString());
|
||||
|
||||
client->sendCommand(client->prepareSessionCommand(cmd));
|
||||
}
|
||||
|
||||
|
||||
void TabUserLists::retranslateUi()
|
||||
{
|
||||
allUsersList->retranslateUi();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "tab.h"
|
||||
#include "pb/serverinfo_user.pb.h"
|
||||
#include <QLineEdit>
|
||||
|
||||
class AbstractClient;
|
||||
class UserList;
|
||||
|
@ -30,12 +31,17 @@ private slots:
|
|||
void ignoreListReceived(const QList<ServerInfo_User> &_ignoreList);
|
||||
void processAddToListEvent(const Event_AddToList &event);
|
||||
void processRemoveFromListEvent(const Event_RemoveFromList &event);
|
||||
void addToIgnoreList();
|
||||
void addToBuddyList();
|
||||
private:
|
||||
AbstractClient *client;
|
||||
UserList *allUsersList;
|
||||
UserList *buddyList;
|
||||
UserList *ignoreList;
|
||||
UserInfoBox *userInfoBox;
|
||||
QLineEdit *addBuddyEdit;
|
||||
QLineEdit *addIgnoreEdit;
|
||||
void addToList(const std::string &listName, const QString &userName);
|
||||
public:
|
||||
TabUserLists(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User &userInfo, QWidget *parent = 0);
|
||||
void retranslateUi();
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include <QPainter>
|
||||
#include <QSet>
|
||||
#include <QGraphicsScene>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#ifdef _WIN32
|
||||
#include "round.h"
|
||||
#endif /* _WIN32 */
|
||||
#include "tablezone.h"
|
||||
#include "player.h"
|
||||
#include "settingscache.h"
|
||||
|
@ -46,7 +49,7 @@ bool TableZone::isInverted() const
|
|||
return ((player->getMirrored() && !settingsCache->getInvertVerticalCoordinate()) || (!player->getMirrored() && settingsCache->getInvertVerticalCoordinate()));
|
||||
}
|
||||
|
||||
void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void TableZone::paint(QPainter *painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
if (bgPixmap.isNull())
|
||||
painter->fillRect(boundingRect(), QColor(0, 0, 100));
|
||||
|
|
|
@ -31,7 +31,7 @@ QRectF ZoneViewZone::boundingRect() const
|
|||
return bRect;
|
||||
}
|
||||
|
||||
void ZoneViewZone::paint(QPainter */*painter*/, const QStyleOptionGraphicsItem */*option*/, QWidget */*widget*/)
|
||||
void ZoneViewZone::paint(QPainter * /*painter*/, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1,56 +1,38 @@
|
|||
# CMakeLists for common directory
|
||||
#
|
||||
# provides the common library
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
add_subdirectory(pb)
|
||||
|
||||
SET(common_SOURCES
|
||||
decklist.cpp
|
||||
get_pb_extension.cpp
|
||||
rng_abstract.cpp
|
||||
rng_qt.cpp
|
||||
rng_sfmt.cpp
|
||||
server.cpp
|
||||
server_abstractuserinterface.cpp
|
||||
server_arrow.cpp
|
||||
server_card.cpp
|
||||
server_cardzone.cpp
|
||||
server_counter.cpp
|
||||
server_game.cpp
|
||||
server_player.cpp
|
||||
server_protocolhandler.cpp
|
||||
server_remoteuserinterface.cpp
|
||||
server_response_containers.cpp
|
||||
server_room.cpp
|
||||
serverinfo_user_container.cpp
|
||||
sfmt/SFMT.c
|
||||
)
|
||||
SET(common_HEADERS
|
||||
decklist.h
|
||||
rng_abstract.h
|
||||
rng_qt.h
|
||||
rng_sfmt.h
|
||||
server.h
|
||||
server_arrowtarget.h
|
||||
server_card.h
|
||||
server_database_interface.h
|
||||
server_game.h
|
||||
server_player.h
|
||||
server_protocolhandler.h
|
||||
server_room.h
|
||||
decklist.cpp
|
||||
get_pb_extension.cpp
|
||||
rng_abstract.cpp
|
||||
rng_sfmt.cpp
|
||||
server.cpp
|
||||
server_abstractuserinterface.cpp
|
||||
server_arrow.cpp
|
||||
server_arrowtarget.h
|
||||
server_card.cpp
|
||||
server_cardzone.cpp
|
||||
server_counter.cpp
|
||||
server_game.cpp
|
||||
server_database_interface.cpp
|
||||
server_player.cpp
|
||||
server_protocolhandler.cpp
|
||||
server_remoteuserinterface.cpp
|
||||
server_response_containers.cpp
|
||||
server_room.cpp
|
||||
serverinfo_user_container.cpp
|
||||
sfmt/SFMT.c
|
||||
)
|
||||
|
||||
FIND_PACKAGE(Qt4 REQUIRED)
|
||||
FIND_PACKAGE(Protobuf REQUIRED)
|
||||
|
||||
QT4_WRAP_CPP(common_HEADERS_MOC ${common_HEADERS})
|
||||
INCLUDE(${QT_USE_FILE})
|
||||
INCLUDE_DIRECTORIES(pb)
|
||||
INCLUDE_DIRECTORIES(sfmt)
|
||||
INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_library(cockatrice_common ${common_SOURCES} ${common_HEADERS_MOC})
|
||||
# Without this check, Linux will put -pthread out of order in link.txt and build will fail
|
||||
if (UNIX)
|
||||
target_link_libraries(cockatrice_common cockatrice_protocol pthread)
|
||||
else (UNIX)
|
||||
target_link_libraries(cockatrice_common cockatrice_protocol)
|
||||
endif (UNIX)
|
||||
add_library(cockatrice_common ${common_SOURCES} ${common_MOC_SRCS})
|
||||
target_link_libraries(cockatrice_common cockatrice_protocol)
|
||||
|
|
|
@ -89,7 +89,7 @@ public:
|
|||
virtual QString getName() const = 0;
|
||||
virtual void setName(const QString &_name) = 0;
|
||||
virtual float getPrice() const = 0;
|
||||
virtual void setPrice(float _price) = 0;
|
||||
virtual void setPrice(const float _price) = 0;
|
||||
float getTotalPrice() const { return getNumber() * getPrice(); }
|
||||
int height() const { return 0; }
|
||||
bool compare(AbstractDecklistNode *other) const;
|
||||
|
|
|
@ -1,157 +1,163 @@
|
|||
# CMakeLists for common directory
|
||||
#
|
||||
# provides the protobuf interfaces
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
|
||||
SET(PROTO_FILES
|
||||
admin_commands.proto
|
||||
card_attributes.proto
|
||||
color.proto
|
||||
command_attach_card.proto
|
||||
command_change_zone_properties.proto
|
||||
command_concede.proto
|
||||
command_create_arrow.proto
|
||||
command_create_counter.proto
|
||||
command_create_token.proto
|
||||
command_deck_del_dir.proto
|
||||
command_deck_del.proto
|
||||
command_deck_download.proto
|
||||
command_deck_list.proto
|
||||
command_deck_new_dir.proto
|
||||
command_deck_select.proto
|
||||
command_deck_upload.proto
|
||||
command_del_counter.proto
|
||||
command_delete_arrow.proto
|
||||
command_draw_cards.proto
|
||||
command_dump_zone.proto
|
||||
command_flip_card.proto
|
||||
command_game_say.proto
|
||||
command_inc_card_counter.proto
|
||||
command_inc_counter.proto
|
||||
command_kick_from_game.proto
|
||||
command_leave_game.proto
|
||||
command_move_card.proto
|
||||
command_mulligan.proto
|
||||
command_next_turn.proto
|
||||
command_ready_start.proto
|
||||
command_replay_delete_match.proto
|
||||
command_replay_list.proto
|
||||
command_replay_download.proto
|
||||
command_replay_modify_match.proto
|
||||
command_reveal_cards.proto
|
||||
command_roll_die.proto
|
||||
command_set_active_phase.proto
|
||||
command_set_card_attr.proto
|
||||
command_set_card_counter.proto
|
||||
command_set_counter.proto
|
||||
command_set_sideboard_plan.proto
|
||||
command_set_sideboard_lock.proto
|
||||
command_shuffle.proto
|
||||
commands.proto
|
||||
command_stop_dump_zone.proto
|
||||
command_undo_draw.proto
|
||||
context_concede.proto
|
||||
context_connection_state_changed.proto
|
||||
context_deck_select.proto
|
||||
context_move_card.proto
|
||||
context_mulligan.proto
|
||||
context_ping_changed.proto
|
||||
context_ready_start.proto
|
||||
context_set_sideboard_lock.proto
|
||||
context_undo_draw.proto
|
||||
event_add_to_list.proto
|
||||
event_attach_card.proto
|
||||
event_change_zone_properties.proto
|
||||
event_connection_closed.proto
|
||||
event_create_arrow.proto
|
||||
event_create_counter.proto
|
||||
event_create_token.proto
|
||||
event_del_counter.proto
|
||||
event_delete_arrow.proto
|
||||
event_destroy_card.proto
|
||||
event_draw_cards.proto
|
||||
event_dump_zone.proto
|
||||
event_flip_card.proto
|
||||
event_game_closed.proto
|
||||
event_game_host_changed.proto
|
||||
event_game_joined.proto
|
||||
event_game_say.proto
|
||||
event_game_state_changed.proto
|
||||
event_join.proto
|
||||
event_join_room.proto
|
||||
event_kicked.proto
|
||||
event_leave.proto
|
||||
event_leave_room.proto
|
||||
event_list_games.proto
|
||||
event_list_rooms.proto
|
||||
event_move_card.proto
|
||||
event_player_properties_changed.proto
|
||||
event_remove_from_list.proto
|
||||
event_replay_added.proto
|
||||
event_reveal_cards.proto
|
||||
event_roll_die.proto
|
||||
event_room_say.proto
|
||||
event_server_complete_list.proto
|
||||
event_server_identification.proto
|
||||
event_server_message.proto
|
||||
event_server_shutdown.proto
|
||||
event_set_active_phase.proto
|
||||
event_set_active_player.proto
|
||||
event_set_card_attr.proto
|
||||
event_set_card_counter.proto
|
||||
event_set_counter.proto
|
||||
event_shuffle.proto
|
||||
event_stop_dump_zone.proto
|
||||
event_user_joined.proto
|
||||
event_user_left.proto
|
||||
event_user_message.proto
|
||||
game_commands.proto
|
||||
game_event_container.proto
|
||||
game_event_context.proto
|
||||
game_event.proto
|
||||
game_replay.proto
|
||||
isl_message.proto
|
||||
moderator_commands.proto
|
||||
move_card_to_zone.proto
|
||||
response_deck_download.proto
|
||||
response_deck_list.proto
|
||||
response_deck_upload.proto
|
||||
response_dump_zone.proto
|
||||
response_get_games_of_user.proto
|
||||
response_get_user_info.proto
|
||||
response_join_room.proto
|
||||
response_list_users.proto
|
||||
response_login.proto
|
||||
response_replay_download.proto
|
||||
response_replay_list.proto
|
||||
response.proto
|
||||
room_commands.proto
|
||||
room_event.proto
|
||||
serverinfo_arrow.proto
|
||||
serverinfo_cardcounter.proto
|
||||
serverinfo_card.proto
|
||||
serverinfo_counter.proto
|
||||
serverinfo_deckstorage.proto
|
||||
serverinfo_game.proto
|
||||
serverinfo_gametype.proto
|
||||
serverinfo_playerping.proto
|
||||
serverinfo_playerproperties.proto
|
||||
serverinfo_player.proto
|
||||
serverinfo_replay.proto
|
||||
serverinfo_replay_match.proto
|
||||
serverinfo_room.proto
|
||||
serverinfo_user.proto
|
||||
serverinfo_zone.proto
|
||||
server_message.proto
|
||||
session_commands.proto
|
||||
session_event.proto
|
||||
admin_commands.proto
|
||||
card_attributes.proto
|
||||
color.proto
|
||||
command_attach_card.proto
|
||||
command_change_zone_properties.proto
|
||||
command_concede.proto
|
||||
command_create_arrow.proto
|
||||
command_create_counter.proto
|
||||
command_create_token.proto
|
||||
command_deck_del_dir.proto
|
||||
command_deck_del.proto
|
||||
command_deck_download.proto
|
||||
command_deck_list.proto
|
||||
command_deck_new_dir.proto
|
||||
command_deck_select.proto
|
||||
command_deck_upload.proto
|
||||
command_del_counter.proto
|
||||
command_delete_arrow.proto
|
||||
command_draw_cards.proto
|
||||
command_dump_zone.proto
|
||||
command_flip_card.proto
|
||||
command_game_say.proto
|
||||
command_inc_card_counter.proto
|
||||
command_inc_counter.proto
|
||||
command_kick_from_game.proto
|
||||
command_leave_game.proto
|
||||
command_move_card.proto
|
||||
command_mulligan.proto
|
||||
command_next_turn.proto
|
||||
command_ready_start.proto
|
||||
command_replay_delete_match.proto
|
||||
command_replay_list.proto
|
||||
command_replay_download.proto
|
||||
command_replay_modify_match.proto
|
||||
command_reveal_cards.proto
|
||||
command_roll_die.proto
|
||||
command_set_active_phase.proto
|
||||
command_set_card_attr.proto
|
||||
command_set_card_counter.proto
|
||||
command_set_counter.proto
|
||||
command_set_sideboard_plan.proto
|
||||
command_set_sideboard_lock.proto
|
||||
command_shuffle.proto
|
||||
commands.proto
|
||||
command_stop_dump_zone.proto
|
||||
command_undo_draw.proto
|
||||
context_concede.proto
|
||||
context_connection_state_changed.proto
|
||||
context_deck_select.proto
|
||||
context_move_card.proto
|
||||
context_mulligan.proto
|
||||
context_ping_changed.proto
|
||||
context_ready_start.proto
|
||||
context_set_sideboard_lock.proto
|
||||
context_undo_draw.proto
|
||||
event_add_to_list.proto
|
||||
event_attach_card.proto
|
||||
event_change_zone_properties.proto
|
||||
event_connection_closed.proto
|
||||
event_create_arrow.proto
|
||||
event_create_counter.proto
|
||||
event_create_token.proto
|
||||
event_del_counter.proto
|
||||
event_delete_arrow.proto
|
||||
event_destroy_card.proto
|
||||
event_draw_cards.proto
|
||||
event_dump_zone.proto
|
||||
event_flip_card.proto
|
||||
event_game_closed.proto
|
||||
event_game_host_changed.proto
|
||||
event_game_joined.proto
|
||||
event_game_say.proto
|
||||
event_game_state_changed.proto
|
||||
event_join.proto
|
||||
event_join_room.proto
|
||||
event_kicked.proto
|
||||
event_leave.proto
|
||||
event_leave_room.proto
|
||||
event_list_games.proto
|
||||
event_list_rooms.proto
|
||||
event_move_card.proto
|
||||
event_player_properties_changed.proto
|
||||
event_remove_from_list.proto
|
||||
event_replay_added.proto
|
||||
event_reveal_cards.proto
|
||||
event_roll_die.proto
|
||||
event_room_say.proto
|
||||
event_server_complete_list.proto
|
||||
event_server_identification.proto
|
||||
event_server_message.proto
|
||||
event_server_shutdown.proto
|
||||
event_set_active_phase.proto
|
||||
event_set_active_player.proto
|
||||
event_set_card_attr.proto
|
||||
event_set_card_counter.proto
|
||||
event_set_counter.proto
|
||||
event_shuffle.proto
|
||||
event_stop_dump_zone.proto
|
||||
event_user_joined.proto
|
||||
event_user_left.proto
|
||||
event_user_message.proto
|
||||
game_commands.proto
|
||||
game_event_container.proto
|
||||
game_event_context.proto
|
||||
game_event.proto
|
||||
game_replay.proto
|
||||
isl_message.proto
|
||||
moderator_commands.proto
|
||||
move_card_to_zone.proto
|
||||
response_deck_download.proto
|
||||
response_deck_list.proto
|
||||
response_deck_upload.proto
|
||||
response_dump_zone.proto
|
||||
response_get_games_of_user.proto
|
||||
response_get_user_info.proto
|
||||
response_join_room.proto
|
||||
response_list_users.proto
|
||||
response_login.proto
|
||||
response_replay_download.proto
|
||||
response_replay_list.proto
|
||||
response.proto
|
||||
room_commands.proto
|
||||
room_event.proto
|
||||
serverinfo_arrow.proto
|
||||
serverinfo_cardcounter.proto
|
||||
serverinfo_card.proto
|
||||
serverinfo_counter.proto
|
||||
serverinfo_deckstorage.proto
|
||||
serverinfo_game.proto
|
||||
serverinfo_gametype.proto
|
||||
serverinfo_playerping.proto
|
||||
serverinfo_playerproperties.proto
|
||||
serverinfo_player.proto
|
||||
serverinfo_replay.proto
|
||||
serverinfo_replay_match.proto
|
||||
serverinfo_room.proto
|
||||
serverinfo_user.proto
|
||||
serverinfo_zone.proto
|
||||
server_message.proto
|
||||
session_commands.proto
|
||||
session_event.proto
|
||||
)
|
||||
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${PROTO_FILES})
|
||||
|
||||
add_library(cockatrice_protocol ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
set(cockatrice_protocol_LIBS ${PROTOBUF_LIBRARIES})
|
||||
if (WIN32)
|
||||
set(cockatrice_protocol_LIBS ${cockatrice_protocol_LIBS} -lprotobuf)
|
||||
endif (WIN32)
|
||||
if (MSVC)
|
||||
set(cockatrice_protocol_LIBS ${cockatrice_protocol_LIBS} -lprotobuf)
|
||||
endif (MSVC)
|
||||
if (UNIX)
|
||||
set(cockatrice_protocol_LIBS ${cockatrice_protocol_LIBS} -lpthread)
|
||||
endif (UNIX)
|
||||
target_link_libraries(cockatrice_protocol ${cockatrice_protocol_LIBS})
|
||||
|
|
|
@ -7,9 +7,9 @@ QVector<int> RNG_Abstract::makeNumbersVector(int n, int min, int max)
|
|||
const int bins = max - min + 1;
|
||||
QVector<int> result(bins);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int number = getNumber(min, max);
|
||||
int number = rand(min, max);
|
||||
if ((number < min) || (number > max))
|
||||
qDebug() << "getNumber(" << min << "," << max << ") returned " << number;
|
||||
qDebug() << "rand(" << min << "," << max << ") returned " << number;
|
||||
else
|
||||
result[number - min]++;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ class RNG_Abstract : public QObject {
|
|||
Q_OBJECT
|
||||
public:
|
||||
RNG_Abstract(QObject *parent = 0) : QObject(parent) { }
|
||||
virtual unsigned int getNumber(unsigned int min, unsigned int max) = 0;
|
||||
virtual unsigned int rand(int min, int max) = 0;
|
||||
QVector<int> makeNumbersVector(int n, int min, int max);
|
||||
double testRandom(const QVector<int> &numbers) const;
|
||||
};
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#include "rng_qt.h"
|
||||
#include <QDateTime>
|
||||
#include <stdlib.h>
|
||||
|
||||
RNG_Qt::RNG_Qt(QObject *parent)
|
||||
: RNG_Abstract(parent)
|
||||
{
|
||||
int seed = QDateTime::currentDateTime().toTime_t();
|
||||
qsrand(seed);
|
||||
}
|
||||
|
||||
unsigned int RNG_Qt::getNumber(unsigned int min, unsigned int max)
|
||||
{
|
||||
int r = qrand();
|
||||
return min + (unsigned int) (((double) (max + 1 - min)) * r / (RAND_MAX + 1.0));
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef RNG_QT_H
|
||||
#define RNG_QT_H
|
||||
|
||||
#include "rng_abstract.h"
|
||||
|
||||
class RNG_Qt : public RNG_Abstract {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RNG_Qt(QObject *parent = 0);
|
||||
unsigned int getNumber(unsigned int min, unsigned int max);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,25 +1,136 @@
|
|||
#include "rng_sfmt.h"
|
||||
#include "sfmt/SFMT.h"
|
||||
#include <QDateTime>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <stdexcept>
|
||||
|
||||
// This is from gcc sources, namely from fixincludes/inclhack.def
|
||||
// On C++11 systems, <cstdint> could be included instead.
|
||||
#ifndef UINT64_MAX
|
||||
#define UINT64_MAX (~(uint64_t)0)
|
||||
#endif
|
||||
|
||||
RNG_SFMT::RNG_SFMT(QObject *parent)
|
||||
: RNG_Abstract(parent)
|
||||
{
|
||||
std::cerr << "Using SFMT random number generator." << std::endl;
|
||||
|
||||
int seed = QDateTime::currentDateTime().toTime_t();
|
||||
init_gen_rand(seed);
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
gen_rand64();
|
||||
// initialize the random number generator with a 32bit integer seed (timestamp)
|
||||
sfmt_init_gen_rand(&sfmt, QDateTime::currentDateTime().toTime_t());
|
||||
}
|
||||
|
||||
unsigned int RNG_SFMT::getNumber(unsigned int min, unsigned int max)
|
||||
{
|
||||
mutex.lock();
|
||||
uint64_t r = gen_rand64();
|
||||
mutex.unlock();
|
||||
|
||||
return min + (unsigned int) (((double) (max + 1 - min)) * r / (18446744073709551616.0 + 1.0));
|
||||
/**
|
||||
* This method is the rand() equivalent which calls the cdf with proper bounds.
|
||||
*
|
||||
* It is possible to generate random numbers from [-min, +/-max] though the RNG uses
|
||||
* unsigned numbers only, so this wrapper handles some special cases for min and max.
|
||||
*
|
||||
* It is only necessary that the upper bound is larger or equal to the lower bound - with the exception
|
||||
* that someone wants something like rand() % -foo.
|
||||
*/
|
||||
unsigned int RNG_SFMT::rand(int min, int max) {
|
||||
/* If min is negative, it would be possible to calculate
|
||||
* cdf(0, max - min) + min
|
||||
* There has been no use for negative random numbers with rand() though, so it's treated as error.
|
||||
*/
|
||||
if(min < 0) {
|
||||
throw std::invalid_argument(
|
||||
QString("Invalid bounds for RNG: Got min " +
|
||||
QString::number(min) + " < 0!\n").toStdString());
|
||||
// at this point, the method exits. No return value is needed, because
|
||||
// basically the exception itself is returned.
|
||||
}
|
||||
|
||||
// For complete fairness and equal timing, this should be a roll, but let's skip it anyway
|
||||
if(min == max)
|
||||
return max;
|
||||
|
||||
// This is actually not used in Cockatrice:
|
||||
// Someone wants rand() % -foo, so we compute -rand(0, +foo)
|
||||
// This is the only time where min > max is (sort of) legal.
|
||||
// Not handling this will cause the application to crash.
|
||||
if(min == 0 && max < 0) {
|
||||
return -cdf(0, -max);
|
||||
}
|
||||
|
||||
// No special cases are left, except !(min > max) which is caught in the cdf itself.
|
||||
return cdf(min, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Much thought went into this, please read this comment before you modify the code.
|
||||
* Let SFMT() be an alias for sfmt_genrand_uint64() aka SFMT's rand() function.
|
||||
*
|
||||
* SMFT() returns a uniformly distributed pseudorandom number from 0 to UINT64_MAX.
|
||||
* As SFMT() operates on a limited integer range, it is a _discrete_ function.
|
||||
*
|
||||
* We want a random number from a given interval [min, max] though, so we need to
|
||||
* implement the (discrete) cumulative distribution function SFMT(min, max), which
|
||||
* returns a random number X from [min, max].
|
||||
*
|
||||
* This CDF is by formal definition:
|
||||
* SFMT(X; min, max) = (floor(X) - min + 1) / (max - min + 1)
|
||||
*
|
||||
* To get out the random variable, solve for X:
|
||||
* floor(X) = SFMT(X; min, max) * (max - min + 1) + min - 1
|
||||
* So this is, what rand(min, max) should look like.
|
||||
* Problem: SFMT(X; min, max) * (max - min + 1) could produce an integer overflow,
|
||||
* so it is not safe.
|
||||
*
|
||||
* One solution is to divide the universe into buckets of equal size depending on the
|
||||
* range [min, max] and assign X to the bucket that contains the number generated
|
||||
* by SFMT(). This equals to modulo computation and is not satisfying:
|
||||
* If the buckets don't divide the universe equally, because the bucket size is not
|
||||
* a divisor of 2, there will be a range in the universe that is biased because one
|
||||
* bucket is too small thus will be chosen less equally!
|
||||
*
|
||||
* This is solved by rejection sampling:
|
||||
* As SFMT() is assumed to be unbiased, we are allowed to ignore those random numbers
|
||||
* from SFMT() that would force us to have an unequal bucket and generate new random
|
||||
* numbers until one number fits into one of the other buckets.
|
||||
* This can be compared to an ideal six sided die that is rolled until only sides
|
||||
* 1-5 show up, while 6 represents something that you don't want. So you basically roll
|
||||
* a five sided die.
|
||||
*
|
||||
* Note: If you replace the SFMT RNG with some other rand() function in the future,
|
||||
* then you _need_ to change the UINT64_MAX constant to the largest possible random
|
||||
* number which can be created by the new rand() function. This value is often defined
|
||||
* in a RAND_MAX constant.
|
||||
* Otherwise you will probably skew the outcome of the rand() method or worsen the
|
||||
* performance of the application.
|
||||
*/
|
||||
unsigned int RNG_SFMT::cdf(unsigned int min, unsigned int max)
|
||||
{
|
||||
// This all makes no sense if min > max, which should never happen.
|
||||
if(min > max) {
|
||||
throw std::invalid_argument(
|
||||
QString("Invalid bounds for RNG: min > max! Values were: min = " +
|
||||
QString::number(min) + ", max = " +
|
||||
QString::number(max)).toStdString());
|
||||
// at this point, the method exits. No return value is needed, because
|
||||
// basically the exception itself is returned.
|
||||
}
|
||||
|
||||
// First compute the diameter (aka size, length) of the [min, max] interval
|
||||
const unsigned int diameter = max - min + 1;
|
||||
|
||||
// Compute how many buckets (each in size of the diameter) will fit into the
|
||||
// universe.
|
||||
// If the division has a remainder, the result is floored automatically.
|
||||
const uint64_t buckets = UINT64_MAX / diameter;
|
||||
|
||||
// Compute the last valid random number. All numbers beyond have to be ignored.
|
||||
// If there was no remainder in the previous step, limit is equal to UINT64_MAX.
|
||||
const uint64_t limit = diameter * buckets;
|
||||
|
||||
uint64_t rand;
|
||||
// To make the random number generation thread-safe, a mutex is created around
|
||||
// the generation. Outside of the loop of course, to avoid lock/unlock overhead.
|
||||
mutex.lock();
|
||||
do {
|
||||
rand = sfmt_genrand_uint64(&sfmt);
|
||||
} while (rand >= limit);
|
||||
mutex.unlock();
|
||||
|
||||
// Now determine the bucket containing the SFMT() random number and after adding
|
||||
// the lower bound, a random number from [min, max] can be returned.
|
||||
return (unsigned int) (rand / buckets + min);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,40 @@
|
|||
#ifndef RNG_SFMT_H
|
||||
#define RNG_SFMT_H
|
||||
|
||||
#include "rng_abstract.h"
|
||||
#include <climits>
|
||||
#include <QMutex>
|
||||
#include "sfmt/SFMT.h"
|
||||
#include "rng_abstract.h"
|
||||
|
||||
/**
|
||||
* This class encapsulates a state of the art PRNG and can be used
|
||||
* to return uniformly distributed integer random numbers from a range [min, max].
|
||||
* Though technically possible, min must be >= 0 and max should always be > 0.
|
||||
* If max < 0 and min == 0 it is assumed that rand() % -max is wanted and the result will
|
||||
* be -rand(0, -max).
|
||||
* This is the only exception to the rule that !(min > max) and is actually unused in
|
||||
* Cockatrice.
|
||||
*
|
||||
* Technical details:
|
||||
* The RNG uses the SIMD-oriented Fast Mersenne Twister code v1.4.1 from
|
||||
* http://www.math.sci.hiroshima-u.ac.jp/~%20m-mat/MT/SFMT/index.html
|
||||
* The SFMT RNG creates unsigned int 64bit pseudo random numbers.
|
||||
*
|
||||
* These are mapped to values from the interval [min, max] without bias by using Knuth's
|
||||
* "Algorithm S (Selection sampling technique)" from "The Art of Computer Programming 3rd
|
||||
* Edition Volume 2 / Seminumerical Algorithms".
|
||||
*/
|
||||
|
||||
class RNG_SFMT : public RNG_Abstract {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QMutex mutex;
|
||||
sfmt_t sfmt;
|
||||
// The discrete cumulative distribution function for the RNG
|
||||
unsigned int cdf(unsigned int min, unsigned int max);
|
||||
public:
|
||||
RNG_SFMT(QObject *parent = 0);
|
||||
unsigned int getNumber(unsigned int min, unsigned int max);
|
||||
unsigned int rand(int min, int max);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
2
common/server_arrowtarget.cpp
Normal file
2
common/server_arrowtarget.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "server_arrowtarget.h"
|
|
@ -26,7 +26,7 @@
|
|||
#include "pb/command_move_card.pb.h"
|
||||
|
||||
Server_CardZone::Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type)
|
||||
: player(_player),
|
||||
: player(_player),
|
||||
name(_name),
|
||||
has_coords(_has_coords),
|
||||
type(_type),
|
||||
|
@ -37,255 +37,257 @@ Server_CardZone::Server_CardZone(Server_Player *_player, const QString &_name, b
|
|||
|
||||
Server_CardZone::~Server_CardZone()
|
||||
{
|
||||
qDebug() << "Server_CardZone destructor:" << name;
|
||||
clear();
|
||||
qDebug() << "Server_CardZone destructor:" << name;
|
||||
clear();
|
||||
}
|
||||
|
||||
void Server_CardZone::shuffle()
|
||||
{
|
||||
QList<Server_Card *> temp;
|
||||
for (int i = cards.size(); i; i--)
|
||||
temp.append(cards.takeAt(rng->getNumber(0, i - 1)));
|
||||
cards = temp;
|
||||
|
||||
playersWithWritePermission.clear();
|
||||
// Size 0 or 1 decks are sorted
|
||||
if (cards.size() < 2) return;
|
||||
for (int i = cards.size() - 1; i > 0; i--){
|
||||
int j = rng->rand(0, i);
|
||||
cards.swap(j,i);
|
||||
}
|
||||
playersWithWritePermission.clear();
|
||||
}
|
||||
|
||||
|
||||
void Server_CardZone::removeCardFromCoordMap(Server_Card *card, int oldX, int oldY)
|
||||
{
|
||||
if (oldX < 0)
|
||||
return;
|
||||
|
||||
const int baseX = (oldX / 3) * 3;
|
||||
QMap<int, Server_Card *> &coordMap = coordinateMap[oldY];
|
||||
|
||||
if (coordMap.contains(baseX) && coordMap.contains(baseX + 1) && coordMap.contains(baseX + 2))
|
||||
// If the removal of this card has opened up a previously full pile...
|
||||
freePilesMap[oldY].insert(coordMap.value(baseX)->getName(), baseX);
|
||||
|
||||
coordMap.remove(oldX);
|
||||
|
||||
if (!(coordMap.contains(baseX) && coordMap.value(baseX)->getName() == card->getName()) && !(coordMap.contains(baseX + 1) && coordMap.value(baseX + 1)->getName() == card->getName()) && !(coordMap.contains(baseX + 2) && coordMap.value(baseX + 2)->getName() == card->getName()))
|
||||
// If this card was the last one with this name...
|
||||
freePilesMap[oldY].remove(card->getName(), baseX);
|
||||
|
||||
if (!coordMap.contains(baseX) && !coordMap.contains(baseX + 1) && !coordMap.contains(baseX + 2)) {
|
||||
// If the removal of this card has freed a whole pile, i.e. it was the last card in it...
|
||||
if (baseX < freeSpaceMap[oldY])
|
||||
freeSpaceMap[oldY] = baseX;
|
||||
}
|
||||
if (oldX < 0)
|
||||
return;
|
||||
|
||||
const int baseX = (oldX / 3) * 3;
|
||||
QMap<int, Server_Card *> &coordMap = coordinateMap[oldY];
|
||||
|
||||
if (coordMap.contains(baseX) && coordMap.contains(baseX + 1) && coordMap.contains(baseX + 2))
|
||||
// If the removal of this card has opened up a previously full pile...
|
||||
freePilesMap[oldY].insert(coordMap.value(baseX)->getName(), baseX);
|
||||
|
||||
coordMap.remove(oldX);
|
||||
|
||||
if (!(coordMap.contains(baseX) && coordMap.value(baseX)->getName() == card->getName()) && !(coordMap.contains(baseX + 1) && coordMap.value(baseX + 1)->getName() == card->getName()) && !(coordMap.contains(baseX + 2) && coordMap.value(baseX + 2)->getName() == card->getName()))
|
||||
// If this card was the last one with this name...
|
||||
freePilesMap[oldY].remove(card->getName(), baseX);
|
||||
|
||||
if (!coordMap.contains(baseX) && !coordMap.contains(baseX + 1) && !coordMap.contains(baseX + 2)) {
|
||||
// If the removal of this card has freed a whole pile, i.e. it was the last card in it...
|
||||
if (baseX < freeSpaceMap[oldY])
|
||||
freeSpaceMap[oldY] = baseX;
|
||||
}
|
||||
}
|
||||
|
||||
void Server_CardZone::insertCardIntoCoordMap(Server_Card *card, int x, int y)
|
||||
{
|
||||
if (x < 0)
|
||||
return;
|
||||
|
||||
coordinateMap[y].insert(x, card);
|
||||
if (!(x % 3)) {
|
||||
if (!freePilesMap[y].contains(card->getName(), x) && card->getAttachedCards().isEmpty())
|
||||
freePilesMap[y].insert(card->getName(), x);
|
||||
if (freeSpaceMap[y] == x) {
|
||||
int nextFreeX = x;
|
||||
do {
|
||||
nextFreeX += 3;
|
||||
} while (coordinateMap[y].contains(nextFreeX) || coordinateMap[y].contains(nextFreeX + 1) || coordinateMap[y].contains(nextFreeX + 2));
|
||||
freeSpaceMap[y] = nextFreeX;
|
||||
}
|
||||
} else if (!((x - 2) % 3)) {
|
||||
const int baseX = (x / 3) * 3;
|
||||
freePilesMap[y].remove(coordinateMap[y].value(baseX)->getName(), baseX);
|
||||
}
|
||||
if (x < 0)
|
||||
return;
|
||||
|
||||
coordinateMap[y].insert(x, card);
|
||||
if (!(x % 3)) {
|
||||
if (!freePilesMap[y].contains(card->getName(), x) && card->getAttachedCards().isEmpty())
|
||||
freePilesMap[y].insert(card->getName(), x);
|
||||
if (freeSpaceMap[y] == x) {
|
||||
int nextFreeX = x;
|
||||
do {
|
||||
nextFreeX += 3;
|
||||
} while (coordinateMap[y].contains(nextFreeX) || coordinateMap[y].contains(nextFreeX + 1) || coordinateMap[y].contains(nextFreeX + 2));
|
||||
freeSpaceMap[y] = nextFreeX;
|
||||
}
|
||||
} else if (!((x - 2) % 3)) {
|
||||
const int baseX = (x / 3) * 3;
|
||||
freePilesMap[y].remove(coordinateMap[y].value(baseX)->getName(), baseX);
|
||||
}
|
||||
}
|
||||
|
||||
int Server_CardZone::removeCard(Server_Card *card)
|
||||
{
|
||||
int index = cards.indexOf(card);
|
||||
cards.removeAt(index);
|
||||
if (has_coords)
|
||||
removeCardFromCoordMap(card, card->getX(), card->getY());
|
||||
card->setZone(0);
|
||||
|
||||
return index;
|
||||
int index = cards.indexOf(card);
|
||||
cards.removeAt(index);
|
||||
if (has_coords)
|
||||
removeCardFromCoordMap(card, card->getX(), card->getY());
|
||||
card->setZone(0);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
Server_Card *Server_CardZone::getCard(int id, int *position, bool remove)
|
||||
{
|
||||
if (type != ServerInfo_Zone::HiddenZone) {
|
||||
for (int i = 0; i < cards.size(); ++i) {
|
||||
Server_Card *tmp = cards[i];
|
||||
if (tmp->getId() == id) {
|
||||
if (position)
|
||||
*position = i;
|
||||
if (remove) {
|
||||
cards.removeAt(i);
|
||||
tmp->setZone(0);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
if ((id >= cards.size()) || (id < 0))
|
||||
return NULL;
|
||||
Server_Card *tmp = cards[id];
|
||||
if (position)
|
||||
*position = id;
|
||||
if (remove) {
|
||||
cards.removeAt(id);
|
||||
tmp->setZone(0);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
if (type != ServerInfo_Zone::HiddenZone) {
|
||||
for (int i = 0; i < cards.size(); ++i) {
|
||||
Server_Card *tmp = cards[i];
|
||||
if (tmp->getId() == id) {
|
||||
if (position)
|
||||
*position = i;
|
||||
if (remove) {
|
||||
cards.removeAt(i);
|
||||
tmp->setZone(0);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
} else {
|
||||
if ((id >= cards.size()) || (id < 0))
|
||||
return NULL;
|
||||
Server_Card *tmp = cards[id];
|
||||
if (position)
|
||||
*position = id;
|
||||
if (remove) {
|
||||
cards.removeAt(id);
|
||||
tmp->setZone(0);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName) const
|
||||
{
|
||||
const QMap<int, Server_Card *> &coordMap = coordinateMap.value(y);
|
||||
if (x == -1) {
|
||||
if (freePilesMap[y].contains(cardName)) {
|
||||
x = (freePilesMap[y].value(cardName) / 3) * 3;
|
||||
if (!coordMap.contains(x))
|
||||
return x;
|
||||
else if (!coordMap.contains(x + 1))
|
||||
return x + 1;
|
||||
else
|
||||
return x + 2;
|
||||
}
|
||||
} else if (x >= 0) {
|
||||
int resultX = 0;
|
||||
x = (x / 3) * 3;
|
||||
if (!coordMap.contains(x))
|
||||
resultX = x;
|
||||
else if (!coordMap.value(x)->getAttachedCards().isEmpty()) {
|
||||
resultX = x;
|
||||
x = -1;
|
||||
} else if (!coordMap.contains(x + 1))
|
||||
resultX = x + 1;
|
||||
else if (!coordMap.contains(x + 2))
|
||||
resultX = x + 2;
|
||||
else {
|
||||
resultX = x;
|
||||
x = -1;
|
||||
}
|
||||
if (x < 0)
|
||||
while (coordMap.contains(resultX))
|
||||
resultX += 3;
|
||||
const QMap<int, Server_Card *> &coordMap = coordinateMap.value(y);
|
||||
if (x == -1) {
|
||||
if (freePilesMap[y].contains(cardName)) {
|
||||
x = (freePilesMap[y].value(cardName) / 3) * 3;
|
||||
if (!coordMap.contains(x))
|
||||
return x;
|
||||
else if (!coordMap.contains(x + 1))
|
||||
return x + 1;
|
||||
else
|
||||
return x + 2;
|
||||
}
|
||||
} else if (x >= 0) {
|
||||
int resultX = 0;
|
||||
x = (x / 3) * 3;
|
||||
if (!coordMap.contains(x))
|
||||
resultX = x;
|
||||
else if (!coordMap.value(x)->getAttachedCards().isEmpty()) {
|
||||
resultX = x;
|
||||
x = -1;
|
||||
} else if (!coordMap.contains(x + 1))
|
||||
resultX = x + 1;
|
||||
else if (!coordMap.contains(x + 2))
|
||||
resultX = x + 2;
|
||||
else {
|
||||
resultX = x;
|
||||
x = -1;
|
||||
}
|
||||
if (x < 0)
|
||||
while (coordMap.contains(resultX))
|
||||
resultX += 3;
|
||||
|
||||
return resultX;
|
||||
}
|
||||
|
||||
return freeSpaceMap[y];
|
||||
return resultX;
|
||||
}
|
||||
|
||||
return freeSpaceMap[y];
|
||||
}
|
||||
|
||||
bool Server_CardZone::isColumnStacked(int x, int y) const
|
||||
{
|
||||
if (!has_coords)
|
||||
return false;
|
||||
|
||||
return coordinateMap[y].contains((x / 3) * 3 + 1);
|
||||
if (!has_coords)
|
||||
return false;
|
||||
|
||||
return coordinateMap[y].contains((x / 3) * 3 + 1);
|
||||
}
|
||||
|
||||
bool Server_CardZone::isColumnEmpty(int x, int y) const
|
||||
{
|
||||
if (!has_coords)
|
||||
return true;
|
||||
|
||||
return !coordinateMap[y].contains((x / 3) * 3);
|
||||
if (!has_coords)
|
||||
return true;
|
||||
|
||||
return !coordinateMap[y].contains((x / 3) * 3);
|
||||
}
|
||||
|
||||
void Server_CardZone::moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y)
|
||||
{
|
||||
CardToMove *cardToMove = new CardToMove;
|
||||
cardToMove->set_card_id(card->getId());
|
||||
player->moveCard(ges, this, QList<const CardToMove *>() << cardToMove, this, x, y, false, false);
|
||||
delete cardToMove;
|
||||
CardToMove *cardToMove = new CardToMove;
|
||||
cardToMove->set_card_id(card->getId());
|
||||
player->moveCard(ges, this, QList<const CardToMove *>() << cardToMove, this, x, y, false, false);
|
||||
delete cardToMove;
|
||||
}
|
||||
|
||||
void Server_CardZone::fixFreeSpaces(GameEventStorage &ges)
|
||||
{
|
||||
if (!has_coords)
|
||||
return;
|
||||
|
||||
QSet<QPair<int, int> > placesToLook;
|
||||
for (int i = 0; i < cards.size(); ++i)
|
||||
placesToLook.insert(QPair<int, int>((cards[i]->getX() / 3) * 3, cards[i]->getY()));
|
||||
|
||||
QSetIterator<QPair<int, int> > placeIterator(placesToLook);
|
||||
while (placeIterator.hasNext()) {
|
||||
const QPair<int, int> &foo = placeIterator.next();
|
||||
int baseX = foo.first;
|
||||
int y = foo.second;
|
||||
|
||||
if (!coordinateMap[y].contains(baseX)) {
|
||||
if (coordinateMap[y].contains(baseX + 1))
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 1), baseX, y);
|
||||
else if (coordinateMap[y].contains(baseX + 2)) {
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX, y);
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
if (!coordinateMap[y].contains(baseX + 1) && coordinateMap[y].contains(baseX + 2))
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX + 1, y);
|
||||
}
|
||||
if (!has_coords)
|
||||
return;
|
||||
|
||||
QSet<QPair<int, int> > placesToLook;
|
||||
for (int i = 0; i < cards.size(); ++i)
|
||||
placesToLook.insert(QPair<int, int>((cards[i]->getX() / 3) * 3, cards[i]->getY()));
|
||||
|
||||
QSetIterator<QPair<int, int> > placeIterator(placesToLook);
|
||||
while (placeIterator.hasNext()) {
|
||||
const QPair<int, int> &foo = placeIterator.next();
|
||||
int baseX = foo.first;
|
||||
int y = foo.second;
|
||||
|
||||
if (!coordinateMap[y].contains(baseX)) {
|
||||
if (coordinateMap[y].contains(baseX + 1))
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 1), baseX, y);
|
||||
else if (coordinateMap[y].contains(baseX + 2)) {
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX, y);
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
if (!coordinateMap[y].contains(baseX + 1) && coordinateMap[y].contains(baseX + 2))
|
||||
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX + 1, y);
|
||||
}
|
||||
}
|
||||
|
||||
void Server_CardZone::updateCardCoordinates(Server_Card *card, int oldX, int oldY)
|
||||
{
|
||||
if (!has_coords)
|
||||
return;
|
||||
|
||||
if (oldX != -1)
|
||||
removeCardFromCoordMap(card, oldX, oldY);
|
||||
insertCardIntoCoordMap(card, card->getX(), card->getY());
|
||||
if (!has_coords)
|
||||
return;
|
||||
|
||||
if (oldX != -1)
|
||||
removeCardFromCoordMap(card, oldX, oldY);
|
||||
insertCardIntoCoordMap(card, card->getX(), card->getY());
|
||||
}
|
||||
|
||||
void Server_CardZone::insertCard(Server_Card *card, int x, int y)
|
||||
{
|
||||
if (hasCoords()) {
|
||||
card->setCoords(x, y);
|
||||
cards.append(card);
|
||||
insertCardIntoCoordMap(card, x, y);
|
||||
} else {
|
||||
card->setCoords(0, 0);
|
||||
if (x == -1)
|
||||
cards.append(card);
|
||||
else
|
||||
cards.insert(x, card);
|
||||
}
|
||||
card->setZone(this);
|
||||
if (hasCoords()) {
|
||||
card->setCoords(x, y);
|
||||
cards.append(card);
|
||||
insertCardIntoCoordMap(card, x, y);
|
||||
} else {
|
||||
card->setCoords(0, 0);
|
||||
if (x == -1)
|
||||
cards.append(card);
|
||||
else
|
||||
cards.insert(x, card);
|
||||
}
|
||||
card->setZone(this);
|
||||
}
|
||||
|
||||
void Server_CardZone::clear()
|
||||
{
|
||||
for (int i = 0; i < cards.size(); i++)
|
||||
delete cards.at(i);
|
||||
cards.clear();
|
||||
coordinateMap.clear();
|
||||
freePilesMap.clear();
|
||||
freeSpaceMap.clear();
|
||||
playersWithWritePermission.clear();
|
||||
for (int i = 0; i < cards.size(); i++)
|
||||
delete cards.at(i);
|
||||
cards.clear();
|
||||
coordinateMap.clear();
|
||||
freePilesMap.clear();
|
||||
freeSpaceMap.clear();
|
||||
playersWithWritePermission.clear();
|
||||
}
|
||||
|
||||
void Server_CardZone::addWritePermission(int playerId)
|
||||
{
|
||||
playersWithWritePermission.insert(playerId);
|
||||
playersWithWritePermission.insert(playerId);
|
||||
}
|
||||
|
||||
void Server_CardZone::getInfo(ServerInfo_Zone *info, Server_Player *playerWhosAsking, bool omniscient)
|
||||
{
|
||||
info->set_name(name.toStdString());
|
||||
info->set_type(type);
|
||||
info->set_with_coords(has_coords);
|
||||
info->set_card_count(cards.size());
|
||||
info->set_always_reveal_top_card(alwaysRevealTopCard);
|
||||
if (
|
||||
(((playerWhosAsking == player) || omniscient) && (type != ServerInfo_Zone::HiddenZone))
|
||||
|| ((playerWhosAsking != player) && (type == ServerInfo_Zone::PublicZone))
|
||||
) {
|
||||
QListIterator<Server_Card *> cardIterator(cards);
|
||||
while (cardIterator.hasNext())
|
||||
cardIterator.next()->getInfo(info->add_card_list());
|
||||
}
|
||||
info->set_name(name.toStdString());
|
||||
info->set_type(type);
|
||||
info->set_with_coords(has_coords);
|
||||
info->set_card_count(cards.size());
|
||||
info->set_always_reveal_top_card(alwaysRevealTopCard);
|
||||
if (
|
||||
(((playerWhosAsking == player) || omniscient) && (type != ServerInfo_Zone::HiddenZone))
|
||||
|| ((playerWhosAsking != player) && (type == ServerInfo_Zone::PublicZone))
|
||||
) {
|
||||
QListIterator<Server_Card *> cardIterator(cards);
|
||||
while (cardIterator.hasNext())
|
||||
cardIterator.next()->getInfo(info->add_card_list());
|
||||
}
|
||||
}
|
||||
|
|
2
common/server_database_interface.cpp
Normal file
2
common/server_database_interface.cpp
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
#include "server_database_interface.h"
|
|
@ -844,7 +844,7 @@ Response::ResponseCode Server_Player::cmdRollDie(const Command_RollDie &cmd, Res
|
|||
|
||||
Event_RollDie event;
|
||||
event.set_sides(cmd.sides());
|
||||
event.set_value(rng->getNumber(1, cmd.sides()));
|
||||
event.set_value(rng->rand(1, cmd.sides()));
|
||||
ges.enqueueGameEvent(event, playerId);
|
||||
|
||||
return Response::RespOk;
|
||||
|
@ -1524,7 +1524,7 @@ Response::ResponseCode Server_Player::cmdRevealCards(const Command_RevealCards &
|
|||
else if (cmd.card_id() == -2) {
|
||||
if (zone->getCards().isEmpty())
|
||||
return Response::RespContextError;
|
||||
cardsToReveal.append(zone->getCards().at(rng->getNumber(0, zone->getCards().size() - 1)));
|
||||
cardsToReveal.append(zone->getCards().at(rng->rand(0, zone->getCards().size() - 1)));
|
||||
} else {
|
||||
Server_Card *card = zone->getCard(cmd.card_id());
|
||||
if (!card)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
Copyright (c) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima
|
||||
University. All rights reserved.
|
||||
University.
|
||||
Copyright (c) 2012 Mutsuo Saito, Makoto Matsumoto, Hiroshima University
|
||||
and The University of Tokyo.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
@ -11,10 +14,10 @@ met:
|
|||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of the Hiroshima University nor the names of
|
||||
its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written
|
||||
permission.
|
||||
* Neither the names of Hiroshima University, The University of
|
||||
Tokyo nor the names of its contributors may be used to endorse
|
||||
or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
|
|
164
common/sfmt/SFMT-common.h
Normal file
164
common/sfmt/SFMT-common.h
Normal file
|
@ -0,0 +1,164 @@
|
|||
#pragma once
|
||||
/**
|
||||
* @file SFMT-common.h
|
||||
*
|
||||
* @brief SIMD oriented Fast Mersenne Twister(SFMT) pseudorandom
|
||||
* number generator with jump function. This file includes common functions
|
||||
* used in random number generation and jump.
|
||||
*
|
||||
* @author Mutsuo Saito (Hiroshima University)
|
||||
* @author Makoto Matsumoto (The University of Tokyo)
|
||||
*
|
||||
* Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima
|
||||
* University.
|
||||
* Copyright (C) 2012 Mutsuo Saito, Makoto Matsumoto, Hiroshima
|
||||
* University and The University of Tokyo.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The 3-clause BSD License is applied to this software, see
|
||||
* LICENSE.txt
|
||||
*/
|
||||
#ifndef SFMT_COMMON_H
|
||||
#define SFMT_COMMON_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "SFMT.h"
|
||||
|
||||
inline static void do_recursion(w128_t * r, w128_t * a, w128_t * b,
|
||||
w128_t * c, w128_t * d);
|
||||
|
||||
inline static void rshift128(w128_t *out, w128_t const *in, int shift);
|
||||
inline static void lshift128(w128_t *out, w128_t const *in, int shift);
|
||||
|
||||
/**
|
||||
* This function simulates SIMD 128-bit right shift by the standard C.
|
||||
* The 128-bit integer given in in is shifted by (shift * 8) bits.
|
||||
* This function simulates the LITTLE ENDIAN SIMD.
|
||||
* @param out the output of this function
|
||||
* @param in the 128-bit data to be shifted
|
||||
* @param shift the shift value
|
||||
*/
|
||||
#ifdef ONLY64
|
||||
inline static void rshift128(w128_t *out, w128_t const *in, int shift) {
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);
|
||||
tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[0] = (uint32_t)(ol >> 32);
|
||||
out->u[1] = (uint32_t)ol;
|
||||
out->u[2] = (uint32_t)(oh >> 32);
|
||||
out->u[3] = (uint32_t)oh;
|
||||
}
|
||||
#else
|
||||
inline static void rshift128(w128_t *out, w128_t const *in, int shift)
|
||||
{
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);
|
||||
tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[1] = (uint32_t)(ol >> 32);
|
||||
out->u[0] = (uint32_t)ol;
|
||||
out->u[3] = (uint32_t)(oh >> 32);
|
||||
out->u[2] = (uint32_t)oh;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This function simulates SIMD 128-bit left shift by the standard C.
|
||||
* The 128-bit integer given in in is shifted by (shift * 8) bits.
|
||||
* This function simulates the LITTLE ENDIAN SIMD.
|
||||
* @param out the output of this function
|
||||
* @param in the 128-bit data to be shifted
|
||||
* @param shift the shift value
|
||||
*/
|
||||
#ifdef ONLY64
|
||||
inline static void lshift128(w128_t *out, w128_t const *in, int shift) {
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);
|
||||
tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[0] = (uint32_t)(ol >> 32);
|
||||
out->u[1] = (uint32_t)ol;
|
||||
out->u[2] = (uint32_t)(oh >> 32);
|
||||
out->u[3] = (uint32_t)oh;
|
||||
}
|
||||
#else
|
||||
inline static void lshift128(w128_t *out, w128_t const *in, int shift)
|
||||
{
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);
|
||||
tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[1] = (uint32_t)(ol >> 32);
|
||||
out->u[0] = (uint32_t)ol;
|
||||
out->u[3] = (uint32_t)(oh >> 32);
|
||||
out->u[2] = (uint32_t)oh;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This function represents the recursion formula.
|
||||
* @param r output
|
||||
* @param a a 128-bit part of the internal state array
|
||||
* @param b a 128-bit part of the internal state array
|
||||
* @param c a 128-bit part of the internal state array
|
||||
* @param d a 128-bit part of the internal state array
|
||||
*/
|
||||
#ifdef ONLY64
|
||||
inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,
|
||||
w128_t *d) {
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
|
||||
lshift128(&x, a, SFMT_SL2);
|
||||
rshift128(&y, c, SFMT_SR2);
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SFMT_SR1) & SFMT_MSK2) ^ y.u[0]
|
||||
^ (d->u[0] << SFMT_SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SFMT_SR1) & SFMT_MSK1) ^ y.u[1]
|
||||
^ (d->u[1] << SFMT_SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SFMT_SR1) & SFMT_MSK4) ^ y.u[2]
|
||||
^ (d->u[2] << SFMT_SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SFMT_SR1) & SFMT_MSK3) ^ y.u[3]
|
||||
^ (d->u[3] << SFMT_SL1);
|
||||
}
|
||||
#else
|
||||
inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b,
|
||||
w128_t *c, w128_t *d)
|
||||
{
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
|
||||
lshift128(&x, a, SFMT_SL2);
|
||||
rshift128(&y, c, SFMT_SR2);
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SFMT_SR1) & SFMT_MSK1)
|
||||
^ y.u[0] ^ (d->u[0] << SFMT_SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SFMT_SR1) & SFMT_MSK2)
|
||||
^ y.u[1] ^ (d->u[1] << SFMT_SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SFMT_SR1) & SFMT_MSK3)
|
||||
^ y.u[2] ^ (d->u[2] << SFMT_SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SFMT_SR1) & SFMT_MSK4)
|
||||
^ y.u[3] ^ (d->u[3] << SFMT_SL1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
|
@ -1,95 +1,96 @@
|
|||
#pragma once
|
||||
#ifndef SFMT_PARAMS_H
|
||||
#define SFMT_PARAMS_H
|
||||
|
||||
#if !defined(MEXP)
|
||||
#ifdef __GNUC__
|
||||
#warning "MEXP is not defined. I assume MEXP is 19937."
|
||||
#if !defined(SFMT_MEXP)
|
||||
#if defined(__GNUC__) && !defined(__ICC)
|
||||
#warning "SFMT_MEXP is not defined. I assume MEXP is 19937."
|
||||
#endif
|
||||
#define MEXP 19937
|
||||
#define SFMT_MEXP 19937
|
||||
#endif
|
||||
/*-----------------
|
||||
BASIC DEFINITIONS
|
||||
-----------------*/
|
||||
/** Mersenne Exponent. The period of the sequence
|
||||
/** Mersenne Exponent. The period of the sequence
|
||||
* is a multiple of 2^MEXP-1.
|
||||
* #define MEXP 19937 */
|
||||
* #define SFMT_MEXP 19937 */
|
||||
/** SFMT generator has an internal state array of 128-bit integers,
|
||||
* and N is its size. */
|
||||
#define N (MEXP / 128 + 1)
|
||||
#define SFMT_N (SFMT_MEXP / 128 + 1)
|
||||
/** N32 is the size of internal state array when regarded as an array
|
||||
* of 32-bit integers.*/
|
||||
#define N32 (N * 4)
|
||||
#define SFMT_N32 (SFMT_N * 4)
|
||||
/** N64 is the size of internal state array when regarded as an array
|
||||
* of 64-bit integers.*/
|
||||
#define N64 (N * 2)
|
||||
#define SFMT_N64 (SFMT_N * 2)
|
||||
|
||||
/*----------------------
|
||||
the parameters of SFMT
|
||||
following definitions are in paramsXXXX.h file.
|
||||
----------------------*/
|
||||
/** the pick up position of the array.
|
||||
#define POS1 122
|
||||
#define SFMT_POS1 122
|
||||
*/
|
||||
|
||||
/** the parameter of shift left as four 32-bit registers.
|
||||
#define SL1 18
|
||||
#define SFMT_SL1 18
|
||||
*/
|
||||
|
||||
/** the parameter of shift left as one 128-bit register.
|
||||
* The 128-bit integer is shifted by (SL2 * 8) bits.
|
||||
#define SL2 1
|
||||
/** the parameter of shift left as one 128-bit register.
|
||||
* The 128-bit integer is shifted by (SFMT_SL2 * 8) bits.
|
||||
#define SFMT_SL2 1
|
||||
*/
|
||||
|
||||
/** the parameter of shift right as four 32-bit registers.
|
||||
#define SR1 11
|
||||
#define SFMT_SR1 11
|
||||
*/
|
||||
|
||||
/** the parameter of shift right as one 128-bit register.
|
||||
* The 128-bit integer is shifted by (SL2 * 8) bits.
|
||||
#define SR2 1
|
||||
/** the parameter of shift right as one 128-bit register.
|
||||
* The 128-bit integer is shifted by (SFMT_SL2 * 8) bits.
|
||||
#define SFMT_SR21 1
|
||||
*/
|
||||
|
||||
/** A bitmask, used in the recursion. These parameters are introduced
|
||||
* to break symmetry of SIMD.
|
||||
#define MSK1 0xdfffffefU
|
||||
#define MSK2 0xddfecb7fU
|
||||
#define MSK3 0xbffaffffU
|
||||
#define MSK4 0xbffffff6U
|
||||
#define SFMT_MSK1 0xdfffffefU
|
||||
#define SFMT_MSK2 0xddfecb7fU
|
||||
#define SFMT_MSK3 0xbffaffffU
|
||||
#define SFMT_MSK4 0xbffffff6U
|
||||
*/
|
||||
|
||||
/** These definitions are part of a 128-bit period certification vector.
|
||||
#define PARITY1 0x00000001U
|
||||
#define PARITY2 0x00000000U
|
||||
#define PARITY3 0x00000000U
|
||||
#define PARITY4 0xc98e126aU
|
||||
#define SFMT_PARITY1 0x00000001U
|
||||
#define SFMT_PARITY2 0x00000000U
|
||||
#define SFMT_PARITY3 0x00000000U
|
||||
#define SFMT_PARITY4 0xc98e126aU
|
||||
*/
|
||||
|
||||
#if MEXP == 607
|
||||
#if SFMT_MEXP == 607
|
||||
#include "SFMT-params607.h"
|
||||
#elif MEXP == 1279
|
||||
#elif SFMT_MEXP == 1279
|
||||
#include "SFMT-params1279.h"
|
||||
#elif MEXP == 2281
|
||||
#elif SFMT_MEXP == 2281
|
||||
#include "SFMT-params2281.h"
|
||||
#elif MEXP == 4253
|
||||
#elif SFMT_MEXP == 4253
|
||||
#include "SFMT-params4253.h"
|
||||
#elif MEXP == 11213
|
||||
#elif SFMT_MEXP == 11213
|
||||
#include "SFMT-params11213.h"
|
||||
#elif MEXP == 19937
|
||||
#elif SFMT_MEXP == 19937
|
||||
#include "SFMT-params19937.h"
|
||||
#elif MEXP == 44497
|
||||
#elif SFMT_MEXP == 44497
|
||||
#include "SFMT-params44497.h"
|
||||
#elif MEXP == 86243
|
||||
#elif SFMT_MEXP == 86243
|
||||
#include "SFMT-params86243.h"
|
||||
#elif MEXP == 132049
|
||||
#elif SFMT_MEXP == 132049
|
||||
#include "SFMT-params132049.h"
|
||||
#elif MEXP == 216091
|
||||
#elif SFMT_MEXP == 216091
|
||||
#include "SFMT-params216091.h"
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
#error "MEXP is not valid."
|
||||
#undef MEXP
|
||||
#if defined(__GNUC__) && !defined(__ICC)
|
||||
#error "SFMT_MEXP is not valid."
|
||||
#undef SFMT_MEXP
|
||||
#else
|
||||
#undef MEXP
|
||||
#undef SFMT_MEXP
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,46 +1,50 @@
|
|||
#pragma once
|
||||
#ifndef SFMT_PARAMS19937_H
|
||||
#define SFMT_PARAMS19937_H
|
||||
|
||||
#define POS1 122
|
||||
#define SL1 18
|
||||
#define SL2 1
|
||||
#define SR1 11
|
||||
#define SR2 1
|
||||
#define MSK1 0xdfffffefU
|
||||
#define MSK2 0xddfecb7fU
|
||||
#define MSK3 0xbffaffffU
|
||||
#define MSK4 0xbffffff6U
|
||||
#define PARITY1 0x00000001U
|
||||
#define PARITY2 0x00000000U
|
||||
#define PARITY3 0x00000000U
|
||||
#define PARITY4 0x13c9e684U
|
||||
#define SFMT_POS1 122
|
||||
#define SFMT_SL1 18
|
||||
#define SFMT_SL2 1
|
||||
#define SFMT_SR1 11
|
||||
#define SFMT_SR2 1
|
||||
#define SFMT_MSK1 0xdfffffefU
|
||||
#define SFMT_MSK2 0xddfecb7fU
|
||||
#define SFMT_MSK3 0xbffaffffU
|
||||
#define SFMT_MSK4 0xbffffff6U
|
||||
#define SFMT_PARITY1 0x00000001U
|
||||
#define SFMT_PARITY2 0x00000000U
|
||||
#define SFMT_PARITY3 0x00000000U
|
||||
#define SFMT_PARITY4 0x13c9e684U
|
||||
|
||||
|
||||
/* PARAMETERS FOR ALTIVEC */
|
||||
#if defined(__APPLE__) /* For OSX */
|
||||
#define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1)
|
||||
#define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1)
|
||||
#define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4)
|
||||
#define ALTI_MSK64 \
|
||||
(vector unsigned int)(MSK2, MSK1, MSK4, MSK3)
|
||||
#define ALTI_SL2_PERM \
|
||||
#define SFMT_ALTI_SL1 \
|
||||
(vector unsigned int)(SFMT_SL1, SFMT_SL1, SFMT_SL1, SFMT_SL1)
|
||||
#define SFMT_ALTI_SR1 \
|
||||
(vector unsigned int)(SFMT_SR1, SFMT_SR1, SFMT_SR1, SFMT_SR1)
|
||||
#define SFMT_ALTI_MSK \
|
||||
(vector unsigned int)(SFMT_MSK1, SFMT_MSK2, SFMT_MSK3, SFMT_MSK4)
|
||||
#define SFMT_ALTI_MSK64 \
|
||||
(vector unsigned int)(SFMT_MSK2, SFMT_MSK1, SFMT_MSK4, SFMT_MSK3)
|
||||
#define SFMT_ALTI_SL2_PERM \
|
||||
(vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8)
|
||||
#define ALTI_SL2_PERM64 \
|
||||
#define SFMT_ALTI_SL2_PERM64 \
|
||||
(vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0)
|
||||
#define ALTI_SR2_PERM \
|
||||
#define SFMT_ALTI_SR2_PERM \
|
||||
(vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14)
|
||||
#define ALTI_SR2_PERM64 \
|
||||
#define SFMT_ALTI_SR2_PERM64 \
|
||||
(vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14)
|
||||
#else /* For OTHER OSs(Linux?) */
|
||||
#define ALTI_SL1 {SL1, SL1, SL1, SL1}
|
||||
#define ALTI_SR1 {SR1, SR1, SR1, SR1}
|
||||
#define ALTI_MSK {MSK1, MSK2, MSK3, MSK4}
|
||||
#define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3}
|
||||
#define ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}
|
||||
#define ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}
|
||||
#define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}
|
||||
#define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}
|
||||
#define SFMT_ALTI_SL1 {SFMT_SL1, SFMT_SL1, SFMT_SL1, SFMT_SL1}
|
||||
#define SFMT_ALTI_SR1 {SFMT_SR1, SFMT_SR1, SFMT_SR1, SFMT_SR1}
|
||||
#define SFMT_ALTI_MSK {SFMT_MSK1, SFMT_MSK2, SFMT_MSK3, SFMT_MSK4}
|
||||
#define SFMT_ALTI_MSK64 {SFMT_MSK2, SFMT_MSK1, SFMT_MSK4, SFMT_MSK3}
|
||||
#define SFMT_ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8}
|
||||
#define SFMT_ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0}
|
||||
#define SFMT_ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14}
|
||||
#define SFMT_ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14}
|
||||
#endif /* For OSX */
|
||||
#define IDSTR "SFMT-19937:122-18-1-11-1:dfffffef-ddfecb7f-bffaffff-bffffff6"
|
||||
#define SFMT_IDSTR "SFMT-19937:122-18-1-11-1:dfffffef-ddfecb7f-bffaffff-bffffff6"
|
||||
|
||||
#endif /* SFMT_PARAMS19937_H */
|
||||
|
|
|
@ -1,19 +1,31 @@
|
|||
/**
|
||||
/**
|
||||
* @file SFMT.c
|
||||
* @brief SIMD oriented Fast Mersenne Twister(SFMT)
|
||||
*
|
||||
* @author Mutsuo Saito (Hiroshima University)
|
||||
* @author Makoto Matsumoto (Hiroshima University)
|
||||
*
|
||||
* Copyright (C) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima
|
||||
* University. All rights reserved.
|
||||
* Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima
|
||||
* University.
|
||||
* Copyright (C) 2012 Mutsuo Saito, Makoto Matsumoto, Hiroshima
|
||||
* University and The University of Tokyo.
|
||||
* Copyright (C) 2013 Mutsuo Saito, Makoto Matsumoto and Hiroshima
|
||||
* University.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The new BSD License is applied to this software, see LICENSE.txt
|
||||
* The 3-clause BSD License is applied to this software, see
|
||||
* LICENSE.txt
|
||||
*/
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "SFMT.h"
|
||||
#include "SFMT-params.h"
|
||||
#include "SFMT-common.h"
|
||||
|
||||
#if defined(__BIG_ENDIAN__) && !defined(__amd64) && !defined(BIG_ENDIAN64)
|
||||
#define BIG_ENDIAN64 1
|
||||
|
@ -27,74 +39,20 @@
|
|||
#endif
|
||||
#undef ONLY64
|
||||
#endif
|
||||
/*------------------------------------------------------
|
||||
128-bit SIMD data type for Altivec, SSE2 or standard C
|
||||
------------------------------------------------------*/
|
||||
#if defined(HAVE_ALTIVEC)
|
||||
#if !defined(__APPLE__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
/** 128-bit data structure */
|
||||
union W128_T {
|
||||
vector unsigned int s;
|
||||
uint32_t u[4];
|
||||
};
|
||||
/** 128-bit data type */
|
||||
typedef union W128_T w128_t;
|
||||
|
||||
#elif defined(HAVE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
|
||||
/** 128-bit data structure */
|
||||
union W128_T {
|
||||
__m128i si;
|
||||
uint32_t u[4];
|
||||
};
|
||||
/** 128-bit data type */
|
||||
typedef union W128_T w128_t;
|
||||
|
||||
#else
|
||||
|
||||
/** 128-bit data structure */
|
||||
struct W128_T {
|
||||
uint32_t u[4];
|
||||
};
|
||||
/** 128-bit data type */
|
||||
typedef struct W128_T w128_t;
|
||||
|
||||
#endif
|
||||
|
||||
/*--------------------------------------
|
||||
FILE GLOBAL VARIABLES
|
||||
internal state, index counter and flag
|
||||
--------------------------------------*/
|
||||
/** the 128-bit internal state array */
|
||||
static w128_t sfmt[N];
|
||||
/** the 32bit integer pointer to the 128-bit internal state array */
|
||||
static uint32_t *psfmt32 = &sfmt[0].u[0];
|
||||
#if !defined(BIG_ENDIAN64) || defined(ONLY64)
|
||||
/** the 64bit integer pointer to the 128-bit internal state array */
|
||||
static uint64_t *psfmt64 = (uint64_t *)&sfmt[0].u[0];
|
||||
#endif
|
||||
/** index counter to the 32-bit internal state array */
|
||||
static int idx;
|
||||
/** a flag: it is 0 if and only if the internal state is not yet
|
||||
* initialized. */
|
||||
static int initialized = 0;
|
||||
/** a parity check vector which certificate the period of 2^{MEXP} */
|
||||
static uint32_t parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4};
|
||||
|
||||
/**
|
||||
* parameters used by sse2.
|
||||
*/
|
||||
static const w128_t sse2_param_mask = {{SFMT_MSK1, SFMT_MSK2,
|
||||
SFMT_MSK3, SFMT_MSK4}};
|
||||
/*----------------
|
||||
STATIC FUNCTIONS
|
||||
----------------*/
|
||||
inline static int idxof(int i);
|
||||
inline static void rshift128(w128_t *out, w128_t const *in, int shift);
|
||||
inline static void lshift128(w128_t *out, w128_t const *in, int shift);
|
||||
inline static void gen_rand_all(void);
|
||||
inline static void gen_rand_array(w128_t *array, int size);
|
||||
inline static void gen_rand_array(sfmt_t * sfmt, w128_t *array, int size);
|
||||
inline static uint32_t func1(uint32_t x);
|
||||
inline static uint32_t func2(uint32_t x);
|
||||
static void period_certification(void);
|
||||
static void period_certification(sfmt_t * sfmt);
|
||||
#if defined(BIG_ENDIAN64) && !defined(ONLY64)
|
||||
inline static void swap(w128_t *array, int size);
|
||||
#endif
|
||||
|
@ -102,11 +60,15 @@ inline static void swap(w128_t *array, int size);
|
|||
#if defined(HAVE_ALTIVEC)
|
||||
#include "SFMT-alti.h"
|
||||
#elif defined(HAVE_SSE2)
|
||||
#include "SFMT-sse2.h"
|
||||
#if defined(_MSC_VER)
|
||||
#include "SFMT-sse2-msc.h"
|
||||
#else
|
||||
#include "SFMT-sse2.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function simulate a 64-bit index of LITTLE ENDIAN
|
||||
* This function simulate a 64-bit index of LITTLE ENDIAN
|
||||
* in BIG ENDIAN machine.
|
||||
*/
|
||||
#ifdef ONLY64
|
||||
|
@ -118,190 +80,48 @@ inline static int idxof(int i) {
|
|||
return i;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This function simulates SIMD 128-bit right shift by the standard C.
|
||||
* The 128-bit integer given in in is shifted by (shift * 8) bits.
|
||||
* This function simulates the LITTLE ENDIAN SIMD.
|
||||
* @param out the output of this function
|
||||
* @param in the 128-bit data to be shifted
|
||||
* @param shift the shift value
|
||||
*/
|
||||
#ifdef ONLY64
|
||||
inline static void rshift128(w128_t *out, w128_t const *in, int shift) {
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);
|
||||
tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[0] = (uint32_t)(ol >> 32);
|
||||
out->u[1] = (uint32_t)ol;
|
||||
out->u[2] = (uint32_t)(oh >> 32);
|
||||
out->u[3] = (uint32_t)oh;
|
||||
}
|
||||
#else
|
||||
inline static void rshift128(w128_t *out, w128_t const *in, int shift) {
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);
|
||||
tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);
|
||||
|
||||
oh = th >> (shift * 8);
|
||||
ol = tl >> (shift * 8);
|
||||
ol |= th << (64 - shift * 8);
|
||||
out->u[1] = (uint32_t)(ol >> 32);
|
||||
out->u[0] = (uint32_t)ol;
|
||||
out->u[3] = (uint32_t)(oh >> 32);
|
||||
out->u[2] = (uint32_t)oh;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This function simulates SIMD 128-bit left shift by the standard C.
|
||||
* The 128-bit integer given in in is shifted by (shift * 8) bits.
|
||||
* This function simulates the LITTLE ENDIAN SIMD.
|
||||
* @param out the output of this function
|
||||
* @param in the 128-bit data to be shifted
|
||||
* @param shift the shift value
|
||||
*/
|
||||
#ifdef ONLY64
|
||||
inline static void lshift128(w128_t *out, w128_t const *in, int shift) {
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]);
|
||||
tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[0] = (uint32_t)(ol >> 32);
|
||||
out->u[1] = (uint32_t)ol;
|
||||
out->u[2] = (uint32_t)(oh >> 32);
|
||||
out->u[3] = (uint32_t)oh;
|
||||
}
|
||||
#else
|
||||
inline static void lshift128(w128_t *out, w128_t const *in, int shift) {
|
||||
uint64_t th, tl, oh, ol;
|
||||
|
||||
th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]);
|
||||
tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]);
|
||||
|
||||
oh = th << (shift * 8);
|
||||
ol = tl << (shift * 8);
|
||||
oh |= tl >> (64 - shift * 8);
|
||||
out->u[1] = (uint32_t)(ol >> 32);
|
||||
out->u[0] = (uint32_t)ol;
|
||||
out->u[3] = (uint32_t)(oh >> 32);
|
||||
out->u[2] = (uint32_t)oh;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function represents the recursion formula.
|
||||
* @param r output
|
||||
* @param a a 128-bit part of the internal state array
|
||||
* @param b a 128-bit part of the internal state array
|
||||
* @param c a 128-bit part of the internal state array
|
||||
* @param d a 128-bit part of the internal state array
|
||||
*/
|
||||
#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))
|
||||
#ifdef ONLY64
|
||||
inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,
|
||||
w128_t *d) {
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
|
||||
lshift128(&x, a, SL2);
|
||||
rshift128(&y, c, SR2);
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0]
|
||||
^ (d->u[0] << SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1]
|
||||
^ (d->u[1] << SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2]
|
||||
^ (d->u[2] << SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3]
|
||||
^ (d->u[3] << SL1);
|
||||
}
|
||||
#else
|
||||
inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c,
|
||||
w128_t *d) {
|
||||
w128_t x;
|
||||
w128_t y;
|
||||
|
||||
lshift128(&x, a, SL2);
|
||||
rshift128(&y, c, SR2);
|
||||
r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0]
|
||||
^ (d->u[0] << SL1);
|
||||
r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1]
|
||||
^ (d->u[1] << SL1);
|
||||
r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2]
|
||||
^ (d->u[2] << SL1);
|
||||
r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3]
|
||||
^ (d->u[3] << SL1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2))
|
||||
/**
|
||||
* This function fills the internal state array with pseudorandom
|
||||
* integers.
|
||||
*/
|
||||
inline static void gen_rand_all(void) {
|
||||
int i;
|
||||
w128_t *r1, *r2;
|
||||
|
||||
r1 = &sfmt[N - 2];
|
||||
r2 = &sfmt[N - 1];
|
||||
for (i = 0; i < N - POS1; i++) {
|
||||
do_recursion(&sfmt[i], &sfmt[i], &sfmt[i + POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &sfmt[i];
|
||||
}
|
||||
for (; i < N; i++) {
|
||||
do_recursion(&sfmt[i], &sfmt[i], &sfmt[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &sfmt[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function fills the user-specified array with pseudorandom
|
||||
* integers.
|
||||
*
|
||||
* @param array an 128-bit array to be filled by pseudorandom numbers.
|
||||
* @param sfmt SFMT internal state
|
||||
* @param array an 128-bit array to be filled by pseudorandom numbers.
|
||||
* @param size number of 128-bit pseudorandom numbers to be generated.
|
||||
*/
|
||||
inline static void gen_rand_array(w128_t *array, int size) {
|
||||
inline static void gen_rand_array(sfmt_t * sfmt, w128_t *array, int size) {
|
||||
int i, j;
|
||||
w128_t *r1, *r2;
|
||||
|
||||
r1 = &sfmt[N - 2];
|
||||
r2 = &sfmt[N - 1];
|
||||
for (i = 0; i < N - POS1; i++) {
|
||||
do_recursion(&array[i], &sfmt[i], &sfmt[i + POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
r1 = &sfmt->state[SFMT_N - 2];
|
||||
r2 = &sfmt->state[SFMT_N - 1];
|
||||
for (i = 0; i < SFMT_N - SFMT_POS1; i++) {
|
||||
do_recursion(&array[i], &sfmt->state[i], &sfmt->state[i + SFMT_POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (; i < N; i++) {
|
||||
do_recursion(&array[i], &sfmt[i], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
for (; i < SFMT_N; i++) {
|
||||
do_recursion(&array[i], &sfmt->state[i],
|
||||
&array[i + SFMT_POS1 - SFMT_N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (; i < size - N; i++) {
|
||||
do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
for (; i < size - SFMT_N; i++) {
|
||||
do_recursion(&array[i], &array[i - SFMT_N],
|
||||
&array[i + SFMT_POS1 - SFMT_N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
}
|
||||
for (j = 0; j < 2 * N - size; j++) {
|
||||
sfmt[j] = array[j + size - N];
|
||||
for (j = 0; j < 2 * SFMT_N - size; j++) {
|
||||
sfmt->state[j] = array[j + size - SFMT_N];
|
||||
}
|
||||
for (; i < size; i++, j++) {
|
||||
do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
sfmt[j] = array[i];
|
||||
do_recursion(&array[i], &array[i - SFMT_N],
|
||||
&array[i + SFMT_POS1 - SFMT_N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &array[i];
|
||||
sfmt->state[j] = array[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -312,12 +132,12 @@ inline static void swap(w128_t *array, int size) {
|
|||
uint32_t x, y;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
x = array[i].u[0];
|
||||
y = array[i].u[2];
|
||||
array[i].u[0] = array[i].u[1];
|
||||
array[i].u[2] = array[i].u[3];
|
||||
array[i].u[1] = x;
|
||||
array[i].u[3] = y;
|
||||
x = array[i].u[0];
|
||||
y = array[i].u[2];
|
||||
array[i].u[0] = array[i].u[1];
|
||||
array[i].u[2] = array[i].u[3];
|
||||
array[i].u[1] = x;
|
||||
array[i].u[3] = y;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -343,114 +163,101 @@ static uint32_t func2(uint32_t x) {
|
|||
|
||||
/**
|
||||
* This function certificate the period of 2^{MEXP}
|
||||
* @param sfmt SFMT internal state
|
||||
*/
|
||||
static void period_certification(void) {
|
||||
static void period_certification(sfmt_t * sfmt) {
|
||||
int inner = 0;
|
||||
int i, j;
|
||||
uint32_t work;
|
||||
uint32_t *psfmt32 = &sfmt->state[0].u[0];
|
||||
const uint32_t parity[4] = {SFMT_PARITY1, SFMT_PARITY2,
|
||||
SFMT_PARITY3, SFMT_PARITY4};
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
inner ^= psfmt32[idxof(i)] & parity[i];
|
||||
inner ^= psfmt32[idxof(i)] & parity[i];
|
||||
for (i = 16; i > 0; i >>= 1)
|
||||
inner ^= inner >> i;
|
||||
inner ^= inner >> i;
|
||||
inner &= 1;
|
||||
/* check OK */
|
||||
if (inner == 1) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
/* check NG, and modification */
|
||||
for (i = 0; i < 4; i++) {
|
||||
work = 1;
|
||||
for (j = 0; j < 32; j++) {
|
||||
if ((work & parity[i]) != 0) {
|
||||
psfmt32[idxof(i)] ^= work;
|
||||
return;
|
||||
}
|
||||
work = work << 1;
|
||||
}
|
||||
work = 1;
|
||||
for (j = 0; j < 32; j++) {
|
||||
if ((work & parity[i]) != 0) {
|
||||
psfmt32[idxof(i)] ^= work;
|
||||
return;
|
||||
}
|
||||
work = work << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------
|
||||
PUBLIC FUNCTIONS
|
||||
----------------*/
|
||||
#define UNUSED_VARIABLE(x) (void)(x)
|
||||
/**
|
||||
* This function returns the identification string.
|
||||
* The string shows the word size, the Mersenne exponent,
|
||||
* and all parameters of this generator.
|
||||
* @param sfmt SFMT internal state
|
||||
*/
|
||||
const char *get_idstring(void) {
|
||||
return IDSTR;
|
||||
const char *sfmt_get_idstring(sfmt_t * sfmt) {
|
||||
UNUSED_VARIABLE(sfmt);
|
||||
return SFMT_IDSTR;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the minimum size of array used for \b
|
||||
* fill_array32() function.
|
||||
* @param sfmt SFMT internal state
|
||||
* @return minimum size of array used for fill_array32() function.
|
||||
*/
|
||||
int get_min_array_size32(void) {
|
||||
return N32;
|
||||
int sfmt_get_min_array_size32(sfmt_t * sfmt) {
|
||||
UNUSED_VARIABLE(sfmt);
|
||||
return SFMT_N32;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the minimum size of array used for \b
|
||||
* fill_array64() function.
|
||||
* @param sfmt SFMT internal state
|
||||
* @return minimum size of array used for fill_array64() function.
|
||||
*/
|
||||
int get_min_array_size64(void) {
|
||||
return N64;
|
||||
int sfmt_get_min_array_size64(sfmt_t * sfmt) {
|
||||
UNUSED_VARIABLE(sfmt);
|
||||
return SFMT_N64;
|
||||
}
|
||||
|
||||
#ifndef ONLY64
|
||||
#if !defined(HAVE_SSE2) && !defined(HAVE_ALTIVEC)
|
||||
/**
|
||||
* This function generates and returns 32-bit pseudorandom number.
|
||||
* init_gen_rand or init_by_array must be called before this function.
|
||||
* @return 32-bit pseudorandom number
|
||||
* This function fills the internal state array with pseudorandom
|
||||
* integers.
|
||||
* @param sfmt SFMT internal state
|
||||
*/
|
||||
uint32_t gen_rand32(void) {
|
||||
uint32_t r;
|
||||
void sfmt_gen_rand_all(sfmt_t * sfmt) {
|
||||
int i;
|
||||
w128_t *r1, *r2;
|
||||
|
||||
assert(initialized);
|
||||
if (idx >= N32) {
|
||||
gen_rand_all();
|
||||
idx = 0;
|
||||
r1 = &sfmt->state[SFMT_N - 2];
|
||||
r2 = &sfmt->state[SFMT_N - 1];
|
||||
for (i = 0; i < SFMT_N - SFMT_POS1; i++) {
|
||||
do_recursion(&sfmt->state[i], &sfmt->state[i],
|
||||
&sfmt->state[i + SFMT_POS1], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &sfmt->state[i];
|
||||
}
|
||||
for (; i < SFMT_N; i++) {
|
||||
do_recursion(&sfmt->state[i], &sfmt->state[i],
|
||||
&sfmt->state[i + SFMT_POS1 - SFMT_N], r1, r2);
|
||||
r1 = r2;
|
||||
r2 = &sfmt->state[i];
|
||||
}
|
||||
r = psfmt32[idx++];
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This function generates and returns 64-bit pseudorandom number.
|
||||
* init_gen_rand or init_by_array must be called before this function.
|
||||
* The function gen_rand64 should not be called after gen_rand32,
|
||||
* unless an initialization is again executed.
|
||||
* @return 64-bit pseudorandom number
|
||||
*/
|
||||
uint64_t gen_rand64(void) {
|
||||
#if defined(BIG_ENDIAN64) && !defined(ONLY64)
|
||||
uint32_t r1, r2;
|
||||
#else
|
||||
uint64_t r;
|
||||
#endif
|
||||
|
||||
assert(initialized);
|
||||
assert(idx % 2 == 0);
|
||||
|
||||
if (idx >= N32) {
|
||||
gen_rand_all();
|
||||
idx = 0;
|
||||
}
|
||||
#if defined(BIG_ENDIAN64) && !defined(ONLY64)
|
||||
r1 = psfmt32[idx];
|
||||
r2 = psfmt32[idx + 1];
|
||||
idx += 2;
|
||||
return ((uint64_t)r2 << 32) | r1;
|
||||
#else
|
||||
r = psfmt64[idx / 2];
|
||||
idx += 2;
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef ONLY64
|
||||
/**
|
||||
|
@ -464,6 +271,7 @@ uint64_t gen_rand64(void) {
|
|||
* before the first call of this function. This function can not be
|
||||
* used after calling gen_rand function, without initialization.
|
||||
*
|
||||
* @param sfmt SFMT internal state
|
||||
* @param array an array where pseudorandom 32-bit integers are filled
|
||||
* by this function. The pointer to the array must be \b "aligned"
|
||||
* (namely, must be a multiple of 16) in the SIMD version, since it
|
||||
|
@ -478,14 +286,13 @@ uint64_t gen_rand64(void) {
|
|||
* memory. Mac OSX doesn't have these functions, but \b malloc of OSX
|
||||
* returns the pointer to the aligned memory block.
|
||||
*/
|
||||
void fill_array32(uint32_t *array, int size) {
|
||||
assert(initialized);
|
||||
assert(idx == N32);
|
||||
void sfmt_fill_array32(sfmt_t * sfmt, uint32_t *array, int size) {
|
||||
assert(sfmt->idx == SFMT_N32);
|
||||
assert(size % 4 == 0);
|
||||
assert(size >= N32);
|
||||
assert(size >= SFMT_N32);
|
||||
|
||||
gen_rand_array((w128_t *)array, size / 4);
|
||||
idx = N32;
|
||||
gen_rand_array(sfmt, (w128_t *)array, size / 4);
|
||||
sfmt->idx = SFMT_N32;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -496,6 +303,7 @@ void fill_array32(uint32_t *array, int size) {
|
|||
* multiple of two. The generation by this function is much faster
|
||||
* than the following gen_rand function.
|
||||
*
|
||||
* @param sfmt SFMT internal state
|
||||
* For initialization, init_gen_rand or init_by_array must be called
|
||||
* before the first call of this function. This function can not be
|
||||
* used after calling gen_rand function, without initialization.
|
||||
|
@ -514,14 +322,13 @@ void fill_array32(uint32_t *array, int size) {
|
|||
* memory. Mac OSX doesn't have these functions, but \b malloc of OSX
|
||||
* returns the pointer to the aligned memory block.
|
||||
*/
|
||||
void fill_array64(uint64_t *array, int size) {
|
||||
assert(initialized);
|
||||
assert(idx == N32);
|
||||
void sfmt_fill_array64(sfmt_t * sfmt, uint64_t *array, int size) {
|
||||
assert(sfmt->idx == SFMT_N32);
|
||||
assert(size % 2 == 0);
|
||||
assert(size >= N64);
|
||||
assert(size >= SFMT_N64);
|
||||
|
||||
gen_rand_array((w128_t *)array, size / 2);
|
||||
idx = N32;
|
||||
gen_rand_array(sfmt, (w128_t *)array, size / 2);
|
||||
sfmt->idx = SFMT_N32;
|
||||
|
||||
#if defined(BIG_ENDIAN64) && !defined(ONLY64)
|
||||
swap((w128_t *)array, size /2);
|
||||
|
@ -532,54 +339,58 @@ void fill_array64(uint64_t *array, int size) {
|
|||
* This function initializes the internal state array with a 32-bit
|
||||
* integer seed.
|
||||
*
|
||||
* @param sfmt SFMT internal state
|
||||
* @param seed a 32-bit integer used as the seed.
|
||||
*/
|
||||
void init_gen_rand(uint32_t seed) {
|
||||
void sfmt_init_gen_rand(sfmt_t * sfmt, uint32_t seed) {
|
||||
int i;
|
||||
|
||||
uint32_t *psfmt32 = &sfmt->state[0].u[0];
|
||||
|
||||
psfmt32[idxof(0)] = seed;
|
||||
for (i = 1; i < N32; i++) {
|
||||
psfmt32[idxof(i)] = 1812433253UL * (psfmt32[idxof(i - 1)]
|
||||
^ (psfmt32[idxof(i - 1)] >> 30))
|
||||
+ i;
|
||||
for (i = 1; i < SFMT_N32; i++) {
|
||||
psfmt32[idxof(i)] = 1812433253UL * (psfmt32[idxof(i - 1)]
|
||||
^ (psfmt32[idxof(i - 1)] >> 30))
|
||||
+ i;
|
||||
}
|
||||
idx = N32;
|
||||
period_certification();
|
||||
initialized = 1;
|
||||
sfmt->idx = SFMT_N32;
|
||||
period_certification(sfmt);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function initializes the internal state array,
|
||||
* with an array of 32-bit integers used as the seeds
|
||||
* @param sfmt SFMT internal state
|
||||
* @param init_key the array of 32-bit integers, used as a seed.
|
||||
* @param key_length the length of init_key.
|
||||
*/
|
||||
void init_by_array(uint32_t *init_key, int key_length) {
|
||||
void sfmt_init_by_array(sfmt_t * sfmt, uint32_t *init_key, int key_length) {
|
||||
int i, j, count;
|
||||
uint32_t r;
|
||||
int lag;
|
||||
int mid;
|
||||
int size = N * 4;
|
||||
int size = SFMT_N * 4;
|
||||
uint32_t *psfmt32 = &sfmt->state[0].u[0];
|
||||
|
||||
if (size >= 623) {
|
||||
lag = 11;
|
||||
lag = 11;
|
||||
} else if (size >= 68) {
|
||||
lag = 7;
|
||||
lag = 7;
|
||||
} else if (size >= 39) {
|
||||
lag = 5;
|
||||
lag = 5;
|
||||
} else {
|
||||
lag = 3;
|
||||
lag = 3;
|
||||
}
|
||||
mid = (size - lag) / 2;
|
||||
|
||||
memset(sfmt, 0x8b, sizeof(sfmt));
|
||||
if (key_length + 1 > N32) {
|
||||
count = key_length + 1;
|
||||
memset(sfmt, 0x8b, sizeof(sfmt_t));
|
||||
if (key_length + 1 > SFMT_N32) {
|
||||
count = key_length + 1;
|
||||
} else {
|
||||
count = N32;
|
||||
count = SFMT_N32;
|
||||
}
|
||||
r = func1(psfmt32[idxof(0)] ^ psfmt32[idxof(mid)]
|
||||
^ psfmt32[idxof(N32 - 1)]);
|
||||
r = func1(psfmt32[idxof(0)] ^ psfmt32[idxof(mid)]
|
||||
^ psfmt32[idxof(SFMT_N32 - 1)]);
|
||||
psfmt32[idxof(mid)] += r;
|
||||
r += key_length;
|
||||
psfmt32[idxof(mid + lag)] += r;
|
||||
|
@ -587,34 +398,36 @@ void init_by_array(uint32_t *init_key, int key_length) {
|
|||
|
||||
count--;
|
||||
for (i = 1, j = 0; (j < count) && (j < key_length); j++) {
|
||||
r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)]
|
||||
^ psfmt32[idxof((i + N32 - 1) % N32)]);
|
||||
psfmt32[idxof((i + mid) % N32)] += r;
|
||||
r += init_key[j] + i;
|
||||
psfmt32[idxof((i + mid + lag) % N32)] += r;
|
||||
psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % SFMT_N32)]
|
||||
^ psfmt32[idxof((i + SFMT_N32 - 1) % SFMT_N32)]);
|
||||
psfmt32[idxof((i + mid) % SFMT_N32)] += r;
|
||||
r += init_key[j] + i;
|
||||
psfmt32[idxof((i + mid + lag) % SFMT_N32)] += r;
|
||||
psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % SFMT_N32;
|
||||
}
|
||||
for (; j < count; j++) {
|
||||
r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)]
|
||||
^ psfmt32[idxof((i + N32 - 1) % N32)]);
|
||||
psfmt32[idxof((i + mid) % N32)] += r;
|
||||
r += i;
|
||||
psfmt32[idxof((i + mid + lag) % N32)] += r;
|
||||
psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % SFMT_N32)]
|
||||
^ psfmt32[idxof((i + SFMT_N32 - 1) % SFMT_N32)]);
|
||||
psfmt32[idxof((i + mid) % SFMT_N32)] += r;
|
||||
r += i;
|
||||
psfmt32[idxof((i + mid + lag) % SFMT_N32)] += r;
|
||||
psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % SFMT_N32;
|
||||
}
|
||||
for (j = 0; j < N32; j++) {
|
||||
r = func2(psfmt32[idxof(i)] + psfmt32[idxof((i + mid) % N32)]
|
||||
+ psfmt32[idxof((i + N32 - 1) % N32)]);
|
||||
psfmt32[idxof((i + mid) % N32)] ^= r;
|
||||
r -= i;
|
||||
psfmt32[idxof((i + mid + lag) % N32)] ^= r;
|
||||
psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % N32;
|
||||
for (j = 0; j < SFMT_N32; j++) {
|
||||
r = func2(psfmt32[idxof(i)] + psfmt32[idxof((i + mid) % SFMT_N32)]
|
||||
+ psfmt32[idxof((i + SFMT_N32 - 1) % SFMT_N32)]);
|
||||
psfmt32[idxof((i + mid) % SFMT_N32)] ^= r;
|
||||
r -= i;
|
||||
psfmt32[idxof((i + mid + lag) % SFMT_N32)] ^= r;
|
||||
psfmt32[idxof(i)] = r;
|
||||
i = (i + 1) % SFMT_N32;
|
||||
}
|
||||
|
||||
idx = N32;
|
||||
period_certification();
|
||||
initialized = 1;
|
||||
sfmt->idx = SFMT_N32;
|
||||
period_certification(sfmt);
|
||||
}
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
/**
|
||||
* @file SFMT.h
|
||||
#pragma once
|
||||
/**
|
||||
* @file SFMT.h
|
||||
*
|
||||
* @brief SIMD oriented Fast Mersenne Twister(SFMT) pseudorandom
|
||||
* number generator
|
||||
* number generator using C structure.
|
||||
*
|
||||
* @author Mutsuo Saito (Hiroshima University)
|
||||
* @author Makoto Matsumoto (Hiroshima University)
|
||||
* @author Makoto Matsumoto (The University of Tokyo)
|
||||
*
|
||||
* Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima
|
||||
* University. All rights reserved.
|
||||
* University.
|
||||
* Copyright (C) 2012 Mutsuo Saito, Makoto Matsumoto, Hiroshima
|
||||
* University and The University of Tokyo.
|
||||
* All rights reserved.
|
||||
*
|
||||
* The new BSD License is applied to this software.
|
||||
* see LICENSE.txt
|
||||
* The 3-clause BSD License is applied to this software, see
|
||||
* LICENSE.txt
|
||||
*
|
||||
* @note We assume that your system has inttypes.h. If your system
|
||||
* doesn't have inttypes.h, you have to typedef uint32_t and uint64_t,
|
||||
* and you have to define PRIu64 and PRIx64 in this file as follows:
|
||||
* @verbatim
|
||||
typedef unsigned int uint32_t
|
||||
typedef unsigned long long uint64_t
|
||||
typedef unsigned long long uint64_t
|
||||
#define PRIu64 "llu"
|
||||
#define PRIx64 "llx"
|
||||
@endverbatim
|
||||
|
@ -28,14 +32,14 @@
|
|||
* unsigned int and 64-bit unsigned int in hexadecimal format.
|
||||
*/
|
||||
|
||||
#ifndef SFMT_H
|
||||
#define SFMT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#ifndef SFMTST_H
|
||||
#define SFMTST_H
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
#include <inttypes.h>
|
||||
|
@ -60,105 +64,232 @@ extern "C" {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ALWAYSINLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define ALWAYSINLINE
|
||||
#endif
|
||||
#include "SFMT-params.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if _MSC_VER >= 1200
|
||||
#define PRE_ALWAYS __forceinline
|
||||
#else
|
||||
#define PRE_ALWAYS inline
|
||||
/*------------------------------------------
|
||||
128-bit SIMD like data type for standard C
|
||||
------------------------------------------*/
|
||||
#if defined(HAVE_ALTIVEC)
|
||||
#if !defined(__APPLE__)
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
/** 128-bit data structure */
|
||||
union W128_T {
|
||||
vector unsigned int s;
|
||||
uint32_t u[4];
|
||||
uint64_t u64[2];
|
||||
};
|
||||
#elif defined(HAVE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
|
||||
/** 128-bit data structure */
|
||||
union W128_T {
|
||||
uint32_t u[4];
|
||||
uint64_t u64[2];
|
||||
__m128i si;
|
||||
};
|
||||
#else
|
||||
#define PRE_ALWAYS inline
|
||||
/** 128-bit data structure */
|
||||
union W128_T {
|
||||
uint32_t u[4];
|
||||
uint64_t u64[2];
|
||||
};
|
||||
#endif
|
||||
|
||||
uint32_t gen_rand32(void);
|
||||
uint64_t gen_rand64(void);
|
||||
void fill_array32(uint32_t *array, int size);
|
||||
void fill_array64(uint64_t *array, int size);
|
||||
void init_gen_rand(uint32_t seed);
|
||||
void init_by_array(uint32_t *init_key, int key_length);
|
||||
const char *get_idstring(void);
|
||||
int get_min_array_size32(void);
|
||||
int get_min_array_size64(void);
|
||||
/** 128-bit data type */
|
||||
typedef union W128_T w128_t;
|
||||
|
||||
/* These real versions are due to Isaku Wada */
|
||||
/** generates a random number on [0,1]-real-interval */
|
||||
inline static double to_real1(uint32_t v)
|
||||
{
|
||||
return v * (1.0/4294967295.0);
|
||||
/* divided by 2^32-1 */
|
||||
/**
|
||||
* SFMT internal state
|
||||
*/
|
||||
struct SFMT_T {
|
||||
/** the 128-bit internal state array */
|
||||
w128_t state[SFMT_N];
|
||||
/** index counter to the 32-bit internal state array */
|
||||
int idx;
|
||||
};
|
||||
|
||||
typedef struct SFMT_T sfmt_t;
|
||||
|
||||
void sfmt_fill_array32(sfmt_t * sfmt, uint32_t * array, int size);
|
||||
void sfmt_fill_array64(sfmt_t * sfmt, uint64_t * array, int size);
|
||||
void sfmt_init_gen_rand(sfmt_t * sfmt, uint32_t seed);
|
||||
void sfmt_init_by_array(sfmt_t * sfmt, uint32_t * init_key, int key_length);
|
||||
const char * sfmt_get_idstring(sfmt_t * sfmt);
|
||||
int sfmt_get_min_array_size32(sfmt_t * sfmt);
|
||||
int sfmt_get_min_array_size64(sfmt_t * sfmt);
|
||||
void sfmt_gen_rand_all(sfmt_t * sfmt);
|
||||
|
||||
#ifndef ONLY64
|
||||
/**
|
||||
* This function generates and returns 32-bit pseudorandom number.
|
||||
* init_gen_rand or init_by_array must be called before this function.
|
||||
* @param sfmt SFMT internal state
|
||||
* @return 32-bit pseudorandom number
|
||||
*/
|
||||
inline static uint32_t sfmt_genrand_uint32(sfmt_t * sfmt) {
|
||||
uint32_t r;
|
||||
uint32_t * psfmt32 = &sfmt->state[0].u[0];
|
||||
|
||||
if (sfmt->idx >= SFMT_N32) {
|
||||
sfmt_gen_rand_all(sfmt);
|
||||
sfmt->idx = 0;
|
||||
}
|
||||
r = psfmt32[sfmt->idx++];
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* This function generates and returns 64-bit pseudorandom number.
|
||||
* init_gen_rand or init_by_array must be called before this function.
|
||||
* The function gen_rand64 should not be called after gen_rand32,
|
||||
* unless an initialization is again executed.
|
||||
* @param sfmt SFMT internal state
|
||||
* @return 64-bit pseudorandom number
|
||||
*/
|
||||
inline static uint64_t sfmt_genrand_uint64(sfmt_t * sfmt) {
|
||||
#if defined(BIG_ENDIAN64) && !defined(ONLY64)
|
||||
uint32_t * psfmt32 = &sfmt->state[0].u[0];
|
||||
uint32_t r1, r2;
|
||||
#else
|
||||
uint64_t r;
|
||||
#endif
|
||||
uint64_t * psfmt64 = &sfmt->state[0].u64[0];
|
||||
assert(sfmt->idx % 2 == 0);
|
||||
|
||||
if (sfmt->idx >= SFMT_N32) {
|
||||
sfmt_gen_rand_all(sfmt);
|
||||
sfmt->idx = 0;
|
||||
}
|
||||
#if defined(BIG_ENDIAN64) && !defined(ONLY64)
|
||||
r1 = psfmt32[sfmt->idx];
|
||||
r2 = psfmt32[sfmt->idx + 1];
|
||||
sfmt->idx += 2;
|
||||
return ((uint64_t)r2 << 32) | r1;
|
||||
#else
|
||||
r = psfmt64[sfmt->idx / 2];
|
||||
sfmt->idx += 2;
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** generates a random number on [0,1]-real-interval */
|
||||
inline static double genrand_real1(void)
|
||||
/* =================================================
|
||||
The following real versions are due to Isaku Wada
|
||||
================================================= */
|
||||
/**
|
||||
* converts an unsigned 32-bit number to a double on [0,1]-real-interval.
|
||||
* @param v 32-bit unsigned integer
|
||||
* @return double on [0,1]-real-interval
|
||||
*/
|
||||
inline static double sfmt_to_real1(uint32_t v)
|
||||
{
|
||||
return to_real1(gen_rand32());
|
||||
return v * (1.0/4294967295.0);
|
||||
/* divided by 2^32-1 */
|
||||
}
|
||||
|
||||
/** generates a random number on [0,1)-real-interval */
|
||||
inline static double to_real2(uint32_t v)
|
||||
/**
|
||||
* generates a random number on [0,1]-real-interval
|
||||
* @param sfmt SFMT internal state
|
||||
* @return double on [0,1]-real-interval
|
||||
*/
|
||||
inline static double sfmt_genrand_real1(sfmt_t * sfmt)
|
||||
{
|
||||
return v * (1.0/4294967296.0);
|
||||
return sfmt_to_real1(sfmt_genrand_uint32(sfmt));
|
||||
}
|
||||
|
||||
/**
|
||||
* converts an unsigned 32-bit integer to a double on [0,1)-real-interval.
|
||||
* @param v 32-bit unsigned integer
|
||||
* @return double on [0,1)-real-interval
|
||||
*/
|
||||
inline static double sfmt_to_real2(uint32_t v)
|
||||
{
|
||||
return v * (1.0/4294967296.0);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
/** generates a random number on [0,1)-real-interval */
|
||||
inline static double genrand_real2(void)
|
||||
/**
|
||||
* generates a random number on [0,1)-real-interval
|
||||
* @param sfmt SFMT internal state
|
||||
* @return double on [0,1)-real-interval
|
||||
*/
|
||||
inline static double sfmt_genrand_real2(sfmt_t * sfmt)
|
||||
{
|
||||
return to_real2(gen_rand32());
|
||||
return sfmt_to_real2(sfmt_genrand_uint32(sfmt));
|
||||
}
|
||||
|
||||
/** generates a random number on (0,1)-real-interval */
|
||||
inline static double to_real3(uint32_t v)
|
||||
/**
|
||||
* converts an unsigned 32-bit integer to a double on (0,1)-real-interval.
|
||||
* @param v 32-bit unsigned integer
|
||||
* @return double on (0,1)-real-interval
|
||||
*/
|
||||
inline static double sfmt_to_real3(uint32_t v)
|
||||
{
|
||||
return (((double)v) + 0.5)*(1.0/4294967296.0);
|
||||
return (((double)v) + 0.5)*(1.0/4294967296.0);
|
||||
/* divided by 2^32 */
|
||||
}
|
||||
|
||||
/** generates a random number on (0,1)-real-interval */
|
||||
inline static double genrand_real3(void)
|
||||
/**
|
||||
* generates a random number on (0,1)-real-interval
|
||||
* @param sfmt SFMT internal state
|
||||
* @return double on (0,1)-real-interval
|
||||
*/
|
||||
inline static double sfmt_genrand_real3(sfmt_t * sfmt)
|
||||
{
|
||||
return to_real3(gen_rand32());
|
||||
}
|
||||
/** These real versions are due to Isaku Wada */
|
||||
|
||||
/** generates a random number on [0,1) with 53-bit resolution*/
|
||||
inline static double to_res53(uint64_t v)
|
||||
{
|
||||
return v * (1.0/18446744073709551616.0L);
|
||||
return sfmt_to_real3(sfmt_genrand_uint32(sfmt));
|
||||
}
|
||||
|
||||
/** generates a random number on [0,1) with 53-bit resolution from two
|
||||
* 32 bit integers */
|
||||
inline static double to_res53_mix(uint32_t x, uint32_t y)
|
||||
{
|
||||
return to_res53(x | ((uint64_t)y << 32));
|
||||
}
|
||||
|
||||
/** generates a random number on [0,1) with 53-bit resolution
|
||||
/**
|
||||
* converts an unsigned 32-bit integer to double on [0,1)
|
||||
* with 53-bit resolution.
|
||||
* @param v 32-bit unsigned integer
|
||||
* @return double on [0,1)-real-interval with 53-bit resolution.
|
||||
*/
|
||||
inline static double genrand_res53(void)
|
||||
{
|
||||
return to_res53(gen_rand64());
|
||||
}
|
||||
inline static double sfmt_to_res53(uint64_t v)
|
||||
{
|
||||
return v * (1.0/18446744073709551616.0);
|
||||
}
|
||||
|
||||
/** generates a random number on [0,1) with 53-bit resolution
|
||||
using 32bit integer.
|
||||
/**
|
||||
* generates a random number on [0,1) with 53-bit resolution
|
||||
* @param sfmt SFMT internal state
|
||||
* @return double on [0,1) with 53-bit resolution
|
||||
*/
|
||||
inline static double genrand_res53_mix(void)
|
||||
{
|
||||
inline static double sfmt_genrand_res53(sfmt_t * sfmt)
|
||||
{
|
||||
return sfmt_to_res53(sfmt_genrand_uint64(sfmt));
|
||||
}
|
||||
|
||||
|
||||
/* =================================================
|
||||
The following function are added by Saito.
|
||||
================================================= */
|
||||
/**
|
||||
* generates a random number on [0,1) with 53-bit resolution from two
|
||||
* 32 bit integers
|
||||
*/
|
||||
inline static double sfmt_to_res53_mix(uint32_t x, uint32_t y)
|
||||
{
|
||||
return sfmt_to_res53(x | ((uint64_t)y << 32));
|
||||
}
|
||||
|
||||
/**
|
||||
* generates a random number on [0,1) with 53-bit resolution
|
||||
* using two 32bit integers.
|
||||
* @param sfmt SFMT internal state
|
||||
* @return double on [0,1) with 53-bit resolution
|
||||
*/
|
||||
inline static double sfmt_genrand_res53_mix(sfmt_t * sfmt)
|
||||
{
|
||||
uint32_t x, y;
|
||||
|
||||
x = gen_rand32();
|
||||
y = gen_rand32();
|
||||
return to_res53_mix(x, y);
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
x = sfmt_genrand_uint32(sfmt);
|
||||
y = sfmt_genrand_uint32(sfmt);
|
||||
return sfmt_to_res53_mix(x, y);
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
689
doc/sets.xml
689
doc/sets.xml
|
@ -1,333 +1,254 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cockatrice_setdatabase version="20130507">
|
||||
<cockatrice_setdatabase version="20110126">
|
||||
<picture_url>http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!cardid!&type=card</picture_url>
|
||||
<set_url>http://gatherer.wizards.com/Pages/Search/Default.aspx?output=spoiler&method=text&set=["!longname!"]&special=true</set_url>
|
||||
<set import="1">
|
||||
<name>10E</name>
|
||||
<longname>Tenth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>15ANN</name>
|
||||
<longname>15th Anniversary</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>4E</name>
|
||||
<longname>Fourth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>5DN</name>
|
||||
<longname>Fifth Dawn</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>5E</name>
|
||||
<longname>Fifth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>6E</name>
|
||||
<longname>Classic Sixth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>7E</name>
|
||||
<longname>Seventh Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>8EB</name>
|
||||
<longname>Eighth Edition Box Set</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>8E</name>
|
||||
<longname>Eighth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>9EB</name>
|
||||
<longname>Ninth Edition Box Set</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>9E</name>
|
||||
<longname>Ninth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AI</name>
|
||||
<longname>Alliances</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ALA</name>
|
||||
<longname>Shards of Alara</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AL</name>
|
||||
<longname>Limited Edition Alpha</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AN</name>
|
||||
<longname>Arabian Nights</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>APAC</name>
|
||||
<longname>Asia Pacific Land Program</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AP</name>
|
||||
<longname>Apocalypse</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AQ</name>
|
||||
<longname>Antiquities</longname>
|
||||
</set>
|
||||
<!-- <picture_url_hq>http://mtgimage.com/multiverseid/!cardid!.jpg</picture_url_hq> -->
|
||||
<set_url>http://mtgjson.com/json/!name!.json</set_url>
|
||||
<set import="1">
|
||||
<name>ARB</name>
|
||||
<longname>Alara Reborn</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ALL</name>
|
||||
<longname>Alliances</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ATQ</name>
|
||||
<longname>Antiquities</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>APC</name>
|
||||
<longname>Apocalypse</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ARN</name>
|
||||
<longname>Arabian Nights</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>ARC</name>
|
||||
<longname>Archenemy</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ARENA</name>
|
||||
<longname>Arena League</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AT</name>
|
||||
<longname>Anthologies</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>AVR</name>
|
||||
<longname>Avacyn Restored</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>BD</name>
|
||||
<longname>Beatdown Box Set</longname>
|
||||
<set import="0">
|
||||
<name>BRB</name>
|
||||
<longname>Battle Royale Box Set</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>BE</name>
|
||||
<longname>Limited Edition Beta</longname>
|
||||
<set import="0">
|
||||
<name>BTD</name>
|
||||
<longname>Beatdown Box Set</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>BOK</name>
|
||||
<longname>Betrayers of Kamigawa</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>BR</name>
|
||||
<longname>Battle Royale Box Set</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CED</name>
|
||||
<longname>Collector's Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CEDI</name>
|
||||
<longname>International Collectors' Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CFX</name>
|
||||
<longname>Conflux</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CH</name>
|
||||
<longname>Chronicles</longname>
|
||||
<name>BNG</name>
|
||||
<longname>Born of the Gods</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CHK</name>
|
||||
<longname>Champions of Kamigawa</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CMA</name>
|
||||
<longname>Commander's Arsenal</longname>
|
||||
<name>CHR</name>
|
||||
<longname>Chronicles</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CMD</name>
|
||||
<longname>Commander</longname>
|
||||
<name>6ED</name>
|
||||
<longname>Classic Sixth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CP</name>
|
||||
<longname>Champs</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CS</name>
|
||||
<name>CSP</name>
|
||||
<longname>Coldsnap</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>CSTD</name>
|
||||
<longname>Coldsnap Theme Decks</longname>
|
||||
<name>CON</name>
|
||||
<longname>Conflux</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>CMA</name>
|
||||
<longname>Commander's Arsenal</longname>
|
||||
</set>
|
||||
<set import="0">^M
|
||||
<name>C13</name>^M
|
||||
<longname>Commander 2013 Edition</longname>^M
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DCILM</name>
|
||||
<longname>Legend Membership</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DDF</name>
|
||||
<longname>Duel Decks: Elspeth vs. Tezzeret</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DDG</name>
|
||||
<longname>Duel Decks: Knights vs. Dragons</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DDH</name>
|
||||
<longname>Duel Decks: Ajani vs. Nicol Bolas</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DDI</name>
|
||||
<longname>Duel Decks: Venser vs. Koth</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DDJ</name>
|
||||
<longname>Duel Decks: Izzet vs. Golgari</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DDK</name>
|
||||
<longname>Duel Decks: Sorin vs. Tibalt</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DGM</name>
|
||||
<longname>Dragon's Maze</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DI</name>
|
||||
<longname>Dissension</longname>
|
||||
<name>DST</name>
|
||||
<longname>Darksteel</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DKA</name>
|
||||
<longname>Dark Ascension</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DK</name>
|
||||
<longname>The Dark</longname>
|
||||
<name>DIS</name>
|
||||
<longname>Dissension</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DM</name>
|
||||
<longname>Deckmasters</longname>
|
||||
<name>DGM</name>
|
||||
<longname>Dragon's Maze</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DPA</name>
|
||||
<longname>Duels of the Planeswalkers</longname>
|
||||
<set import="0">
|
||||
<name>DDH</name>
|
||||
<longname>Duel Decks: Ajani vs. Nicol Bolas</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DRC</name>
|
||||
<longname>Dragon Con</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DS</name>
|
||||
<longname>Darksteel</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>DVD</name>
|
||||
<set import="0">
|
||||
<name>DDC</name>
|
||||
<longname>Duel Decks: Divine vs. Demonic</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDF</name>
|
||||
<longname>Duel Decks: Elspeth vs. Tezzeret</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>EVG</name>
|
||||
<longname>Duel Decks: Elves vs. Goblins</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDD</name>
|
||||
<longname>Duel Decks: Garruk vs. Liliana</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDL</name>
|
||||
<longname>Duel Decks: Heroes vs. Monsters</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDJ</name>
|
||||
<longname>Duel Decks: Izzet vs. Golgari</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DD2</name>
|
||||
<longname>Duel Decks: Jace vs. Chandra</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDM</name>
|
||||
<longname>Duel Decks: Jace vs. Vraska</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDG</name>
|
||||
<longname>Duel Decks: Knights vs. Dragons</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDE</name>
|
||||
<longname>Duel Decks: Phyrexia vs. the Coalition</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDK</name>
|
||||
<longname>Duel Decks: Sorin vs. Tibalt</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DDI</name>
|
||||
<longname>Duel Decks: Venser vs. Koth</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>EURO</name>
|
||||
<longname>European Land Program</longname>
|
||||
<name>8ED</name>
|
||||
<longname>Eighth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>EVE</name>
|
||||
<longname>Eventide</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>EVG</name>
|
||||
<longname>Duel Decks: Elves vs. Goblins</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>EX</name>
|
||||
<name>EXO</name>
|
||||
<longname>Exodus</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FE</name>
|
||||
<name>FEM</name>
|
||||
<longname>Fallen Empires</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FNMP</name>
|
||||
<longname>Friday Night Magic</longname>
|
||||
<name>5DN</name>
|
||||
<longname>Fifth Dawn</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>5ED</name>
|
||||
<longname>Fifth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>4ED</name>
|
||||
<longname>Fourth Edition</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>DRB</name>
|
||||
<longname>From the Vault: Dragons</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>V09</name>
|
||||
<longname>From the Vault: Exiled</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>V11</name>
|
||||
<longname>From the Vault: Legends</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>V12</name>
|
||||
<longname>From the Vault: Realms</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>V10</name>
|
||||
<longname>From the Vault: Relics</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>V13</name>
|
||||
<longname>From the Vault: Twenty</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FUT</name>
|
||||
<longname>Future Sight</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FVD</name>
|
||||
<longname>From the Vault: Dragons</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FVE</name>
|
||||
<longname>From the Vault: Exiled</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FVL</name>
|
||||
<longname>From the Vault: Legends</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>FVR</name>
|
||||
<longname>From the Vault: Relics</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>GP</name>
|
||||
<name>GPT</name>
|
||||
<longname>Guildpact</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>GPX</name>
|
||||
<longname>Grand Prix</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>GRC</name>
|
||||
<longname>WPN/Gateway</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>GTC</name>
|
||||
<longname>Gatecrash</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>GURU</name>
|
||||
<longname>Guru</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>GVL</name>
|
||||
<longname>Duel Decks: Garruk vs. Liliana</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>HHO</name>
|
||||
<longname>Happy Holidays</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>HL</name>
|
||||
<name>HML</name>
|
||||
<longname>Homelands</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>IA</name>
|
||||
<name>ICE</name>
|
||||
<longname>Ice Age</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>IN</name>
|
||||
<longname>Invasion</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ISD</name>
|
||||
<longname>Innistrad</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ITP</name>
|
||||
<longname>Introductory Two-Player Set</longname>
|
||||
<name>INV</name>
|
||||
<longname>Invasion</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>JR</name>
|
||||
<longname>Judge Gift Program</longname>
|
||||
<name>JOU</name>
|
||||
<longname>Journey into Nyx</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>JU</name>
|
||||
<name>JUD</name>
|
||||
<longname>Judgment</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>JVC</name>
|
||||
<longname>Duel Decks: Jace vs. Chandra</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>LE</name>
|
||||
<longname>Legions</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>LG</name>
|
||||
<name>LEG</name>
|
||||
<longname>Legends</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>LW</name>
|
||||
<name>LGN</name>
|
||||
<longname>Legions</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>LEA</name>
|
||||
<longname>Limited Edition Alpha</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>LEB</name>
|
||||
<longname>Limited Edition Beta</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>LRW</name>
|
||||
<longname>Lorwyn</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
|
@ -347,63 +268,59 @@
|
|||
<longname>Magic 2013</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MBP</name>
|
||||
<longname>Media Inserts</longname>
|
||||
<name>M14</name>
|
||||
<longname>Magic 2014 Core Set</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>CMD</name>
|
||||
<longname>Magic: The Gathering-Commander</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>CNS</name>
|
||||
<longname>Magic: The Gathering—Conspiracy</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>MED</name>
|
||||
<longname>Masters Edition</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>ME2</name>
|
||||
<longname>Masters Edition II</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>ME3</name>
|
||||
<longname>Masters Edition III</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>ME4</name>
|
||||
<longname>Masters Edition IV</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MMQ</name>
|
||||
<longname>Mercadian Masques</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MIR</name>
|
||||
<longname>Mirage</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MRD</name>
|
||||
<longname>Mirrodin</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MBS</name>
|
||||
<longname>Mirrodin Besieged</longname>
|
||||
</set>
|
||||
<set import="0">^M
|
||||
<name>MMA</name>^M
|
||||
<longname>Modern Masters</longname>^M
|
||||
</set>^
|
||||
<set import="1">
|
||||
<name>ME2</name>
|
||||
<longname>MTGO Masters Edition II</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ME3</name>
|
||||
<longname>MTGO Masters Edition III</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ME4</name>
|
||||
<longname>MTGO Masters Edition IV</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MED</name>
|
||||
<longname>MTGO Masters Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MGBC</name>
|
||||
<longname>Multiverse Gift Box Cards</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MGDC</name>
|
||||
<longname>Magic Game Day Cards</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MI</name>
|
||||
<longname>Mirrodin</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MLP</name>
|
||||
<longname>Magic: The Gathering Launch Parties</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MM</name>
|
||||
<longname>Mercadian Masques</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MPRP</name>
|
||||
<longname>Magic Player Rewards</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MR</name>
|
||||
<longname>Mirage</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>MT</name>
|
||||
<name>MOR</name>
|
||||
<longname>Morningtide</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>NE</name>
|
||||
<name>NMS</name>
|
||||
<longname>Nemesis</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
|
@ -411,104 +328,80 @@
|
|||
<longname>New Phyrexia</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>OD</name>
|
||||
<name>9ED</name>
|
||||
<longname>Ninth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ODY</name>
|
||||
<longname>Odyssey</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ON</name>
|
||||
<name>ONS</name>
|
||||
<longname>Onslaught</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>P3K</name>
|
||||
<longname>Portal Three Kingdoms</longname>
|
||||
<name>PLC</name>
|
||||
<longname>Planar Chaos</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<set import="0">
|
||||
<name>HOP</name>
|
||||
<longname>Planechase</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>PC2</name>
|
||||
<longname>Planechase 2012 Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PC</name>
|
||||
<longname>Planar Chaos</longname>
|
||||
<name>PLS</name>
|
||||
<longname>Planeshift</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PCH</name>
|
||||
<longname>Planechase</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PD2</name>
|
||||
<longname>Premium Deck Series: Fire and Lightning</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PD3</name>
|
||||
<longname>Premium Deck Series: Graveborn</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PDS</name>
|
||||
<longname>Premium Deck Series: Slivers</longname>
|
||||
<name>POR</name>
|
||||
<longname>Portal</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PO2</name>
|
||||
<longname>Portal Second Age</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PO</name>
|
||||
<longname>Portal</longname>
|
||||
<name>PTK</name>
|
||||
<longname>Portal Three Kingdoms</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>PD2</name>
|
||||
<longname>Premium Deck Series: Fire and Lightning</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>PD3</name>
|
||||
<longname>Premium Deck Series: Graveborn</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>H09</name>
|
||||
<longname>Premium Deck Series: Slivers</longname>
|
||||
</set>
|
||||
<set import="0">
|
||||
<name>PPR</name>
|
||||
<longname>Promo set for Gatherer</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>POT</name>
|
||||
<longname>Portal Demogame</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PR</name>
|
||||
<name>PCY</name>
|
||||
<longname>Prophecy</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PRO</name>
|
||||
<longname>Pro Tour</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PS</name>
|
||||
<longname>Planeshift</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PTC</name>
|
||||
<longname>Prerelease Events</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>PVC</name>
|
||||
<longname>Duel Decks: Phyrexia vs. The Coalition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>RAV</name>
|
||||
<longname>Ravnica: City of Guilds</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>REP</name>
|
||||
<longname>Release Events</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ROE</name>
|
||||
<longname>Rise of the Eldrazi</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>RTR</name>
|
||||
<longname>Return to Ravnica</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>RV</name>
|
||||
<name>3ED</name>
|
||||
<longname>Revised Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SC</name>
|
||||
<longname>Scourge</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SH</name>
|
||||
<longname>Stronghold</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SHM</name>
|
||||
<longname>Shadowmoor</longname>
|
||||
<name>ROE</name>
|
||||
<longname>Rise of the Eldrazi</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SOK</name>
|
||||
|
@ -519,97 +412,97 @@
|
|||
<longname>Scars of Mirrodin</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ST2K</name>
|
||||
<longname>Starter 2000</longname>
|
||||
<name>SCG</name>
|
||||
<longname>Scourge</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ST</name>
|
||||
<name>7ED</name>
|
||||
<longname>Seventh Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SHM</name>
|
||||
<longname>Shadowmoor</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>ALA</name>
|
||||
<longname>Shards of Alara</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>S99</name>
|
||||
<longname>Starter 1999</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SUM</name>
|
||||
<longname>Summer of Magic</longname>
|
||||
<name>S00</name>
|
||||
<longname>Starter 2000</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>SUS</name>
|
||||
<longname>Super Series</longname>
|
||||
<name>STH</name>
|
||||
<longname>Stronghold</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>THGT</name>
|
||||
<longname>Two-Headed Giant Tournament</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>TP</name>
|
||||
<name>TMP</name>
|
||||
<longname>Tempest</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>TR</name>
|
||||
<longname>Torment</longname>
|
||||
<name>10E</name>
|
||||
<longname>Tenth Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>TS</name>
|
||||
<name>DRK</name>
|
||||
<longname>The Dark</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>THS</name>
|
||||
<longname>Theros</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>TSP</name>
|
||||
<longname>Time Spiral</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>TSTS</name>
|
||||
<name>TSB</name>
|
||||
<longname>Time Spiral "Timeshifted"</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UD</name>
|
||||
<longname>Urza's Destiny</longname>
|
||||
<name>TOR</name>
|
||||
<longname>Torment</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UG</name>
|
||||
<set import="0">
|
||||
<name>UGL</name>
|
||||
<longname>Unglued</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UHAA</name>
|
||||
<longname>Unhinged Alternate Foils</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UH</name>
|
||||
<set import="0">
|
||||
<name>UNH</name>
|
||||
<longname>Unhinged</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UL</name>
|
||||
<longname>Urza's Legacy</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UN</name>
|
||||
<name>2ED</name>
|
||||
<longname>Unlimited Edition</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>UQC</name>
|
||||
<longname>Celebration Cards</longname>
|
||||
<name>UDS</name>
|
||||
<longname>Urza's Destiny</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>US</name>
|
||||
<name>ULG</name>
|
||||
<longname>Urza's Legacy</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>USG</name>
|
||||
<longname>Urza's Saga</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>V12</name>
|
||||
<longname>From the Vault: Realms</longname>
|
||||
<set import="0">
|
||||
<name>VAN</name>
|
||||
<longname>Vanguard</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>VI</name>
|
||||
<name>VIS</name>
|
||||
<longname>Visions</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>WL</name>
|
||||
<name>WTH</name>
|
||||
<longname>Weatherlight</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>WMCQ</name>
|
||||
<longname>World Magic Cup Qualifiers</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>WOTC</name>
|
||||
<longname>WotC Online Store</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>WRL</name>
|
||||
<longname>Worlds</longname>
|
||||
</set>
|
||||
<set import="1">
|
||||
<name>WWK</name>
|
||||
<longname>Worldwake</longname>
|
||||
|
|
|
@ -213,12 +213,22 @@ FreeBSD 9.1 until the systems were updated.
|
|||
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\
|
||||
`cd git clone https://github.com/Daenyth/Cockatrice.git`
|
||||
2. Download the sources from github via
|
||||
`git clone https://github.com/Daenyth/Cockatrice.git`
|
||||
|
||||
3. To compile the sources, change into the newly created directory,
|
||||
create a build directory and invoke cmake:\
|
||||
`cd Cockatrice mkdir build cd build cmake .. make`\
|
||||
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`.
|
||||
|
@ -229,7 +239,7 @@ FreeBSD 9.1 until the systems were updated.
|
|||
|
||||
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\
|
||||
paths for decks, pics, cards and tokens are located in
|
||||
`/home/<user>/.local/share/data/Cockatrice/Cockatrice`.
|
||||
|
||||
#### MacOS X
|
||||
|
@ -244,10 +254,10 @@ 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:\
|
||||
- If you want to build the server, use:
|
||||
`cmake -DWITH_SERVER=1 ..`
|
||||
|
||||
- If you want to build the server, but not the client, use:\
|
||||
- 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
|
||||
|
@ -368,13 +378,14 @@ has come out, you must re-run the Oracle and download set information.
|
|||
except for “Instant” along with “Artifact” and “U”, the Deck editor
|
||||
will only show you all Blue Instant and Artifact cards.
|
||||
|
||||
ll Letter & Card Type\
|
||||
U & Blue\
|
||||
W & White\
|
||||
X & Colorless\
|
||||
G & Green\
|
||||
R & Red\
|
||||
B & Black\
|
||||
|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
|
||||
|
@ -736,7 +747,7 @@ players can not add or remove counters to or from your cards.
|
|||
|
||||
#### Pointing at Cards / Arrows
|
||||
|
||||
Pointing at cars is needed for resolving spells, or declaring attackers
|
||||
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
|
||||
|
|
Binary file not shown.
|
@ -83,87 +83,115 @@ If someone runs his own server where you can register a user profile, read his d
|
|||
\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{Client compilation}
|
||||
\subsection{Building the Client}
|
||||
\subsubsection{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.
|
||||
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}
|
||||
We need the Cockatrice sourcecode, it's dependencies and build tools. Everything is freely available:
|
||||
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 MinGW is needed to compile everything, it ships the compiler and other tools for this task.
|
||||
\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 cmake is needed to create Cockatrice's project files for MinGW.
|
||||
\item Qt 4.8 is a dependency for Cockatrice (download the MinGW version!)
|
||||
\item protobuf 2.5 is another dependency for Cockatrice (download the zip file with the sourcecode).
|
||||
\item Nullsoft Scriptable Install System (NSIS) which can be used to create an installer for your own Cockatrice version.
|
||||
\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://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/mingw-get-inst-20120426/}
|
||||
\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://www.cmake.org/files/v2.8/cmake-2.8.10.2-win32-x86.exe}
|
||||
\item \url{http://download.qt-project.org/official_releases/qt/4.8/4.8.4/qt-win-opensource-4.8.4-mingw.exe}
|
||||
\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 Download MinGW (mingw-get-inst), git, cmake, protobuf 2.5 sources zip and Qt 4.8.4 MinGW (see links above).
|
||||
\item Make a standard installation for git, cmake and NSIS.
|
||||
\item Install MinGW:
|
||||
\begin{enumerate}
|
||||
\item Select default values everywhere, except: Also check the C++ Compiler, check the MSYS Basis System and MinGW Developer Toolkit.
|
||||
\item Append \shellcmd{C:\textbackslash MinGW\textbackslash 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.
|
||||
\end{enumerate}
|
||||
\item Install Qt, select default values everywhere, ignore the message regarding win32.h and append \shellcmd{C:\textbackslash Qt\textbackslash 4.8.4\textbackslash bin} to your PATH variable like you did for MinGW.
|
||||
\item Unpack protobuf-2.5.0.zip in \shellcmd{C:\textbackslash MinGW\textbackslash msys\textbackslash 1.0\textbackslash home\textbackslash YOURLOGIN}
|
||||
\begin{enumerate}
|
||||
\item Open MinGW Shell, type the following commands exactly:
|
||||
\item \shellcmd{cd protobuf-2.5.0/protobuf-2.5.0}
|
||||
\item \shellcmd{./configure --prefix=`cd /mingw; pwd -W`} (these apostrophs are backticks and do not forget the dot at the beginning!)
|
||||
\item \shellcmd{make; make install} (this builds and installs protobuf, which needs some time)
|
||||
\item Close MinGW Shell
|
||||
\end{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}
|
||||
Your system is now able to compile Cockatrice. You do not have to repeat the process so far ever again.
|
||||
|
||||
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/Daenyth/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 Checkout Cockatrice:
|
||||
\begin{enumerate}
|
||||
\item Start Git Bash and type the following command exactly:
|
||||
\item \shellcmd{git clone https://github.com/Daenyth/Cockatrice}
|
||||
\item Close Git Bash
|
||||
\end{enumerate}
|
||||
\item Start CMake (cmake-gui) and do the following:
|
||||
\begin{enumerate}
|
||||
\item Where is the source code: Point to the Cockatrice directory
|
||||
\item 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)
|
||||
\item Check Advanced
|
||||
\item Click Configure, choose MinGW, leave the rest default
|
||||
\item An error will occur, set the following:
|
||||
\begin{itemize}
|
||||
\item Set PROTOBUF\_INCLUDE\_DIR to C:/MinGW/include/google/protobuf
|
||||
\item Set PROTOBUF\_LIBRARY to C:/MinGW/lib/libprotobuf.dll.a
|
||||
\item Set PROTOBUF\_LITE\_LIBRARY to C:/MinGW/lib/libprotobuf-lite.dll.a
|
||||
\item Set PROTOBUF\_PROTOC\_EXECUTABLE to C:/MinGW/bin/protoc.exe
|
||||
\item Set PROTOBUF\_PROTOC\_LIBRARY to C:/MinGW/lib/libprotoc.dll.a
|
||||
\end{itemize}
|
||||
\item Click Configure again, then Generate
|
||||
\item Close CMake
|
||||
\end{enumerate}
|
||||
\item Start cmd.exe (the Windows Command Prompt) and type the following commands:
|
||||
\begin{enumerate}
|
||||
\item \shellcmd{cd Cockatrice/build} (this changes to the build directory)
|
||||
\item \shellcmd{make} (this builds everything and might need some time)
|
||||
\end{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}
|
||||
Cockatrice has now been downloaded and built for the first time. You do not have to repeat the process so far ever again.
|
||||
|
||||
\paragraph{Updating your Cockatrice build}
|
||||
If you just compiled Cockatrice for the first time, you skip this step obviously.
|
||||
|
@ -174,17 +202,19 @@ But if you want to update Cockatrice after the source code changed on github, do
|
|||
\item \shellcmd{git pull origin master}
|
||||
\item \shellcmd{Close Git Bash}
|
||||
\end{enumerate}
|
||||
\item Start cmd.exe, change to Cockatrice/build, type make like you did previously.
|
||||
\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 root directory, right click the cockatrice.nsi file and select \shellcmd{Compile NSIS Script}.
|
||||
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 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.
|
||||
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:
|
||||
|
@ -194,7 +224,7 @@ Start the oracle.exe (the installer does this automatically) and let it generate
|
|||
\end{enumerate}
|
||||
Congratulations, you may now use Cockatrice!
|
||||
|
||||
\subsubsection{Linux and BSD}
|
||||
\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,
|
||||
|
@ -204,11 +234,12 @@ Before you install new software, you should update your system. The following in
|
|||
\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} (see note below regarding pthread)
|
||||
\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/Daenyth/Cockatrice.git}
|
||||
\item To compile the sources, change into the newly created directory, create a build directory and invoke cmake:\\
|
||||
|
@ -217,19 +248,13 @@ mkdir build \\
|
|||
cd build \\
|
||||
cmake ..\\
|
||||
make}\\
|
||||
\begin{framed}
|
||||
If you have linking errors with pthread\_* add 'pthread' to the ``target\_link\_libraries'' entry in the \shellcmd{CMakeFiles.txt} in \shellcmd{Cockatrice/common}, e.g. for Xubuntu, then continue the make process.
|
||||
\end{framed}
|
||||
\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/<user>/.local/share/data/Cockatrice/Cockatrice}.
|
||||
\end{enumerate}
|
||||
|
||||
\subsubsection{MacOS X}
|
||||
TODO, please contribute this section! See Linux section, then use the \shellcmd{prepareMacRelease.sh} script from Cockatrice.
|
||||
|
||||
\subsection{Server compilation}
|
||||
\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}
|
||||
|
@ -487,7 +512,7 @@ Sometimes Counters are needed to be placed on cards that the counters on the sid
|
|||
(One of each counter)
|
||||
|
||||
\subsubsection{Pointing at Cards / Arrows}
|
||||
Pointing at cars 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.
|
||||
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} \\
|
||||
|
|
|
@ -9,6 +9,9 @@ OutFile "cockatrice_win32_${TIMESTAMP}_git-${VERSION}.exe"
|
|||
SetCompressor /SOLID lzma
|
||||
InstallDir "$PROGRAMFILES\Cockatrice"
|
||||
|
||||
; set the Qt install dir here (and make sure you use the latest 4.8 version for packaging)
|
||||
!define QTDIR "C:\Qt\4.8.6"
|
||||
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "leftimage.bmp"
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "leftimage.bmp"
|
||||
|
@ -34,35 +37,33 @@ InstallDir "$PROGRAMFILES\Cockatrice"
|
|||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
Section "Application" SecApplication
|
||||
SetShellVarContext all
|
||||
SetOutPath "$INSTDIR"
|
||||
File ..\build\cockatrice\cockatrice.exe
|
||||
File ..\build\oracle\oracle.exe
|
||||
File ..\build\cockatrice\Release\cockatrice.exe
|
||||
File ..\build\oracle\Release\oracle.exe
|
||||
File ..\doc\usermanual\Usermanual.pdf
|
||||
File C:\MinGW\bin\libstdc++-6.dll
|
||||
File C:\MinGW\bin\libgcc_s_dw2-1.dll
|
||||
File C:\MinGW\bin\mingwm10.dll
|
||||
File C:\MinGW\bin\libprotobuf-8.dll
|
||||
File C:\Qt\4.8.4\bin\QtCore4.dll
|
||||
File C:\Qt\4.8.4\bin\QtGui4.dll
|
||||
File C:\Qt\4.8.4\bin\QtNetwork4.dll
|
||||
File C:\Qt\4.8.4\bin\QtSvg4.dll
|
||||
File C:\Qt\4.8.4\bin\QtXml4.dll
|
||||
File C:\Qt\4.8.4\bin\QtMultimedia4.dll
|
||||
File ..\build\protobuf-2.5.0\protobuf-2.5.0\vsprojects\Release\libprotobuf.lib
|
||||
File "${QTDIR}\bin\QtCore4.dll"
|
||||
File "${QTDIR}\bin\QtGui4.dll"
|
||||
File "${QTDIR}\bin\QtNetwork4.dll"
|
||||
File "${QTDIR}\bin\QtSvg4.dll"
|
||||
File "${QTDIR}\bin\QtXml4.dll"
|
||||
File "${QTDIR}\bin\QtMultimedia4.dll"
|
||||
|
||||
SetOutPath "$INSTDIR\zonebg"
|
||||
File /r ..\zonebg\*.*
|
||||
|
||||
SetOutPath "$INSTDIR\plugins"
|
||||
SetOutPath "$INSTDIR\plugins\codecs"
|
||||
File C:\Qt\4.8.4\plugins\codecs\qcncodecs4.dll
|
||||
File C:\Qt\4.8.4\plugins\codecs\qjpcodecs4.dll
|
||||
File C:\Qt\4.8.4\plugins\codecs\qkrcodecs4.dll
|
||||
File C:\Qt\4.8.4\plugins\codecs\qtwcodecs4.dll
|
||||
File "${QTDIR}\plugins\codecs\qcncodecs4.dll"
|
||||
File "${QTDIR}\plugins\codecs\qjpcodecs4.dll"
|
||||
File "${QTDIR}\plugins\codecs\qkrcodecs4.dll"
|
||||
File "${QTDIR}\plugins\codecs\qtwcodecs4.dll"
|
||||
SetOutPath "$INSTDIR\plugins\iconengines"
|
||||
File C:\Qt\4.8.4\plugins\iconengines\qsvgicon4.dll
|
||||
File "${QTDIR}\plugins\iconengines\qsvgicon4.dll"
|
||||
SetOutPath "$INSTDIR\plugins\imageformats"
|
||||
File C:\Qt\4.8.4\plugins\imageformats\qjpeg4.dll
|
||||
File C:\Qt\4.8.4\plugins\imageformats\qsvg4.dll
|
||||
File "${QTDIR}\plugins\imageformats\qjpeg4.dll"
|
||||
File "${QTDIR}\plugins\imageformats\qsvg4.dll"
|
||||
|
||||
SetOutPath "$INSTDIR\sounds"
|
||||
File /r ..\sounds\*.*
|
||||
|
@ -70,9 +71,9 @@ Section "Application" SecApplication
|
|||
SetOutPath "$INSTDIR\translations"
|
||||
File /r ..\build\cockatrice\*.qm
|
||||
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "DisplayName" "Cockatrice"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||
|
@ -96,38 +97,36 @@ Section "Start menu item" SecStartMenu
|
|||
SectionEnd
|
||||
|
||||
Section Uninstall
|
||||
RMDir /r "$INSTDIR\zonebg"
|
||||
RMDir /r "$INSTDIR\plugins"
|
||||
RMDir /r "$INSTDIR\sounds"
|
||||
SetShellVarContext all
|
||||
RMDir /r "$INSTDIR\zonebg"
|
||||
RMDir /r "$INSTDIR\plugins"
|
||||
RMDir /r "$INSTDIR\sounds"
|
||||
RMDir /r "$INSTDIR\translations"
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
Delete "$INSTDIR\cockatrice.exe"
|
||||
Delete "$INSTDIR\oracle.exe"
|
||||
Delete "$INSTDIR\Usermanual.pdf"
|
||||
Delete "$INSTDIR\libstdc++-6.dll"
|
||||
Delete "$INSTDIR\libprotobuf-8.dll"
|
||||
Delete "$INSTDIR\libgcc_s_dw2-1.dll"
|
||||
Delete "$INSTDIR\mingwm10.dll"
|
||||
Delete "$INSTDIR\QtCore4.dll"
|
||||
Delete "$INSTDIR\QtGui4.dll"
|
||||
Delete "$INSTDIR\QtNetwork4.dll"
|
||||
Delete "$INSTDIR\QtSvg4.dll"
|
||||
Delete "$INSTDIR\QtXml4.dll"
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
Delete "$INSTDIR\cockatrice.exe"
|
||||
Delete "$INSTDIR\oracle.exe"
|
||||
Delete "$INSTDIR\Usermanual.pdf"
|
||||
Delete "$INSTDIR\libprotobuf.lib"
|
||||
Delete "$INSTDIR\QtCore4.dll"
|
||||
Delete "$INSTDIR\QtGui4.dll"
|
||||
Delete "$INSTDIR\QtNetwork4.dll"
|
||||
Delete "$INSTDIR\QtSvg4.dll"
|
||||
Delete "$INSTDIR\QtXml4.dll"
|
||||
Delete "$INSTDIR\QtMultimedia4.dll"
|
||||
RMDir "$INSTDIR"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
RMDir /r "$SMPROGRAMS\Cockatrice"
|
||||
|
||||
DeleteRegKey HKCU "Software\Cockatrice"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice"
|
||||
|
||||
DeleteRegKey HKCU "Software\Cockatrice"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cockatrice"
|
||||
SectionEnd
|
||||
|
||||
LangString DESC_SecApplication ${LANG_ENGLISH} "Cockatrice program files"
|
||||
LangString DESC_SecUpdateConfig ${LANG_ENGLISH} "Update the paths in the application settings according to the installation paths."
|
||||
LangString DESC_SecStartMenu ${LANG_ENGLISH} "Create start menu items for Cockatrice and Oracle."
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecApplication} $(DESC_SecApplication)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecUpdateConfig} $(DESC_SecUpdateConfig)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecApplication} $(DESC_SecApplication)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecUpdateConfig} $(DESC_SecUpdateConfig)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu)
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
||||
|
|
|
@ -1,32 +1,77 @@
|
|||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
# CMakeLists for oracle directory
|
||||
#
|
||||
# provides the oracle binary
|
||||
|
||||
PROJECT(oracle)
|
||||
|
||||
# paths
|
||||
set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files")
|
||||
|
||||
SET(oracle_SOURCES src/main.cpp src/oracleimporter.cpp src/window_main.cpp ../cockatrice/src/carddatabase.cpp ../cockatrice/src/settingscache.cpp)
|
||||
SET(oracle_HEADERS src/oracleimporter.h src/window_main.h ../cockatrice/src/carddatabase.h ../cockatrice/src/settingscache.h)
|
||||
SET(oracle_SOURCES
|
||||
src/main.cpp
|
||||
src/oraclewizard.cpp
|
||||
src/oracleimporter.cpp
|
||||
../cockatrice/src/carddatabase.cpp
|
||||
../cockatrice/src/settingscache.cpp
|
||||
../cockatrice/src/qt-json/json.cpp
|
||||
)
|
||||
|
||||
SET(QT_USE_QTNETWORK TRUE)
|
||||
SET(QT_USE_QTXML TRUE)
|
||||
SET(QT_USE_QTSVG TRUE)
|
||||
FIND_PACKAGE(Qt4 REQUIRED)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
|
||||
|
||||
QT4_WRAP_CPP(oracle_HEADERS_MOC ${oracle_HEADERS})
|
||||
# Include directories
|
||||
INCLUDE(${QT_USE_FILE})
|
||||
INCLUDE_DIRECTORIES(../cockatrice/src)
|
||||
|
||||
ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_HEADERS_MOC})
|
||||
TARGET_LINK_LIBRARIES(oracle ${QT_LIBRARIES})
|
||||
# Build oracle binary and link it
|
||||
ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_MOC_SRCS})
|
||||
TARGET_LINK_LIBRARIES(oracle ${QT_QTMAIN_LIBRARY} ${QT_LIBRARIES})
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(oracle PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
|
||||
endif(MSVC)
|
||||
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
INSTALL(TARGETS oracle BUNDLE DESTINATION ./)
|
||||
else()
|
||||
# Assume linux
|
||||
INSTALL(TARGETS oracle RUNTIME DESTINATION bin/)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
INSTALL(TARGETS oracle RUNTIME DESTINATION ./)
|
||||
endif()
|
||||
|
||||
IF (NOT APPLE)
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/oracle DESTINATION bin)
|
||||
ELSE (APPLE)
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/oracle.app DESTINATION bin)
|
||||
ENDIF (NOT APPLE)
|
||||
IF (NOT WIN32 AND NOT APPLE)
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION ${DESKTOPDIR})
|
||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION ${DESKTOPDIR})
|
||||
ENDIF (NOT WIN32 AND NOT APPLE)
|
||||
|
||||
if(APPLE)
|
||||
set(plugin_dest_dir ./oracle.app/Contents/Plugins)
|
||||
set(qtconf_dest_dir ./oracle.app/Contents/Resources)
|
||||
|
||||
# note: no codecs in qt5
|
||||
# note: phonon_backend => mediaservice
|
||||
# note: needs platform on osx
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(codecs|iconengines|imageformats|mediaservice|phonon_backend|platforms)/.*_debug\\.dylib")
|
||||
else()
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(codecs|iconengines|imageformats|mediaservice|phonon_backend|platforms)/[^_]*\\.dylib")
|
||||
endif()
|
||||
|
||||
install(CODE "
|
||||
file(WRITE \"${qtconf_dest_dir}/qt.conf\" \"[Paths]
|
||||
Plugins = Plugins\")
|
||||
" COMPONENT Runtime)
|
||||
|
||||
install(CODE "
|
||||
file(GLOB_RECURSE QTPLUGINS
|
||||
\"${plugin_dest_dir}/*.dylib\")
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/oracle.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
|
||||
" COMPONENT Runtime)
|
||||
endif()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <QApplication>
|
||||
#include <QTextCodec>
|
||||
#include "window_main.h"
|
||||
#include "oraclewizard.h"
|
||||
#include "settingscache.h"
|
||||
|
||||
SettingsCache *settingsCache;
|
||||
|
@ -12,13 +12,14 @@ int main(int argc, char *argv[])
|
|||
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
QCoreApplication::setOrganizationName("Cockatrice");
|
||||
QCoreApplication::setOrganizationDomain("cockatrice.de");
|
||||
QCoreApplication::setOrganizationDomain("cockatrice");
|
||||
// this can't be changed, as it influences the default savepath for cards.xml
|
||||
QCoreApplication::setApplicationName("Cockatrice");
|
||||
|
||||
settingsCache = new SettingsCache;
|
||||
|
||||
WindowMain wnd;
|
||||
wnd.show();
|
||||
|
||||
|
||||
OracleWizard wizard;
|
||||
wizard.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
|
@ -1,325 +1,242 @@
|
|||
#include "oracleimporter.h"
|
||||
#include <QtGui>
|
||||
#include <QtNetwork>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDomDocument>
|
||||
#include <QDebug>
|
||||
|
||||
#include "qt-json/json.h"
|
||||
|
||||
OracleImporter::OracleImporter(const QString &_dataDir, QObject *parent)
|
||||
: CardDatabase(parent), dataDir(_dataDir), setIndex(-1)
|
||||
: CardDatabase(parent), dataDir(_dataDir)
|
||||
{
|
||||
buffer = new QBuffer(this);
|
||||
http = new QHttp(this);
|
||||
connect(http, SIGNAL(requestFinished(int, bool)), this, SLOT(httpRequestFinished(int, bool)));
|
||||
connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), this, SLOT(readResponseHeader(const QHttpResponseHeader &)));
|
||||
connect(http, SIGNAL(dataReadProgress(int, int)), this, SIGNAL(dataReadProgress(int, int)));
|
||||
}
|
||||
|
||||
bool OracleImporter::readSetsFromFile(const QString &fileName)
|
||||
{
|
||||
QFile setsFile(fileName);
|
||||
if (!setsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamReader xml(&setsFile);
|
||||
return readSetsFromXml(xml);
|
||||
}
|
||||
|
||||
bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
|
||||
{
|
||||
QXmlStreamReader xml(data);
|
||||
return readSetsFromXml(xml);
|
||||
}
|
||||
QList<SetToDownload> newSetList;
|
||||
|
||||
bool ok;
|
||||
setsMap = QtJson::Json::parse(QString(data), ok).toMap();
|
||||
if (!ok) {
|
||||
qDebug() << "error: QtJson::Json::parse()";
|
||||
return 0;
|
||||
}
|
||||
|
||||
QListIterator<QVariant> it(setsMap.values());
|
||||
QVariantMap map;
|
||||
|
||||
bool OracleImporter::readSetsFromXml(QXmlStreamReader &xml)
|
||||
{
|
||||
QList<SetToDownload> newSetList;
|
||||
|
||||
QString edition;
|
||||
QString editionLong;
|
||||
QString editionURL;
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement)
|
||||
break;
|
||||
if (xml.name() == "set") {
|
||||
bool import = xml.attributes().value("import").toString().toInt();
|
||||
while (!xml.atEnd()) {
|
||||
if (xml.readNext() == QXmlStreamReader::EndElement)
|
||||
break;
|
||||
if (xml.name() == "name")
|
||||
edition = xml.readElementText();
|
||||
else if (xml.name() == "longname")
|
||||
editionLong = xml.readElementText();
|
||||
else if (xml.name() == "url")
|
||||
editionURL = xml.readElementText();
|
||||
}
|
||||
newSetList.append(SetToDownload(edition, editionLong, editionURL, import));
|
||||
edition = editionLong = editionURL = QString();
|
||||
} else if (xml.name() == "picture_url")
|
||||
pictureUrl = xml.readElementText();
|
||||
else if (xml.name() == "picture_url_hq")
|
||||
pictureUrlHq = xml.readElementText();
|
||||
else if (xml.name() == "picture_url_st")
|
||||
pictureUrlSt = xml.readElementText();
|
||||
else if (xml.name() == "set_url")
|
||||
setUrl = xml.readElementText();
|
||||
}
|
||||
if (newSetList.isEmpty())
|
||||
return false;
|
||||
allSets = newSetList;
|
||||
return true;
|
||||
QString edition;
|
||||
QString editionLong;
|
||||
QVariant editionCards;
|
||||
bool import;
|
||||
|
||||
while (it.hasNext()) {
|
||||
map = it.next().toMap();
|
||||
edition = map.value("code").toString();
|
||||
editionLong = map.value("name").toString();
|
||||
editionCards = map.value("cards");
|
||||
|
||||
// core and expansion sets are marked to be imported by default
|
||||
import = (0 == QString::compare(map.value("type").toString(), QString("core"), Qt::CaseInsensitive) ||
|
||||
0 == QString::compare(map.value("type").toString(), QString("expansion"), Qt::CaseInsensitive));
|
||||
|
||||
newSetList.append(SetToDownload(edition, editionLong, editionCards, import));
|
||||
}
|
||||
|
||||
qSort(newSetList);
|
||||
|
||||
if (newSetList.isEmpty())
|
||||
return false;
|
||||
allSets = newSetList;
|
||||
return true;
|
||||
}
|
||||
|
||||
CardInfo *OracleImporter::addCard(const QString &setName,
|
||||
QString cardName,
|
||||
bool isToken,
|
||||
int cardId,
|
||||
const QString &cardCost,
|
||||
const QString &cardType,
|
||||
const QString &cardPT,
|
||||
int cardLoyalty,
|
||||
const QStringList &cardText)
|
||||
QString cardName,
|
||||
bool isToken,
|
||||
int cardId,
|
||||
QString &cardCost,
|
||||
const QString &cardType,
|
||||
const QString &cardPT,
|
||||
int cardLoyalty,
|
||||
const QStringList &cardText)
|
||||
{
|
||||
QString fullCardText = cardText.join("\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("’", "'");
|
||||
QString fullCardText = cardText.join("\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("’", "'");
|
||||
|
||||
CardInfo *card;
|
||||
if (cardHash.contains(cardName)) {
|
||||
card = cardHash.value(cardName);
|
||||
if (splitCard && !card->getText().contains(fullCardText))
|
||||
card->setText(card->getText() + "\n---\n" + fullCardText);
|
||||
} else {
|
||||
bool mArtifact = false;
|
||||
if (cardType.endsWith("Artifact"))
|
||||
for (int i = 0; i < cardText.size(); ++i)
|
||||
if (cardText[i].contains("{T}") && cardText[i].contains("to your mana pool"))
|
||||
mArtifact = true;
|
||||
|
||||
QStringList colors;
|
||||
QStringList allColors = QStringList() << "W" << "U" << "B" << "R" << "G";
|
||||
for (int i = 0; i < allColors.size(); i++)
|
||||
if (cardCost.contains(allColors[i]))
|
||||
colors << allColors[i];
|
||||
|
||||
if (cardText.contains(cardName + " is white."))
|
||||
colors << "W";
|
||||
if (cardText.contains(cardName + " is blue."))
|
||||
colors << "U";
|
||||
if (cardText.contains(cardName + " is black."))
|
||||
colors << "B";
|
||||
if (cardText.contains(cardName + " is red."))
|
||||
colors << "R";
|
||||
if (cardText.contains(cardName + " is green."))
|
||||
colors << "G";
|
||||
|
||||
bool cipt = (cardText.contains(cardName + " enters the battlefield tapped."));
|
||||
|
||||
card = new CardInfo(this, cardName, isToken, cardCost, cardType, cardPT, fullCardText, colors, cardLoyalty, cipt);
|
||||
int tableRow = 1;
|
||||
QString mainCardType = card->getMainCardType();
|
||||
if ((mainCardType == "Land") || mArtifact)
|
||||
tableRow = 0;
|
||||
else if ((mainCardType == "Sorcery") || (mainCardType == "Instant"))
|
||||
tableRow = 3;
|
||||
else if (mainCardType == "Creature")
|
||||
tableRow = 2;
|
||||
card->setTableRow(tableRow);
|
||||
|
||||
cardHash.insert(cardName, card);
|
||||
}
|
||||
card->setPicURL(setName, getPictureUrl(pictureUrl, cardId, cardName, setName));
|
||||
card->setPicURLHq(setName, getPictureUrl(pictureUrlHq, cardId, cardName, setName));
|
||||
card->setPicURLSt(setName, getPictureUrl(pictureUrlSt, cardId, cardName, setName));
|
||||
return card;
|
||||
// Remove {} around mana costs
|
||||
cardCost.remove(QChar('{'));
|
||||
cardCost.remove(QChar('}'));
|
||||
|
||||
CardInfo *card;
|
||||
if (cardHash.contains(cardName)) {
|
||||
card = cardHash.value(cardName);
|
||||
if (splitCard && !card->getText().contains(fullCardText))
|
||||
card->setText(card->getText() + "\n---\n" + fullCardText);
|
||||
} else {
|
||||
bool mArtifact = false;
|
||||
if (cardType.endsWith("Artifact"))
|
||||
for (int i = 0; i < cardText.size(); ++i)
|
||||
if (cardText[i].contains("{T}") && cardText[i].contains("to your mana pool"))
|
||||
mArtifact = true;
|
||||
|
||||
QStringList colors;
|
||||
QStringList allColors = QStringList() << "W" << "U" << "B" << "R" << "G";
|
||||
for (int i = 0; i < allColors.size(); i++)
|
||||
if (cardCost.contains(allColors[i]))
|
||||
colors << allColors[i];
|
||||
|
||||
if (cardText.contains(cardName + " is white."))
|
||||
colors << "W";
|
||||
if (cardText.contains(cardName + " is blue."))
|
||||
colors << "U";
|
||||
if (cardText.contains(cardName + " is black."))
|
||||
colors << "B";
|
||||
if (cardText.contains(cardName + " is red."))
|
||||
colors << "R";
|
||||
if (cardText.contains(cardName + " is green."))
|
||||
colors << "G";
|
||||
|
||||
bool cipt = (cardText.contains(cardName + " enters the battlefield tapped."));
|
||||
|
||||
card = new CardInfo(this, cardName, isToken, cardCost, cardType, cardPT, fullCardText, colors, cardLoyalty, cipt);
|
||||
int tableRow = 1;
|
||||
QString mainCardType = card->getMainCardType();
|
||||
if ((mainCardType == "Land") || mArtifact)
|
||||
tableRow = 0;
|
||||
else if ((mainCardType == "Sorcery") || (mainCardType == "Instant"))
|
||||
tableRow = 3;
|
||||
else if (mainCardType == "Creature")
|
||||
tableRow = 2;
|
||||
card->setTableRow(tableRow);
|
||||
|
||||
cardHash.insert(cardName, card);
|
||||
}
|
||||
card->setMuId(setName, cardId);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
int OracleImporter::importTextSpoiler(CardSet *set, const QByteArray &data)
|
||||
int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data)
|
||||
{
|
||||
int cards = 0;
|
||||
QString bufferContents(data);
|
||||
|
||||
// Workaround for ampersand bug in text spoilers
|
||||
int index = -1;
|
||||
while ((index = bufferContents.indexOf('&', index + 1)) != -1) {
|
||||
int semicolonIndex = bufferContents.indexOf(';', index);
|
||||
if (semicolonIndex > 5) {
|
||||
bufferContents.insert(index + 1, "amp;");
|
||||
index += 4;
|
||||
}
|
||||
}
|
||||
|
||||
QDomDocument doc;
|
||||
QString errorMsg;
|
||||
int errorLine, errorColumn;
|
||||
if (!doc.setContent(bufferContents, &errorMsg, &errorLine, &errorColumn))
|
||||
qDebug() << "error:" << errorMsg << "line:" << errorLine << "column:" << errorColumn;
|
||||
int cards = 0;
|
||||
|
||||
QListIterator<QVariant> it(data.toList());
|
||||
QVariantMap map;
|
||||
QString cardName;
|
||||
QString cardCost;
|
||||
QString cardType;
|
||||
QString cardPT;
|
||||
QString cardText;
|
||||
int cardId;
|
||||
int cardLoyalty;
|
||||
QMap<int, QVariantMap> splitCards;
|
||||
|
||||
QDomNodeList divs = doc.elementsByTagName("div");
|
||||
for (int i = 0; i < divs.size(); ++i) {
|
||||
QDomElement div = divs.at(i).toElement();
|
||||
QDomNode divClass = div.attributes().namedItem("class");
|
||||
if (divClass.nodeValue() == "textspoiler") {
|
||||
QString cardName, cardCost, cardType, cardPT, cardText;
|
||||
int cardId = 0;
|
||||
int cardLoyalty = 0;
|
||||
|
||||
QDomNodeList trs = div.elementsByTagName("tr");
|
||||
for (int j = 0; j < trs.size(); ++j) {
|
||||
QDomElement tr = trs.at(j).toElement();
|
||||
QDomNodeList tds = tr.elementsByTagName("td");
|
||||
if (tds.size() != 2) {
|
||||
QStringList cardTextSplit = cardText.split("\n");
|
||||
for (int i = 0; i < cardTextSplit.size(); ++i)
|
||||
cardTextSplit[i] = cardTextSplit[i].trimmed();
|
||||
|
||||
CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardTextSplit);
|
||||
if (!set->contains(card)) {
|
||||
card->addToSet(set);
|
||||
cards++;
|
||||
}
|
||||
cardName = cardCost = cardType = cardPT = cardText = QString();
|
||||
} else {
|
||||
QString v1 = tds.at(0).toElement().text().simplified();
|
||||
QString v2 = tds.at(1).toElement().text().replace(trUtf8("—"), "-");
|
||||
|
||||
if (v1 == "Name") {
|
||||
QDomElement a = tds.at(1).toElement().elementsByTagName("a").at(0).toElement();
|
||||
QString href = a.attributes().namedItem("href").nodeValue();
|
||||
cardId = href.mid(href.indexOf("multiverseid=") + 13).toInt();
|
||||
cardName = v2.simplified();
|
||||
} else if (v1 == "Cost:")
|
||||
cardCost = v2.simplified();
|
||||
else if (v1 == "Type:")
|
||||
cardType = v2.simplified();
|
||||
else if (v1 == "Pow/Tgh:")
|
||||
cardPT = v2.simplified().remove('(').remove(')');
|
||||
else if (v1 == "Rules Text:")
|
||||
cardText = v2.trimmed();
|
||||
else if (v1 == "Loyalty:")
|
||||
cardLoyalty = v2.trimmed().remove('(').remove(')').toInt();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cards;
|
||||
while (it.hasNext()) {
|
||||
map = it.next().toMap();
|
||||
if(0 == QString::compare(map.value("layout").toString(), QString("split"), Qt::CaseInsensitive))
|
||||
{
|
||||
// Split card 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("");
|
||||
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("");
|
||||
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("");
|
||||
} else {
|
||||
// first card od a pair; enqueue for later merging
|
||||
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("");
|
||||
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;
|
||||
}
|
||||
|
||||
CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cardType, cardPT, cardLoyalty, cardText.split("\n"));
|
||||
|
||||
if (!set->contains(card)) {
|
||||
card->addToSet(set);
|
||||
cards++;
|
||||
}
|
||||
}
|
||||
|
||||
return cards;
|
||||
}
|
||||
|
||||
QString OracleImporter::getPictureUrl(QString url, int cardId, QString name, const QString &setName) const
|
||||
int OracleImporter::startImport()
|
||||
{
|
||||
if ((name == "Island") || (name == "Swamp") || (name == "Mountain") || (name == "Plains") || (name == "Forest"))
|
||||
name.append("1");
|
||||
return url.replace("!cardid!", QString::number(cardId)).replace("!set!", setName).replace("!name!", name
|
||||
.replace("ö", "o")
|
||||
// .remove('\'')
|
||||
.remove(" // ")
|
||||
// .remove(',')
|
||||
// .remove(':')
|
||||
// .remove('.')
|
||||
.remove(QRegExp("\\(.*\\)"))
|
||||
.simplified()
|
||||
// .replace(' ', '_')
|
||||
// .replace('-', '_')
|
||||
);
|
||||
}
|
||||
|
||||
int OracleImporter::startDownload()
|
||||
{
|
||||
clear();
|
||||
|
||||
setsToDownload.clear();
|
||||
for (int i = 0; i < allSets.size(); ++i)
|
||||
if (allSets[i].getImport())
|
||||
setsToDownload.append(allSets[i]);
|
||||
|
||||
if (setsToDownload.isEmpty())
|
||||
return 0;
|
||||
setIndex = 0;
|
||||
emit setIndexChanged(0, 0, setsToDownload[0].getLongName());
|
||||
|
||||
downloadNextFile();
|
||||
return setsToDownload.size();
|
||||
}
|
||||
|
||||
void OracleImporter::downloadNextFile()
|
||||
{
|
||||
QString urlString = setsToDownload[setIndex].getUrl();
|
||||
if (urlString.isEmpty())
|
||||
urlString = setUrl;
|
||||
urlString = urlString.replace("!longname!", setsToDownload[setIndex].getLongName());
|
||||
|
||||
if (urlString.startsWith("http://")) {
|
||||
QUrl url(urlString);
|
||||
http->setHost(url.host(), QHttp::ConnectionModeHttp, url.port() == -1 ? 0 : url.port());
|
||||
QString path = QUrl::toPercentEncoding(urlString.mid(url.host().size() + 7).replace(' ', '+'), "?!$&'()*+,;=:@/");
|
||||
|
||||
buffer->close();
|
||||
buffer->setData(QByteArray());
|
||||
buffer->open(QIODevice::ReadWrite | QIODevice::Text);
|
||||
reqId = http->get(path, buffer);
|
||||
} else {
|
||||
QFile file(dataDir + "/" + urlString);
|
||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||
|
||||
buffer->close();
|
||||
buffer->setData(file.readAll());
|
||||
buffer->open(QIODevice::ReadWrite | QIODevice::Text);
|
||||
reqId = 0;
|
||||
httpRequestFinished(reqId, false);
|
||||
}
|
||||
}
|
||||
|
||||
void OracleImporter::httpRequestFinished(int requestId, bool error)
|
||||
{
|
||||
if (error) {
|
||||
QMessageBox::information(0, tr("HTTP"), tr("Error."));
|
||||
return;
|
||||
}
|
||||
if (requestId != reqId)
|
||||
return;
|
||||
|
||||
CardSet *set = new CardSet(setsToDownload[setIndex].getShortName(), setsToDownload[setIndex].getLongName());
|
||||
if (!setHash.contains(set->getShortName()))
|
||||
setHash.insert(set->getShortName(), set);
|
||||
|
||||
buffer->seek(0);
|
||||
buffer->close();
|
||||
int cards = importTextSpoiler(set, buffer->data());
|
||||
if (cards > 0)
|
||||
++setIndex;
|
||||
|
||||
if (setIndex == setsToDownload.size()) {
|
||||
emit setIndexChanged(cards, setIndex, QString());
|
||||
setIndex = -1;
|
||||
} else {
|
||||
downloadNextFile();
|
||||
emit setIndexChanged(cards, setIndex, setsToDownload[setIndex].getLongName());
|
||||
}
|
||||
}
|
||||
|
||||
void OracleImporter::readResponseHeader(const QHttpResponseHeader &responseHeader)
|
||||
{
|
||||
switch (responseHeader.statusCode()) {
|
||||
case 200:
|
||||
case 301:
|
||||
case 302:
|
||||
case 303:
|
||||
case 307:
|
||||
break;
|
||||
default:
|
||||
QMessageBox::information(0, tr("HTTP"), tr("Download failed: %1.").arg(responseHeader.reasonPhrase()));
|
||||
http->abort();
|
||||
deleteLater();
|
||||
}
|
||||
clear();
|
||||
|
||||
int setCards = 0, setIndex= 0;
|
||||
QListIterator<SetToDownload> it(allSets);
|
||||
const SetToDownload * curSet;
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
curSet = & it.next();
|
||||
if(!curSet->getImport())
|
||||
continue;
|
||||
|
||||
CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName());
|
||||
if (!setHash.contains(set->getShortName()))
|
||||
setHash.insert(set->getShortName(), set);
|
||||
|
||||
int setCards = importTextSpoiler(set, curSet->getCards());
|
||||
|
||||
++setIndex;
|
||||
|
||||
emit setIndexChanged(setCards, setIndex, curSet->getLongName());
|
||||
}
|
||||
|
||||
emit setIndexChanged(setCards, setIndex, QString());
|
||||
|
||||
// total number of sets
|
||||
return setIndex;
|
||||
}
|
||||
|
|
|
@ -1,53 +1,42 @@
|
|||
#ifndef ORACLEIMPORTER_H
|
||||
#define ORACLEIMPORTER_H
|
||||
|
||||
#include <carddatabase.h>
|
||||
#include <QHttp>
|
||||
#include <QMap>
|
||||
|
||||
class QBuffer;
|
||||
class QXmlStreamReader;
|
||||
#include <carddatabase.h>
|
||||
|
||||
class SetToDownload {
|
||||
private:
|
||||
QString shortName, longName, url;
|
||||
QString shortName, longName;
|
||||
bool import;
|
||||
QVariant cards;
|
||||
public:
|
||||
const QString &getShortName() const { return shortName; }
|
||||
const QString &getLongName() const { return longName; }
|
||||
const QString &getUrl() const { return url; }
|
||||
const QVariant &getCards() const { return cards; }
|
||||
bool getImport() const { return import; }
|
||||
void setImport(bool _import) { import = _import; }
|
||||
SetToDownload(const QString &_shortName, const QString &_longName, const QString &_url, bool _import)
|
||||
: shortName(_shortName), longName(_longName), url(_url), import(_import) { }
|
||||
SetToDownload(const QString &_shortName, const QString &_longName, const QVariant &_cards, bool _import)
|
||||
: shortName(_shortName), longName(_longName), cards(_cards), import(_import) { }
|
||||
bool operator<(const SetToDownload &set) const { return longName.compare(set.longName, Qt::CaseInsensitive) < 0; }
|
||||
};
|
||||
|
||||
class OracleImporter : public CardDatabase {
|
||||
Q_OBJECT
|
||||
private:
|
||||
QList<SetToDownload> allSets, setsToDownload;
|
||||
QString pictureUrl, pictureUrlHq, pictureUrlSt, setUrl;
|
||||
QList<SetToDownload> allSets;
|
||||
QVariantMap setsMap;
|
||||
QString dataDir;
|
||||
int setIndex;
|
||||
int reqId;
|
||||
QBuffer *buffer;
|
||||
QHttp *http;
|
||||
QString getPictureUrl(QString url, int cardId, QString name, const QString &setName) const;
|
||||
|
||||
void downloadNextFile();
|
||||
bool readSetsFromXml(QXmlStreamReader &xml);
|
||||
CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, const QString &cardCost, const QString &cardType, const QString &cardPT, int cardLoyalty, const QStringList &cardText);
|
||||
private slots:
|
||||
void httpRequestFinished(int requestId, bool error);
|
||||
void readResponseHeader(const QHttpResponseHeader &responseHeader);
|
||||
CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, const QString &cardType, const QString &cardPT, int cardLoyalty, const QStringList &cardText);
|
||||
signals:
|
||||
void setIndexChanged(int cardsImported, int setIndex, const QString &nextSetName);
|
||||
void setIndexChanged(int cardsImported, int setIndex, const QString &setName);
|
||||
void dataReadProgress(int bytesRead, int totalBytes);
|
||||
public:
|
||||
OracleImporter(const QString &_dataDir, QObject *parent = 0);
|
||||
bool readSetsFromByteArray(const QByteArray &data);
|
||||
bool readSetsFromFile(const QString &fileName);
|
||||
int startDownload();
|
||||
int importTextSpoiler(CardSet *set, const QByteArray &data);
|
||||
int startImport();
|
||||
int importTextSpoiler(CardSet *set, const QVariant &data);
|
||||
QList<SetToDownload> &getSets() { return allSets; }
|
||||
const QString &getDataDir() const { return dataDir; }
|
||||
};
|
||||
|
|
398
oracle/src/oraclewizard.cpp
Normal file
398
oracle/src/oraclewizard.cpp
Normal file
|
@ -0,0 +1,398 @@
|
|||
#include <QtGui>
|
||||
#include <QGridLayout>
|
||||
#include <QDesktopServices>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "oraclewizard.h"
|
||||
#include "oracleimporter.h"
|
||||
|
||||
#define ALLSETS_URL "http://mtgjson.com/json/AllSets.json"
|
||||
|
||||
OracleWizard::OracleWizard(QWidget *parent)
|
||||
: QWizard(parent)
|
||||
{
|
||||
settings = new QSettings(this);
|
||||
importer = new OracleImporter(QDesktopServices::storageLocation(QDesktopServices::DataLocation), this);
|
||||
|
||||
addPage(new IntroPage);
|
||||
addPage(new LoadSetsPage);
|
||||
addPage(new ChooseSetsPage);
|
||||
addPage(new SaveSetsPage);
|
||||
|
||||
setWindowTitle(tr("Oracle Importer"));
|
||||
QWizard::setButtonText(QWizard::FinishButton, tr("Save"));
|
||||
}
|
||||
|
||||
void OracleWizard::accept()
|
||||
{
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void OracleWizard::enableButtons()
|
||||
{
|
||||
button(QWizard::NextButton)->setDisabled(false);
|
||||
button(QWizard::BackButton)->setDisabled(false);
|
||||
}
|
||||
|
||||
void OracleWizard::disableButtons()
|
||||
{
|
||||
button(QWizard::NextButton)->setDisabled(true);
|
||||
button(QWizard::BackButton)->setDisabled(true);
|
||||
}
|
||||
|
||||
IntroPage::IntroPage(QWidget *parent)
|
||||
: OracleWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Introduction"));
|
||||
|
||||
label = new QLabel(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."),
|
||||
this);
|
||||
label->setWordWrap(true);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->addWidget(label);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
LoadSetsPage::LoadSetsPage(QWidget *parent)
|
||||
: OracleWizardPage(parent), nam(0)
|
||||
{
|
||||
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 "
|
||||
"use an existing file from your computer."));
|
||||
|
||||
urlRadioButton = new QRadioButton(tr("Download url:"), this);
|
||||
fileRadioButton = new QRadioButton(tr("Local file:"), this);
|
||||
|
||||
urlLineEdit = new QLineEdit(this);
|
||||
fileLineEdit = new QLineEdit(this);
|
||||
|
||||
progressLabel = new QLabel(this);
|
||||
progressBar = new QProgressBar(this);
|
||||
|
||||
urlRadioButton->setChecked(true);
|
||||
|
||||
fileButton = new QPushButton(tr("Choose file..."), this);
|
||||
connect(fileButton, SIGNAL(clicked()), this, SLOT(actLoadSetsFile()));
|
||||
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->addWidget(urlRadioButton, 0, 0);
|
||||
layout->addWidget(urlLineEdit, 0, 1);
|
||||
layout->addWidget(fileRadioButton, 1, 0);
|
||||
layout->addWidget(fileLineEdit, 1, 1);
|
||||
layout->addWidget(fileButton, 2, 1, Qt::AlignRight);
|
||||
layout->addWidget(progressLabel, 3, 0);
|
||||
layout->addWidget(progressBar, 3, 1);
|
||||
|
||||
connect(&watcher, SIGNAL(finished()), this, SLOT(importFinished()));
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void LoadSetsPage::initializePage()
|
||||
{
|
||||
urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString());
|
||||
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
}
|
||||
|
||||
void LoadSetsPage::actLoadSetsFile()
|
||||
{
|
||||
QFileDialog dialog(this, tr("Load sets file"));
|
||||
dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
dialog.setNameFilter("Sets JSON file (*.json)");
|
||||
|
||||
if(!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text()))
|
||||
dialog.selectFile(fileLineEdit->text());
|
||||
|
||||
if (!dialog.exec())
|
||||
return;
|
||||
|
||||
fileLineEdit->setText(dialog.selectedFiles().at(0));
|
||||
}
|
||||
|
||||
bool LoadSetsPage::validatePage()
|
||||
{
|
||||
// once the import is finished, we call next(); skip validation
|
||||
if(wizard()->importer->getSets().count() > 0)
|
||||
return true;
|
||||
|
||||
// else, try to import sets
|
||||
if(urlRadioButton->isChecked())
|
||||
{
|
||||
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(actDownloadFinishedSetsFile()));
|
||||
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSetsFile(qint64, qint64)));
|
||||
|
||||
} else if(fileRadioButton->isChecked()) {
|
||||
QFile setsFile(fileLineEdit->text());
|
||||
if(!setsFile.exists())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Please choose a file."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text()));
|
||||
return false;
|
||||
}
|
||||
|
||||
wizard()->disableButtons();
|
||||
setEnabled(false);
|
||||
|
||||
readSetsFromByteArray(setsFile.readAll());
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total)
|
||||
{
|
||||
if(total > 0 && progressBar->maximum()==0)
|
||||
{
|
||||
progressBar->setMaximum(total);
|
||||
progressBar->setValue(received);
|
||||
}
|
||||
progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / 1048576));
|
||||
}
|
||||
|
||||
void LoadSetsPage::actDownloadFinishedSetsFile()
|
||||
{
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
// check for a reply
|
||||
QNetworkReply *reply = static_cast<QNetworkReply *>(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 allsets.json url, but only if the user customized it and download was successfull
|
||||
if(urlLineEdit->text() != QString(ALLSETS_URL))
|
||||
wizard()->settings->setValue("allsetsurl", urlLineEdit->text());
|
||||
else
|
||||
wizard()->settings->remove("allsetsurl");
|
||||
|
||||
readSetsFromByteArray(reply->readAll());
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void LoadSetsPage::readSetsFromByteArray(QByteArray data)
|
||||
{
|
||||
// show an infinite progressbar
|
||||
progressBar->setMaximum(0);
|
||||
progressBar->setMinimum(0);
|
||||
progressBar->setValue(0);
|
||||
progressLabel->setText(tr("Parsing file"));
|
||||
progressLabel->show();
|
||||
progressBar->show();
|
||||
|
||||
// Start the computation.
|
||||
future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, data);
|
||||
watcher.setFuture(future);
|
||||
}
|
||||
|
||||
void LoadSetsPage::importFinished()
|
||||
{
|
||||
wizard()->enableButtons();
|
||||
setEnabled(true);
|
||||
progressLabel->hide();
|
||||
progressBar->hide();
|
||||
|
||||
if(watcher.future().result())
|
||||
{
|
||||
wizard()->next();
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error"), tr("The file was retrieved successfully, but it does not contain any sets data."));
|
||||
}
|
||||
}
|
||||
|
||||
ChooseSetsPage::ChooseSetsPage(QWidget *parent)
|
||||
: OracleWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Sets selection"));
|
||||
setSubTitle(tr("The following sets has been found in the source file. "
|
||||
"Please mark the sets that will be imported."));
|
||||
|
||||
checkBoxLayout = new QVBoxLayout;
|
||||
|
||||
QWidget *checkboxFrame = new QWidget(this);
|
||||
checkboxFrame->setLayout(checkBoxLayout);
|
||||
|
||||
QScrollArea *checkboxArea = new QScrollArea(this);
|
||||
checkboxArea->setWidget(checkboxFrame);
|
||||
checkboxArea->setWidgetResizable(true);
|
||||
|
||||
checkAllButton = new QPushButton(tr("&Check all"));
|
||||
connect(checkAllButton, SIGNAL(clicked()), this, SLOT(actCheckAll()));
|
||||
uncheckAllButton = new QPushButton(tr("&Uncheck all"));
|
||||
connect(uncheckAllButton, SIGNAL(clicked()), this, SLOT(actUncheckAll()));
|
||||
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->addWidget(checkboxArea, 0, 0, 1, 2);
|
||||
layout->addWidget(checkAllButton, 1, 0);
|
||||
layout->addWidget(uncheckAllButton, 1, 1);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void ChooseSetsPage::initializePage()
|
||||
{
|
||||
// populate checkbox list
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
delete checkBoxList[i];
|
||||
checkBoxList.clear();
|
||||
|
||||
QList<SetToDownload> &sets = wizard()->importer->getSets();
|
||||
for (int i = 0; i < sets.size(); ++i) {
|
||||
QCheckBox *checkBox = new QCheckBox(sets[i].getLongName());
|
||||
checkBox->setChecked(sets[i].getImport());
|
||||
connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxChanged(int)));
|
||||
checkBoxLayout->addWidget(checkBox);
|
||||
checkBoxList << checkBox;
|
||||
}
|
||||
}
|
||||
|
||||
void ChooseSetsPage::checkBoxChanged(int state)
|
||||
{
|
||||
QCheckBox *checkBox = qobject_cast<QCheckBox *>(sender());
|
||||
QList<SetToDownload> &sets = wizard()->importer->getSets();
|
||||
for (int i = 0; i < sets.size(); ++i)
|
||||
if (sets[i].getLongName() == checkBox->text()) {
|
||||
sets[i].setImport(state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ChooseSetsPage::actCheckAll()
|
||||
{
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
checkBoxList[i]->setChecked(true);
|
||||
}
|
||||
|
||||
void ChooseSetsPage::actUncheckAll()
|
||||
{
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
checkBoxList[i]->setChecked(false);
|
||||
}
|
||||
|
||||
bool ChooseSetsPage::validatePage()
|
||||
{
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
{
|
||||
if(checkBoxList[i]->isChecked())
|
||||
return true;
|
||||
}
|
||||
|
||||
QMessageBox::critical(this, tr("Error"), tr("Please mark at least one set."));
|
||||
return false;
|
||||
}
|
||||
|
||||
SaveSetsPage::SaveSetsPage(QWidget *parent)
|
||||
: OracleWizardPage(parent)
|
||||
{
|
||||
setTitle(tr("Sets imported"));
|
||||
setSubTitle(tr("The following sets has been imported. "
|
||||
"Press \"Save\" to save the imported cards to the Cockatrice database."));
|
||||
|
||||
defaultPathCheckBox = new QCheckBox(this);
|
||||
defaultPathCheckBox->setText(tr("Save to the default path (recommended)"));
|
||||
defaultPathCheckBox->setChecked(true);
|
||||
|
||||
messageLog = new QTextEdit(this);
|
||||
messageLog->setReadOnly(true);
|
||||
|
||||
QGridLayout *layout = new QGridLayout(this);
|
||||
layout->addWidget(defaultPathCheckBox, 0, 0);
|
||||
layout->addWidget(messageLog, 1, 0);
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void SaveSetsPage::cleanupPage()
|
||||
{
|
||||
disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), 0, 0);
|
||||
}
|
||||
|
||||
void SaveSetsPage::initializePage()
|
||||
{
|
||||
messageLog->clear();
|
||||
|
||||
connect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &)));
|
||||
|
||||
if (!wizard()->importer->startImport())
|
||||
QMessageBox::critical(this, tr("Error"), tr("No set has been imported."));
|
||||
}
|
||||
|
||||
void SaveSetsPage::updateTotalProgress(int cardsImported, int setIndex, const QString &setName)
|
||||
{
|
||||
if (setName.isEmpty()) {
|
||||
messageLog->append("<b>" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + "</b>");
|
||||
} else {
|
||||
messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported));
|
||||
}
|
||||
messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
bool SaveSetsPage::validatePage()
|
||||
{
|
||||
bool ok = false;
|
||||
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
QDir dir(dataDir);
|
||||
if (!dir.exists())
|
||||
dir.mkpath(dataDir);
|
||||
QString savePath = dataDir + "/cards.xml";
|
||||
do {
|
||||
QString fileName;
|
||||
if (savePath.isEmpty() || !defaultPathCheckBox->isChecked())
|
||||
fileName = QFileDialog::getSaveFileName(this, tr("Save card database"), dataDir + "/cards.xml", tr("XML card database (*.xml)"));
|
||||
else {
|
||||
fileName = savePath;
|
||||
savePath.clear();
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (wizard()->importer->saveToFile(fileName))
|
||||
ok = true;
|
||||
else
|
||||
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location."));
|
||||
} while (!ok);
|
||||
|
||||
return true;
|
||||
}
|
114
oracle/src/oraclewizard.h
Normal file
114
oracle/src/oraclewizard.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
#ifndef ORACLEWIZARD_H
|
||||
#define ORACLEWIZARD_H
|
||||
|
||||
#include <QWizard>
|
||||
#include <QFutureWatcher>
|
||||
#include <QFuture>
|
||||
|
||||
class QCheckBox;
|
||||
class QGroupBox;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QRadioButton;
|
||||
class QProgressBar;
|
||||
class QNetworkAccessManager;
|
||||
class QTextEdit;
|
||||
class QVBoxLayout;
|
||||
class OracleImporter;
|
||||
class QSettings;
|
||||
|
||||
class OracleWizard : public QWizard
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OracleWizard(QWidget *parent = 0);
|
||||
void accept();
|
||||
void enableButtons();
|
||||
void disableButtons();
|
||||
public:
|
||||
OracleImporter *importer;
|
||||
QSettings * settings;
|
||||
};
|
||||
|
||||
|
||||
class OracleWizardPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OracleWizardPage(QWidget *parent = 0): QWizardPage(parent) {};
|
||||
protected:
|
||||
inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); };
|
||||
};
|
||||
|
||||
class IntroPage : public OracleWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IntroPage(QWidget *parent = 0);
|
||||
private:
|
||||
QLabel *label;
|
||||
};
|
||||
|
||||
class LoadSetsPage : public OracleWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LoadSetsPage(QWidget *parent = 0);
|
||||
protected:
|
||||
void initializePage();
|
||||
bool validatePage();
|
||||
void readSetsFromByteArray(QByteArray data);
|
||||
private:
|
||||
QRadioButton *urlRadioButton;
|
||||
QRadioButton *fileRadioButton;
|
||||
QLineEdit *urlLineEdit;
|
||||
QLineEdit *fileLineEdit;
|
||||
QPushButton *fileButton;
|
||||
QLabel *progressLabel;
|
||||
QProgressBar * progressBar;
|
||||
|
||||
QNetworkAccessManager *nam;
|
||||
QFutureWatcher<bool> watcher;
|
||||
QFuture<bool> future;
|
||||
private slots:
|
||||
void actLoadSetsFile();
|
||||
void actDownloadProgressSetsFile(qint64 received, qint64 total);
|
||||
void actDownloadFinishedSetsFile();
|
||||
void importFinished();
|
||||
};
|
||||
|
||||
class ChooseSetsPage : public OracleWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ChooseSetsPage(QWidget *parent = 0);
|
||||
protected:
|
||||
void initializePage();
|
||||
bool validatePage();
|
||||
private:
|
||||
QPushButton *checkAllButton, *uncheckAllButton;
|
||||
QVBoxLayout *checkBoxLayout;
|
||||
QList<QCheckBox *> checkBoxList;
|
||||
private slots:
|
||||
void actCheckAll();
|
||||
void actUncheckAll();
|
||||
void checkBoxChanged(int state);
|
||||
};
|
||||
|
||||
class SaveSetsPage : public OracleWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SaveSetsPage(QWidget *parent = 0);
|
||||
private:
|
||||
QTextEdit *messageLog;
|
||||
QCheckBox * defaultPathCheckBox;
|
||||
protected:
|
||||
void initializePage();
|
||||
void cleanupPage();
|
||||
bool validatePage();
|
||||
private slots:
|
||||
void updateTotalProgress(int cardsImported, int setIndex, const QString &setName);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,248 +0,0 @@
|
|||
#include <QApplication>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QStatusBar>
|
||||
#include <QLineEdit>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollArea>
|
||||
#include <QTextEdit>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QProgressBar>
|
||||
#include <QVBoxLayout>
|
||||
#include <QDesktopServices>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include "window_main.h"
|
||||
#include "oracleimporter.h"
|
||||
|
||||
const QString WindowMain::defaultSetsUrl = QString("http://www.woogerworks.com/files/sets.xml");
|
||||
|
||||
WindowMain::WindowMain(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
importer = new OracleImporter(QDesktopServices::storageLocation(QDesktopServices::DataLocation), this);
|
||||
nam = new QNetworkAccessManager(this);
|
||||
|
||||
checkBoxLayout = new QVBoxLayout;
|
||||
|
||||
QWidget *checkboxFrame = new QWidget;
|
||||
checkboxFrame->setLayout(checkBoxLayout);
|
||||
|
||||
QScrollArea *checkboxArea = new QScrollArea;
|
||||
checkboxArea->setWidget(checkboxFrame);
|
||||
checkboxArea->setWidgetResizable(true);
|
||||
|
||||
checkAllButton = new QPushButton(tr("&Check all"));
|
||||
connect(checkAllButton, SIGNAL(clicked()), this, SLOT(actCheckAll()));
|
||||
uncheckAllButton = new QPushButton(tr("&Uncheck all"));
|
||||
connect(uncheckAllButton, SIGNAL(clicked()), this, SLOT(actUncheckAll()));
|
||||
|
||||
QHBoxLayout *checkAllButtonLayout = new QHBoxLayout;
|
||||
checkAllButtonLayout->addWidget(checkAllButton);
|
||||
checkAllButtonLayout->addWidget(uncheckAllButton);
|
||||
|
||||
startButton = new QPushButton(tr("&Start download"));
|
||||
connect(startButton, SIGNAL(clicked()), this, SLOT(actStart()));
|
||||
|
||||
QVBoxLayout *settingsLayout = new QVBoxLayout;
|
||||
settingsLayout->addWidget(checkboxArea);
|
||||
settingsLayout->addLayout(checkAllButtonLayout);
|
||||
settingsLayout->addWidget(startButton);
|
||||
|
||||
totalLabel = new QLabel(tr("Total progress:"));
|
||||
totalProgressBar = new QProgressBar;
|
||||
nextSetLabel1 = new QLabel(tr("Current file:"));
|
||||
nextSetLabel2 = new QLabel;
|
||||
fileLabel = new QLabel(tr("Progress:"));
|
||||
fileProgressBar = new QProgressBar;
|
||||
|
||||
messageLog = new QTextEdit;
|
||||
messageLog->setReadOnly(true);
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
grid->addWidget(totalLabel, 0, 0);
|
||||
grid->addWidget(totalProgressBar, 0, 1);
|
||||
grid->addWidget(nextSetLabel1, 1, 0);
|
||||
grid->addWidget(nextSetLabel2, 1, 1);
|
||||
grid->addWidget(fileLabel, 2, 0);
|
||||
grid->addWidget(fileProgressBar, 2, 1);
|
||||
grid->addWidget(messageLog, 3, 0, 1, 2);
|
||||
|
||||
QHBoxLayout *mainLayout = new QHBoxLayout;
|
||||
mainLayout->addLayout(settingsLayout, 6);
|
||||
mainLayout->addSpacing(10);
|
||||
mainLayout->addLayout(grid, 10);
|
||||
|
||||
QWidget *centralWidget = new QWidget;
|
||||
centralWidget->setLayout(mainLayout);
|
||||
setCentralWidget(centralWidget);
|
||||
|
||||
connect(importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &)));
|
||||
connect(importer, SIGNAL(dataReadProgress(int, int)), this, SLOT(updateFileProgress(int, int)));
|
||||
|
||||
aLoadSetsFile = new QAction(tr("Load sets information from &file..."), this);
|
||||
connect(aLoadSetsFile, SIGNAL(triggered()), this, SLOT(actLoadSetsFile()));
|
||||
aDownloadSetsFile = new QAction(tr("&Download sets information..."), this);
|
||||
connect(aDownloadSetsFile, SIGNAL(triggered()), this, SLOT(actDownloadSetsFile()));
|
||||
aExit = new QAction(tr("E&xit"), this);
|
||||
connect(aExit, SIGNAL(triggered()), this, SLOT(close()));
|
||||
|
||||
fileMenu = menuBar()->addMenu(tr("&File"));
|
||||
fileMenu->addAction(aLoadSetsFile);
|
||||
fileMenu->addAction(aDownloadSetsFile);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(aExit);
|
||||
|
||||
setWindowTitle(tr("Oracle importer"));
|
||||
setMinimumSize(750, 500);
|
||||
|
||||
QStringList args = qApp->arguments();
|
||||
if (args.contains("-dlsets"))
|
||||
downloadSetsFile(defaultSetsUrl);
|
||||
|
||||
statusLabel = new QLabel;
|
||||
statusBar()->addWidget(statusLabel);
|
||||
statusLabel->setText(tr("Sets data not loaded."));
|
||||
}
|
||||
|
||||
void WindowMain::updateSetList()
|
||||
{
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
delete checkBoxList[i];
|
||||
checkBoxList.clear();
|
||||
|
||||
QList<SetToDownload> &sets = importer->getSets();
|
||||
for (int i = 0; i < sets.size(); ++i) {
|
||||
QCheckBox *checkBox = new QCheckBox(sets[i].getLongName());
|
||||
checkBox->setChecked(sets[i].getImport());
|
||||
connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxChanged(int)));
|
||||
checkBoxLayout->addWidget(checkBox);
|
||||
checkBoxList << checkBox;
|
||||
}
|
||||
statusLabel->setText(tr("Ready."));
|
||||
}
|
||||
|
||||
void WindowMain::actLoadSetsFile()
|
||||
{
|
||||
QFileDialog dialog(this, tr("Load sets file"));
|
||||
dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
dialog.setNameFilter("Sets XML file (*.xml)");
|
||||
if (!dialog.exec())
|
||||
return;
|
||||
|
||||
QString fileName = dialog.selectedFiles().at(0);
|
||||
if (importer->readSetsFromFile(fileName))
|
||||
updateSetList();
|
||||
else
|
||||
QMessageBox::critical(this, tr("Error"), tr("This file does not contain any sets data."));
|
||||
}
|
||||
|
||||
void WindowMain::actDownloadSetsFile()
|
||||
{
|
||||
QString url = QInputDialog::getText(this, tr("Load sets from URL"), tr("Please enter the URL of the sets file:"), QLineEdit::Normal, defaultSetsUrl);
|
||||
if (!url.isEmpty())
|
||||
downloadSetsFile(url);
|
||||
}
|
||||
|
||||
void WindowMain::downloadSetsFile(const QString &url)
|
||||
{
|
||||
QNetworkReply *reply = nam->get(QNetworkRequest(url));
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(setsDownloadFinished()));
|
||||
}
|
||||
|
||||
void WindowMain::setsDownloadFinished()
|
||||
{
|
||||
QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
|
||||
QNetworkReply::NetworkError errorCode = reply->error();
|
||||
if (errorCode == QNetworkReply::NoError) {
|
||||
if (importer->readSetsFromByteArray(reply->readAll()))
|
||||
updateSetList();
|
||||
else
|
||||
QMessageBox::critical(this, tr("Error"), tr("The file was retrieved successfully, but it does not contain any sets data."));
|
||||
} else
|
||||
QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString()));
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void WindowMain::updateTotalProgress(int cardsImported, int setIndex, const QString &nextSetName)
|
||||
{
|
||||
if (setIndex != 0)
|
||||
messageLog->append(QString("%1: %2 cards imported").arg(nextSetLabel2->text()).arg(cardsImported));
|
||||
totalProgressBar->setValue(setIndex);
|
||||
if (nextSetName.isEmpty()) {
|
||||
QMessageBox::information(this, tr("Oracle importer"), tr("Import finished: %1 cards.").arg(importer->getCardList().size()));
|
||||
bool ok = false;
|
||||
const QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
QDir dir(dataDir);
|
||||
if (!dir.exists())
|
||||
dir.mkpath(dataDir);
|
||||
QString savePath = dataDir + "/cards.xml";
|
||||
do {
|
||||
QString fileName;
|
||||
if (savePath.isEmpty())
|
||||
fileName = QFileDialog::getSaveFileName(this, tr("Save card database"), dataDir + "/cards.xml", tr("XML card database (*.xml)"));
|
||||
else {
|
||||
fileName = savePath;
|
||||
savePath.clear();
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
qApp->quit();
|
||||
return;
|
||||
}
|
||||
if (importer->saveToFile(fileName))
|
||||
ok = true;
|
||||
else
|
||||
QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to the desired location."));
|
||||
} while (!ok);
|
||||
qApp->quit();
|
||||
} else {
|
||||
nextSetLabel2->setText(nextSetName);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowMain::updateFileProgress(int bytesRead, int totalBytes)
|
||||
{
|
||||
fileProgressBar->setMaximum(totalBytes);
|
||||
fileProgressBar->setValue(bytesRead);
|
||||
}
|
||||
|
||||
void WindowMain::actCheckAll()
|
||||
{
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
checkBoxList[i]->setChecked(true);
|
||||
}
|
||||
|
||||
void WindowMain::actUncheckAll()
|
||||
{
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
checkBoxList[i]->setChecked(false);
|
||||
}
|
||||
|
||||
void WindowMain::actStart()
|
||||
{
|
||||
int setsCount = importer->startDownload();
|
||||
if (!setsCount) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("No sets to download selected."));
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < checkBoxList.size(); ++i)
|
||||
checkBoxList[i]->setEnabled(false);
|
||||
startButton->setEnabled(false);
|
||||
totalProgressBar->setMaximum(setsCount);
|
||||
statusLabel->setText(tr("Downloading card data..."));
|
||||
}
|
||||
|
||||
void WindowMain::checkBoxChanged(int state)
|
||||
{
|
||||
QCheckBox *checkBox = qobject_cast<QCheckBox *>(sender());
|
||||
QList<SetToDownload> &sets = importer->getSets();
|
||||
for (int i = 0; i < sets.size(); ++i)
|
||||
if (sets[i].getLongName() == checkBox->text()) {
|
||||
sets[i].setImport(state);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef WINDOW_MAIN_H
|
||||
#define WINDOW_MAIN_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QList>
|
||||
|
||||
class OracleImporter;
|
||||
class QLabel;
|
||||
class QProgressBar;
|
||||
class QTextEdit;
|
||||
class QPushButton;
|
||||
class QCheckBox;
|
||||
class QVBoxLayout;
|
||||
class QMenu;
|
||||
class QAction;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class WindowMain : public QMainWindow {
|
||||
Q_OBJECT
|
||||
private:
|
||||
static const QString defaultSetsUrl;
|
||||
|
||||
OracleImporter *importer;
|
||||
QNetworkAccessManager *nam;
|
||||
|
||||
QMenu *fileMenu;
|
||||
QAction *aLoadSetsFile, *aDownloadSetsFile, *aExit;
|
||||
QPushButton *checkAllButton, *uncheckAllButton, *startButton;
|
||||
QLabel *totalLabel, *fileLabel, *nextSetLabel1, *nextSetLabel2;
|
||||
QProgressBar *totalProgressBar, *fileProgressBar;
|
||||
QTextEdit *messageLog;
|
||||
QVBoxLayout *checkBoxLayout;
|
||||
QList<QCheckBox *> checkBoxList;
|
||||
QLabel *statusLabel;
|
||||
|
||||
void downloadSetsFile(const QString &url);
|
||||
private slots:
|
||||
void updateTotalProgress(int cardsImported, int setIndex, const QString &nextSetName);
|
||||
void updateFileProgress(int bytesRead, int totalBytes);
|
||||
void updateSetList();
|
||||
void actCheckAll();
|
||||
void actUncheckAll();
|
||||
void actStart();
|
||||
void actLoadSetsFile();
|
||||
void actDownloadSetsFile();
|
||||
void setsDownloadFinished();
|
||||
void checkBoxChanged(int state);
|
||||
public:
|
||||
WindowMain(QWidget *parent = 0);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,44 +1,30 @@
|
|||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# CMakeLists for servatrice directory
|
||||
#
|
||||
# provides the servatrice binary
|
||||
|
||||
PROJECT(servatrice)
|
||||
|
||||
# cmake modules
|
||||
include(GNUInstallDirs)
|
||||
# cmake module for libgcrypt is included in current directory
|
||||
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
FIND_PACKAGE(Libgcrypt REQUIRED)
|
||||
|
||||
SET(servatrice_SOURCES
|
||||
src/main.cpp
|
||||
src/passwordhasher.cpp
|
||||
src/servatrice.cpp
|
||||
src/servatrice_connection_pool.cpp
|
||||
src/servatrice_database_interface.cpp
|
||||
src/server_logger.cpp
|
||||
src/serversocketinterface.cpp
|
||||
src/isl_interface.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp
|
||||
)
|
||||
SET(servatrice_HEADERS
|
||||
src/servatrice.h
|
||||
src/servatrice_connection_pool.h
|
||||
src/servatrice_database_interface.h
|
||||
src/server_logger.h
|
||||
src/serversocketinterface.h
|
||||
src/isl_interface.h
|
||||
src/main.cpp
|
||||
src/passwordhasher.cpp
|
||||
src/servatrice.cpp
|
||||
src/servatrice_connection_pool.cpp
|
||||
src/servatrice_database_interface.cpp
|
||||
src/server_logger.cpp
|
||||
src/serversocketinterface.cpp
|
||||
src/isl_interface.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp
|
||||
)
|
||||
|
||||
SET(QT_DONTUSE_QTGUI)
|
||||
SET(QT_USE_QTNETWORK TRUE)
|
||||
SET(QT_USE_QTSQL TRUE)
|
||||
FIND_PACKAGE(Qt4 REQUIRED)
|
||||
FIND_PACKAGE(Protobuf REQUIRED)
|
||||
FIND_PACKAGE(Libgcrypt REQUIRED)
|
||||
FIND_PACKAGE(Threads)
|
||||
|
||||
#set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O2")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||
|
||||
QT4_WRAP_CPP(servatrice_HEADERS_MOC ${servatrice_HEADERS})
|
||||
|
||||
# Include directories
|
||||
INCLUDE(${QT_USE_FILE})
|
||||
INCLUDE_DIRECTORIES(../common)
|
||||
INCLUDE_DIRECTORIES(${LIBGCRYPT_INCLUDE_DIR})
|
||||
|
@ -46,14 +32,53 @@ INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR})
|
|||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/../common)
|
||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
ADD_EXECUTABLE(servatrice ${servatrice_SOURCES} ${servatrice_HEADERS_MOC})
|
||||
# Build servatrice binary and link it
|
||||
ADD_EXECUTABLE(servatrice MACOSX_BUNDLE ${servatrice_SOURCES} ${servatrice_MOC_SRCS})
|
||||
TARGET_LINK_LIBRARIES(servatrice cockatrice_common ${QT_LIBRARIES} ${LIBGCRYPT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
#add_custom_target(versionheader ALL DEPENDS version_header)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version_string.h ${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp
|
||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/../common/getversion.cmake
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/version_string.h ${CMAKE_CURRENT_BINARY_DIR}/version_string.cpp
|
||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/../common/getversion.cmake
|
||||
)
|
||||
|
||||
# install rules
|
||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/servatrice DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
INSTALL(TARGETS servatrice BUNDLE DESTINATION ./)
|
||||
else()
|
||||
# Assume linux
|
||||
INSTALL(TARGETS servatrice RUNTIME DESTINATION bin/)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
INSTALL(TARGETS servatrice RUNTIME DESTINATION ./)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(plugin_dest_dir ./servatrice.app/Contents/Plugins)
|
||||
set(qtconf_dest_dir ./servatrice.app/Contents/Resources)
|
||||
|
||||
# note: no codecs in qt5
|
||||
# note: phonon_backend => mediaservice
|
||||
# note: needs platform on osx
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(codecs|iconengines|imageformats|mediaservice|phonon_backend|platforms)/.*_debug\\.dylib")
|
||||
else()
|
||||
install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime
|
||||
FILES_MATCHING REGEX "(codecs|iconengines|imageformats|mediaservice|phonon_backend|platforms)/[^_]*\\.dylib")
|
||||
endif()
|
||||
|
||||
install(CODE "
|
||||
file(WRITE \"${qtconf_dest_dir}/qt.conf\" \"[Paths]
|
||||
Plugins = Plugins\")
|
||||
" COMPONENT Runtime)
|
||||
|
||||
install(CODE "
|
||||
file(GLOB_RECURSE QTPLUGINS
|
||||
\"${plugin_dest_dir}/*.dylib\")
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\")
|
||||
" COMPONENT Runtime)
|
||||
endif()
|
||||
|
|
16
sounds/CMakeLists.txt
Normal file
16
sounds/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
# CMakeLists for sounds/ directory
|
||||
#
|
||||
# Installs default sound files
|
||||
|
||||
FILE(GLOB sounds "${CMAKE_CURRENT_SOURCE_DIR}/*.raw")
|
||||
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
INSTALL(FILES ${sounds} DESTINATION sounds/)
|
||||
else()
|
||||
# Assume linux
|
||||
INSTALL(FILES ${sounds} DESTINATION share/cockatrice/sounds/)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
INSTALL(FILES ${sounds} DESTINATION sounds/)
|
||||
endif()
|
16
zonebg/CMakeLists.txt
Normal file
16
zonebg/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
# CMakeLists for zonebg/ directory
|
||||
#
|
||||
# Installs default "zone background" files
|
||||
|
||||
FILE(GLOB zonebg "${CMAKE_CURRENT_SOURCE_DIR}/*.*")
|
||||
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
INSTALL(FILES ${zonebg} DESTINATION zonebg/)
|
||||
else()
|
||||
# Assume linux
|
||||
INSTALL(FILES ${zonebg} DESTINATION share/cockatrice/zonebg/)
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
INSTALL(FILES ${zonebg} DESTINATION zonebg/)
|
||||
endif()
|
Loading…
Reference in a new issue