Merge remote-tracking branch 'upstream/develop' into enet
# Conflicts: # CI/mac/before_install.sh
@ -202,7 +202,6 @@ void ObjectClusterizer::clusterize()
|
||||
Obj::WHIRLPOOL,
|
||||
Obj::BUOY,
|
||||
Obj::SIGN,
|
||||
Obj::SIGN,
|
||||
Obj::GARRISON,
|
||||
Obj::MONSTER,
|
||||
Obj::GARRISON2,
|
||||
|
12
CI/conan/base/apple
Normal file
@ -0,0 +1,12 @@
|
||||
[settings]
|
||||
compiler=apple-clang
|
||||
compiler.version=14
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
|
||||
# required for Boost.Locale in versions >= 1.81
|
||||
compiler.cppstd=11
|
||||
|
||||
[conf]
|
||||
tools.apple:enable_bitcode = False
|
||||
tools.cmake.cmaketoolchain:generator = Ninja
|
5
CI/conan/base/ios
Normal file
@ -0,0 +1,5 @@
|
||||
include(apple)
|
||||
|
||||
[settings]
|
||||
os=iOS
|
||||
os.sdk=iphoneos
|
4
CI/conan/base/macos
Normal file
@ -0,0 +1,4 @@
|
||||
include(apple)
|
||||
|
||||
[settings]
|
||||
os=Macos
|
@ -1,14 +1,5 @@
|
||||
include(base/ios)
|
||||
|
||||
[settings]
|
||||
os=iOS
|
||||
os.version=12.0
|
||||
os.sdk=iphoneos
|
||||
arch=armv8
|
||||
compiler=apple-clang
|
||||
compiler.version=13
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
[options]
|
||||
[build_requires]
|
||||
[env]
|
||||
[conf]
|
||||
tools.cmake.cmaketoolchain:generator = Ninja
|
||||
|
@ -1,14 +1,8 @@
|
||||
include(base/ios)
|
||||
|
||||
[settings]
|
||||
os=iOS
|
||||
os.version=10.0
|
||||
os.sdk=iphoneos
|
||||
arch=armv7
|
||||
compiler=apple-clang
|
||||
|
||||
# Xcode 13.x is the last version that can build for armv7
|
||||
compiler.version=13
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
[options]
|
||||
[build_requires]
|
||||
[env]
|
||||
[conf]
|
||||
tools.cmake.cmaketoolchain:generator = Ninja
|
||||
|
@ -1,13 +1,5 @@
|
||||
include(base/macos)
|
||||
|
||||
[settings]
|
||||
os=Macos
|
||||
os.version=11.0
|
||||
arch=armv8
|
||||
compiler=apple-clang
|
||||
compiler.version=13
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
[options]
|
||||
[build_requires]
|
||||
[env]
|
||||
[conf]
|
||||
tools.cmake.cmaketoolchain:generator = Ninja
|
||||
|
@ -1,13 +1,5 @@
|
||||
include(base/macos)
|
||||
|
||||
[settings]
|
||||
os=Macos
|
||||
os.version=10.13
|
||||
arch=x86_64
|
||||
compiler=apple-clang
|
||||
compiler.version=13
|
||||
compiler.libcxx=libc++
|
||||
build_type=Release
|
||||
[options]
|
||||
[build_requires]
|
||||
[env]
|
||||
[conf]
|
||||
tools.cmake.cmaketoolchain:generator = Ninja
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV
|
||||
echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV
|
||||
|
||||
mkdir ~/.conan ; cd ~/.conan
|
||||
curl -L 'https://github.com/vcmi/vcmi-ios-deps/releases/download/1.1/ios-arm64.xz' \
|
||||
curl -L 'https://github.com/vcmi/vcmi-ios-deps/releases/download/1.2/ios-arm64.txz' \
|
||||
| tar -xf -
|
||||
|
@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV
|
||||
echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV
|
||||
|
||||
brew install ninja
|
||||
|
||||
mkdir ~/.conan ; cd ~/.conan
|
||||
curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/download/1.1/$DEPS_FILENAME.txz" \
|
||||
| tar -xf -
|
||||
curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/download/1.2/$DEPS_FILENAME.txz" \
|
||||
| tar -xf -
|
||||
|
@ -1,10 +1,10 @@
|
||||
curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z" \
|
||||
"https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.5/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
|
||||
7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
|
||||
curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z" \
|
||||
"https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.6/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z"
|
||||
7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z"
|
||||
|
||||
rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug
|
||||
mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
|
||||
cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
|
||||
#rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug
|
||||
#mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
|
||||
#cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
|
||||
|
||||
DUMPBIN_DIR=$(vswhere -latest -find **/dumpbin.exe | head -n 1)
|
||||
dirname "$DUMPBIN_DIR" > $GITHUB_PATH
|
||||
dirname "$DUMPBIN_DIR" > $GITHUB_PATH
|
||||
|
@ -90,6 +90,11 @@ if(APPLE_IOS AND COPY_CONFIG_ON_BUILD)
|
||||
set(COPY_CONFIG_ON_BUILD OFF)
|
||||
endif()
|
||||
|
||||
# No QT Linguist on MXE
|
||||
if((MINGW) AND (${CMAKE_CROSSCOMPILING}))
|
||||
set(ENABLE_TRANSLATIONS OFF)
|
||||
endif()
|
||||
|
||||
############################################
|
||||
# Miscellaneous options #
|
||||
############################################
|
||||
@ -181,6 +186,14 @@ set(CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION ${APP_SHORT_VERSION})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH NO)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES)
|
||||
|
||||
if(ENABLE_LAUNCHER)
|
||||
add_definitions(-DENABLE_LAUNCHER)
|
||||
endif()
|
||||
|
||||
if(ENABLE_EDITOR)
|
||||
add_definitions(-DENABLE_EDITOR)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SINGLE_APP_BUILD)
|
||||
add_definitions(-DSINGLE_PROCESS_APP=1)
|
||||
endif()
|
||||
@ -232,7 +245,7 @@ if(MINGW OR MSVC)
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800") # 4800: implicit conversion from 'xxx' to bool. Possible information loss
|
||||
|
||||
if(ENABLE_STRICT_COMPILATION)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wx") # Treats all compiler warnings as errors
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") # Treats all compiler warnings as errors
|
||||
endif()
|
||||
|
||||
if(ENABLE_MULTI_PROCESS_BUILDS)
|
||||
@ -361,12 +374,8 @@ if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS LinguistTools)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools)
|
||||
if(NOT Qt${QT_VERSION_MAJOR}LinguistTools_DIR)
|
||||
set(ENABLE_TRANSLATIONS OFF)
|
||||
endif()
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS LinguistTools)
|
||||
add_definitions(-DENABLE_QT_TRANSLATIONS)
|
||||
endif()
|
||||
endif()
|
||||
@ -564,6 +573,9 @@ if(WIN32)
|
||||
FILES ${integration_loc}
|
||||
DESTINATION ${BIN_DIR}/platforms
|
||||
)
|
||||
install(
|
||||
FILES "$<TARGET_FILE:Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin>"
|
||||
DESTINATION ${BIN_DIR}/styles)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -1,6 +1,20 @@
|
||||
{
|
||||
"name" : "VCMI essential files",
|
||||
"description" : "Essential files required for VCMI to run correctly",
|
||||
|
||||
"german" : {
|
||||
"name" : "VCMI - grundlegende Dateien",
|
||||
"description" : "Grundlegende Dateien, die für die korrekte Ausführung von VCMI erforderlich sind",
|
||||
"author" : "VCMI-Team",
|
||||
"modType" : "Grafik",
|
||||
},
|
||||
|
||||
"ukrainian" : {
|
||||
"name" : "VCMI - ключові файли",
|
||||
"description" : "Ключові файли необхідні для повноцінної роботи VCMI",
|
||||
"author" : "Команда VCMI",
|
||||
"modType" : "Графіка",
|
||||
},
|
||||
|
||||
"version" : "1.1",
|
||||
"author" : "VCMI Team",
|
||||
|
@ -38,7 +38,7 @@ VCMI_LIB_NAMESPACE_END
|
||||
class CMapHandler;
|
||||
class CSoundHandler;
|
||||
class CMusicHandler;
|
||||
class CCursorHandler;
|
||||
class CursorHandler;
|
||||
class IMainVideoPlayer;
|
||||
class CServerHandler;
|
||||
|
||||
@ -49,7 +49,7 @@ public:
|
||||
CSoundHandler * soundh;
|
||||
CMusicHandler * musich;
|
||||
CConsoleHandler * consoleh;
|
||||
CCursorHandler * curh;
|
||||
CursorHandler * curh;
|
||||
IMainVideoPlayer * videoh;
|
||||
};
|
||||
extern CClientState * CCS;
|
||||
|
520
client/CMT.cpp
@ -13,8 +13,6 @@
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <vcmi/scripting/Service.h>
|
||||
|
||||
#include "gui/SDL_Extensions.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "mapHandler.h"
|
||||
@ -25,7 +23,7 @@
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
#include "gui/CCursorHandler.h"
|
||||
#include "gui/CursorHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../CCallback.h"
|
||||
#include "CPlayerInterface.h"
|
||||
@ -33,32 +31,26 @@
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "CVideoHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "Graphics.h"
|
||||
#include "Client.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "CMessage.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/ScriptHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "gui/CAnimation.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/NotificationHandler.h"
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@ -71,7 +63,7 @@
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "lib/CAndroidVMHelper.h"
|
||||
#endif
|
||||
#include "../lib/UnlockGuard.h"
|
||||
|
||||
#include "CMT.h"
|
||||
|
||||
#if __MINGW32__
|
||||
@ -208,6 +200,8 @@ int main(int argc, char * argv[])
|
||||
("lobby-host", "if this client hosts session")
|
||||
("lobby-uuid", po::value<std::string>(), "uuid to the server")
|
||||
("lobby-connections", po::value<ui16>(), "connections of server")
|
||||
("lobby-username", po::value<std::string>(), "player name")
|
||||
("lobby-gamemode", po::value<ui16>(), "use 0 for new game and 1 for load game")
|
||||
("uuid", po::value<std::string>(), "uuid for the client");
|
||||
|
||||
if(argc > 1)
|
||||
@ -247,7 +241,14 @@ int main(int argc, char * argv[])
|
||||
std::cout.flags(std::ios::unitbuf);
|
||||
#ifndef VCMI_IOS
|
||||
console = new CConsoleHandler();
|
||||
*console->cb = processCommand;
|
||||
|
||||
auto callbackFunction = [](std::string buffer, bool calledFromIngameConsole)
|
||||
{
|
||||
ClientCommandManager commandController;
|
||||
commandController.processCommand(buffer, calledFromIngameConsole);
|
||||
};
|
||||
|
||||
*console->cb = callbackFunction;
|
||||
console->start();
|
||||
#endif
|
||||
|
||||
@ -470,7 +471,7 @@ int main(int argc, char * argv[])
|
||||
pomtime.getDiff();
|
||||
graphics = new Graphics(); // should be before curh
|
||||
|
||||
CCS->curh = new CCursorHandler();
|
||||
CCS->curh = new CursorHandler();
|
||||
logGlobal->info("Screen handler: %d ms", pomtime.getDiff());
|
||||
pomtime.getDiff();
|
||||
|
||||
@ -489,29 +490,8 @@ int main(int argc, char * argv[])
|
||||
session["autoSkip"].Bool() = vm.count("autoSkip");
|
||||
session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
|
||||
session["aiSolo"].Bool() = false;
|
||||
std::shared_ptr<CMainMenu> mmenu;
|
||||
|
||||
session["lobby"].Bool() = false;
|
||||
if(vm.count("lobby"))
|
||||
{
|
||||
session["lobby"].Bool() = true;
|
||||
session["host"].Bool() = false;
|
||||
session["address"].String() = vm["lobby-address"].as<std::string>();
|
||||
CSH->uuid = vm["uuid"].as<std::string>();
|
||||
session["port"].Integer() = vm["lobby-port"].as<ui16>();
|
||||
logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid);
|
||||
if(vm.count("lobby-host"))
|
||||
{
|
||||
session["host"].Bool() = true;
|
||||
session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as<ui16>());
|
||||
session["hostUuid"].String() = vm["lobby-uuid"].as<std::string>();
|
||||
logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String());
|
||||
}
|
||||
|
||||
//we should not reconnect to previous game in online mode
|
||||
Settings saveSession = settings.write["server"]["reconnect"];
|
||||
saveSession->Bool() = false;
|
||||
}
|
||||
|
||||
if(vm.count("testmap"))
|
||||
{
|
||||
session["testmap"].String() = vm["testmap"].as<std::string>();
|
||||
@ -526,7 +506,44 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
GH.curInt = CMainMenu::create().get();
|
||||
mmenu = CMainMenu::create();
|
||||
GH.curInt = mmenu.get();
|
||||
}
|
||||
|
||||
std::vector<std::string> names;
|
||||
session["lobby"].Bool() = false;
|
||||
if(vm.count("lobby"))
|
||||
{
|
||||
session["lobby"].Bool() = true;
|
||||
session["host"].Bool() = false;
|
||||
session["address"].String() = vm["lobby-address"].as<std::string>();
|
||||
if(vm.count("lobby-username"))
|
||||
session["username"].String() = vm["lobby-username"].as<std::string>();
|
||||
else
|
||||
session["username"].String() = settings["launcher"]["lobbyUsername"].String();
|
||||
if(vm.count("lobby-gamemode"))
|
||||
session["gamemode"].Integer() = vm["lobby-gamemode"].as<ui16>();
|
||||
else
|
||||
session["gamemode"].Integer() = 0;
|
||||
CSH->uuid = vm["uuid"].as<std::string>();
|
||||
session["port"].Integer() = vm["lobby-port"].as<ui16>();
|
||||
logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid);
|
||||
if(vm.count("lobby-host"))
|
||||
{
|
||||
session["host"].Bool() = true;
|
||||
session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as<ui16>());
|
||||
session["hostUuid"].String() = vm["lobby-uuid"].as<std::string>();
|
||||
logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String());
|
||||
}
|
||||
|
||||
//we should not reconnect to previous game in online mode
|
||||
Settings saveSession = settings.write["server"]["reconnect"];
|
||||
saveSession->Bool() = false;
|
||||
|
||||
//start lobby immediately
|
||||
names.push_back(session["username"].String());
|
||||
ESelectionScreen sscreen = session["gamemode"].Integer() == 0 ? ESelectionScreen::newGame : ESelectionScreen::loadGame;
|
||||
mmenu->openLobby(sscreen, session["host"].Bool(), &names, ELoadMode::MULTI);
|
||||
}
|
||||
|
||||
// Restore remote session - start game immediately
|
||||
@ -548,437 +565,6 @@ int main(int argc, char * argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printInfoAboutIntObject(const CIntObject *obj, int level)
|
||||
{
|
||||
std::stringstream sbuffer;
|
||||
sbuffer << std::string(level, '\t');
|
||||
|
||||
sbuffer << typeid(*obj).name() << " *** ";
|
||||
if (obj->active)
|
||||
{
|
||||
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
|
||||
PRINT(LCLICK, 'L');
|
||||
PRINT(RCLICK, 'R');
|
||||
PRINT(HOVER, 'H');
|
||||
PRINT(MOVE, 'M');
|
||||
PRINT(KEYBOARD, 'K');
|
||||
PRINT(TIME, 'T');
|
||||
PRINT(GENERAL, 'A');
|
||||
PRINT(WHEEL, 'W');
|
||||
PRINT(DOUBLECLICK, 'D');
|
||||
#undef PRINT
|
||||
}
|
||||
else
|
||||
sbuffer << "inactive";
|
||||
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
|
||||
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
|
||||
logGlobal->info(sbuffer.str());
|
||||
|
||||
for(const CIntObject *child : obj->children)
|
||||
printInfoAboutIntObject(child, level+1);
|
||||
}
|
||||
|
||||
void removeGUI()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
adventureInt = nullptr;
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar = nullptr;
|
||||
logGlobal->info("Removed GUI.");
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
}
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
void processCommand(const std::string &message)
|
||||
{
|
||||
std::istringstream readed;
|
||||
readed.str(message);
|
||||
std::string cn; //command name
|
||||
readed >> cn;
|
||||
|
||||
// Check mantis issue 2292 for details
|
||||
// if(LOCPLINT && LOCPLINT->cingconsole)
|
||||
// LOCPLINT->cingconsole->print(message);
|
||||
|
||||
if(message==std::string("die, fool"))
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if(cn==std::string("activate"))
|
||||
{
|
||||
int what;
|
||||
readed >> what;
|
||||
switch (what)
|
||||
{
|
||||
case 0:
|
||||
GH.topInt()->activate();
|
||||
break;
|
||||
case 1:
|
||||
adventureInt->activate();
|
||||
break;
|
||||
case 2:
|
||||
LOCPLINT->castleInt->activate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(cn=="redraw")
|
||||
{
|
||||
GH.totalRedraw();
|
||||
}
|
||||
else if(cn=="screen")
|
||||
{
|
||||
std::cout << "Screenbuf points to ";
|
||||
|
||||
if(screenBuf == screen)
|
||||
logGlobal->error("screen");
|
||||
else if(screenBuf == screen2)
|
||||
logGlobal->error("screen2");
|
||||
else
|
||||
logGlobal->error("?!?");
|
||||
|
||||
SDL_SaveBMP(screen, "Screen_c.bmp");
|
||||
SDL_SaveBMP(screen2, "Screen2_c.bmp");
|
||||
}
|
||||
else if(cn=="save")
|
||||
{
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
CSH->client->save(fname);
|
||||
}
|
||||
// else if(cn=="load")
|
||||
// {
|
||||
// // TODO: this code should end the running game and manage to call startGame instead
|
||||
// std::string fname;
|
||||
// readed >> fname;
|
||||
// CSH->client->loadGame(fname);
|
||||
// }
|
||||
else if(message=="convert txt")
|
||||
{
|
||||
VLC->generaltexth->dumpAllTexts();
|
||||
}
|
||||
else if(message=="get config")
|
||||
{
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "configuration";
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
|
||||
|
||||
for(auto contentName : contentNames)
|
||||
{
|
||||
auto & content = (*VLC->modh->content)[contentName];
|
||||
|
||||
auto contentOutPath = outPath / contentName;
|
||||
bfs::create_directories(contentOutPath);
|
||||
|
||||
for(auto & iter : content.modData)
|
||||
{
|
||||
const JsonNode & modData = iter.second.modData;
|
||||
|
||||
for(auto & nameAndObject : modData.Struct())
|
||||
{
|
||||
const JsonNode & object = nameAndObject.second;
|
||||
|
||||
std::string name = CModHandler::normalizeIdentifier(object.meta, CModHandler::scopeBuiltin(), nameAndObject.first);
|
||||
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const bfs::path filePath = contentOutPath / (name + ".json");
|
||||
bfs::ofstream file(filePath);
|
||||
file << object.toJson();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||
}
|
||||
#if SCRIPTING_ENABLED
|
||||
else if(message=="get scripts")
|
||||
{
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "scripts";
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
for(auto & kv : VLC->scriptHandler->objects)
|
||||
{
|
||||
std::string name = kv.first;
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const scripting::ScriptImpl * script = kv.second.get();
|
||||
bfs::path filePath = outPath / (name + ".lua");
|
||||
bfs::ofstream file(filePath);
|
||||
file << script->getSource();
|
||||
}
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||
}
|
||||
#endif
|
||||
else if(message=="get txt")
|
||||
{
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userExtractedPath();
|
||||
|
||||
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
|
||||
});
|
||||
|
||||
for (auto & filename : list)
|
||||
{
|
||||
const bfs::path filePath = outPath / (filename.getName() + ".TXT");
|
||||
|
||||
bfs::create_directories(filePath.parent_path());
|
||||
|
||||
bfs::ofstream file(filePath);
|
||||
auto text = CResourceHandler::get()->load(filename)->readAll();
|
||||
|
||||
file.write((char*)text.first.get(), text.second);
|
||||
}
|
||||
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||
}
|
||||
else if(cn=="crash")
|
||||
{
|
||||
int *ptr = nullptr;
|
||||
*ptr = 666;
|
||||
//disaster!
|
||||
}
|
||||
else if(cn == "mp" && adventureInt)
|
||||
{
|
||||
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
||||
std::cout << h->movement << "; max: " << h->maxMovePoints(true) << "/" << h->maxMovePoints(false) << std::endl;
|
||||
}
|
||||
else if(cn == "bonuses")
|
||||
{
|
||||
bool jsonFormat = (message == "bonuses json");
|
||||
auto format = [jsonFormat](const BonusList & b) -> std::string
|
||||
{
|
||||
if(jsonFormat)
|
||||
return b.toJsonNode().toJson(true);
|
||||
std::ostringstream ss;
|
||||
ss << b;
|
||||
return ss.str();
|
||||
};
|
||||
std::cout << "Bonuses of " << adventureInt->selection->getObjectName() << std::endl
|
||||
<< format(adventureInt->selection->getBonusList()) << std::endl;
|
||||
|
||||
std::cout << "\nInherited bonuses:\n";
|
||||
TCNodes parents;
|
||||
adventureInt->selection->getParents(parents);
|
||||
for(const CBonusSystemNode *parent : parents)
|
||||
{
|
||||
std::cout << "\nBonuses from " << typeid(*parent).name() << std::endl << format(*parent->getAllBonuses(Selector::all, Selector::all)) << std::endl;
|
||||
}
|
||||
}
|
||||
else if(cn == "not dialog")
|
||||
{
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
}
|
||||
else if(cn == "gui")
|
||||
{
|
||||
for(auto & child : GH.listInt)
|
||||
{
|
||||
const auto childPtr = child.get();
|
||||
if(const CIntObject * obj = dynamic_cast<const CIntObject *>(childPtr))
|
||||
printInfoAboutIntObject(obj, 0);
|
||||
else
|
||||
std::cout << typeid(childPtr).name() << std::endl;
|
||||
}
|
||||
}
|
||||
else if(cn=="tell")
|
||||
{
|
||||
std::string what;
|
||||
int id1, id2;
|
||||
readed >> what >> id1 >> id2;
|
||||
if(what == "hs")
|
||||
{
|
||||
for(const CGHeroInstance *h : LOCPLINT->cb->getHeroesInfo())
|
||||
if(h->type->ID.getNum() == id1)
|
||||
if(const CArtifactInstance *a = h->getArt(ArtifactPosition(id2)))
|
||||
std::cout << a->nodeName();
|
||||
}
|
||||
}
|
||||
else if (cn == "set")
|
||||
{
|
||||
std::string what, value;
|
||||
readed >> what;
|
||||
|
||||
Settings conf = settings.write["session"][what];
|
||||
|
||||
readed >> value;
|
||||
|
||||
if (value == "on")
|
||||
{
|
||||
conf->Bool() = true;
|
||||
logGlobal->info("Option %s enabled!", what);
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
conf->Bool() = false;
|
||||
logGlobal->info("Option %s disabled!", what);
|
||||
}
|
||||
}
|
||||
else if(cn == "unlock")
|
||||
{
|
||||
std::string mxname;
|
||||
readed >> mxname;
|
||||
if(mxname == "pim" && LOCPLINT)
|
||||
LOCPLINT->pim->unlock();
|
||||
}
|
||||
else if(cn == "def2bmp")
|
||||
{
|
||||
std::string URI;
|
||||
readed >> URI;
|
||||
std::unique_ptr<CAnimation> anim = std::make_unique<CAnimation>(URI);
|
||||
anim->preload();
|
||||
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||
}
|
||||
else if(cn == "extract")
|
||||
{
|
||||
std::string URI;
|
||||
readed >> URI;
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||
{
|
||||
const bfs::path outPath = VCMIDirs::get().userExtractedPath() / URI;
|
||||
|
||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||
|
||||
bfs::create_directories(outPath.parent_path());
|
||||
bfs::ofstream outFile(outPath, bfs::ofstream::binary);
|
||||
outFile.write((char*)data.first.get(), data.second);
|
||||
}
|
||||
else
|
||||
logGlobal->error("File not found!");
|
||||
}
|
||||
else if(cn == "setBattleAI")
|
||||
{
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
std::cout << "Will try loading that AI to see if it is correct name...\n";
|
||||
try
|
||||
{
|
||||
if(auto ai = CDynLibHandler::getNewBattleAI(fname)) //test that given AI is indeed available... heavy but it is easy to make a typo and break the game
|
||||
{
|
||||
Settings neutralAI = settings.write["server"]["neutralAI"];
|
||||
neutralAI->String() = fname;
|
||||
std::cout << "Setting changed, from now the battle ai will be " << fname << "!\n";
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
logGlobal->warn("Failed opening %s: %s", fname, e.what());
|
||||
logGlobal->warn("Setting not changes, AI not found or invalid!");
|
||||
}
|
||||
}
|
||||
|
||||
auto giveTurn = [&](PlayerColor player)
|
||||
{
|
||||
YourTurn yt;
|
||||
yt.player = player;
|
||||
yt.daysWithoutCastle = CSH->client->getPlayerState(player)->daysWithoutCastle;
|
||||
yt.applyCl(CSH->client);
|
||||
};
|
||||
|
||||
Settings session = settings.write["session"];
|
||||
if(cn == "autoskip")
|
||||
{
|
||||
session["autoSkip"].Bool() = !session["autoSkip"].Bool();
|
||||
}
|
||||
else if(cn == "gosolo")
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(session["aiSolo"].Bool())
|
||||
{
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = LOCPLINT->playerID;
|
||||
removeGUI();
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
{
|
||||
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
|
||||
logNetwork->info("Player %s will be lead by %s", elem.first, AiToGive);
|
||||
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
}
|
||||
}
|
||||
GH.totalRedraw();
|
||||
giveTurn(color);
|
||||
}
|
||||
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
|
||||
}
|
||||
else if(cn == "controlai")
|
||||
{
|
||||
std::string colorName;
|
||||
readed >> colorName;
|
||||
boost::to_lower(colorName);
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(LOCPLINT)
|
||||
color = LOCPLINT->playerID;
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human || (colorName.length() &&
|
||||
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
removeGUI();
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
GH.totalRedraw();
|
||||
if(color != PlayerColor::NEUTRAL)
|
||||
giveTurn(color);
|
||||
}
|
||||
// Check mantis issue 2292 for details
|
||||
/* else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
LOCPLINT->cb->sendMessage(message);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
//plays intro, ends when intro is over or button has been pressed (handles events)
|
||||
void playIntro()
|
||||
{
|
||||
|
@ -19,5 +19,4 @@ extern SDL_Surface *screen; // main screen surface
|
||||
extern SDL_Surface *screen2; // and hlp surface (used to store not-active interfaces layer)
|
||||
extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
|
||||
|
||||
void removeGUI();
|
||||
void handleQuit(bool ask = true);
|
||||
|
@ -18,7 +18,7 @@ set(client_SRCS
|
||||
|
||||
gui/CAnimation.cpp
|
||||
gui/Canvas.cpp
|
||||
gui/CCursorHandler.cpp
|
||||
gui/CursorHandler.cpp
|
||||
gui/CGuiHandler.cpp
|
||||
gui/CIntObject.cpp
|
||||
gui/ColorFilter.cpp
|
||||
@ -82,6 +82,7 @@ set(client_SRCS
|
||||
NetPacksClient.cpp
|
||||
NetPacksLobbyClient.cpp
|
||||
SDLRWwrapper.cpp
|
||||
ClientCommandManager.cpp
|
||||
)
|
||||
|
||||
set(client_HEADERS
|
||||
@ -104,7 +105,7 @@ set(client_HEADERS
|
||||
|
||||
gui/CAnimation.h
|
||||
gui/Canvas.h
|
||||
gui/CCursorHandler.h
|
||||
gui/CursorHandler.h
|
||||
gui/CGuiHandler.h
|
||||
gui/ColorFilter.h
|
||||
gui/CIntObject.h
|
||||
@ -168,6 +169,7 @@ set(client_HEADERS
|
||||
mapHandler.h
|
||||
resource.h
|
||||
SDLRWwrapper.h
|
||||
ClientCommandManager.h
|
||||
)
|
||||
|
||||
if(APPLE_IOS)
|
||||
@ -232,7 +234,7 @@ if(WIN32)
|
||||
add_custom_command(TARGET vcmiclient POST_BUILD
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:vcmiclient>"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy AI/fuzzylite.dll fuzzylite.dll
|
||||
COMMAND ${CMAKE_COMMAND} -E copy AI/tbb.dll tbb.dll
|
||||
COMMAND ${CMAKE_COMMAND} -E copy AI/tbb12.dll tbb12.dll
|
||||
)
|
||||
endif()
|
||||
elseif(APPLE_IOS)
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/Terrain.h"
|
||||
#include "../lib/TerrainHandler.h"
|
||||
|
||||
#define VCMI_SOUND_NAME(x)
|
||||
#define VCMI_SOUND_FILE(y) #y,
|
||||
@ -89,36 +89,6 @@ CSoundHandler::CSoundHandler():
|
||||
soundBase::battle02, soundBase::battle03, soundBase::battle04,
|
||||
soundBase::battle05, soundBase::battle06, soundBase::battle07
|
||||
};
|
||||
|
||||
//predefine terrain set
|
||||
//TODO: support custom sounds for new terrains and load from json
|
||||
horseSounds =
|
||||
{
|
||||
{Terrain::DIRT, soundBase::horseDirt},
|
||||
{Terrain::SAND, soundBase::horseSand},
|
||||
{Terrain::GRASS, soundBase::horseGrass},
|
||||
{Terrain::SNOW, soundBase::horseSnow},
|
||||
{Terrain::SWAMP, soundBase::horseSwamp},
|
||||
{Terrain::ROUGH, soundBase::horseRough},
|
||||
{Terrain::SUBTERRANEAN, soundBase::horseSubterranean},
|
||||
{Terrain::LAVA, soundBase::horseLava},
|
||||
{Terrain::WATER, soundBase::horseWater},
|
||||
{Terrain::ROCK, soundBase::horseRock}
|
||||
};
|
||||
}
|
||||
|
||||
void CSoundHandler::loadHorseSounds()
|
||||
{
|
||||
const auto & terrains = CGI->terrainTypeHandler->terrains();
|
||||
for(const auto & terrain : terrains)
|
||||
{
|
||||
//since all sounds are hardcoded, let's keep it
|
||||
if(vstd::contains(horseSounds, terrain.id))
|
||||
continue;
|
||||
|
||||
//Use already existing horse sound
|
||||
horseSounds[terrain.id] = horseSounds.at(terrains[terrain.id].horseSoundId);
|
||||
}
|
||||
}
|
||||
|
||||
void CSoundHandler::init()
|
||||
@ -368,9 +338,9 @@ CMusicHandler::CMusicHandler():
|
||||
|
||||
void CMusicHandler::loadTerrainMusicThemes()
|
||||
{
|
||||
for (const auto & terrain : CGI->terrainTypeHandler->terrains())
|
||||
for (const auto & terrain : CGI->terrainTypeHandler->objects)
|
||||
{
|
||||
addEntryToSet("terrain_" + terrain.name, "Music/" + terrain.musicFilename);
|
||||
addEntryToSet("terrain_" + terrain->getJsonKey(), "Music/" + terrain->musicFilename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -542,6 +512,20 @@ MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string mu
|
||||
}
|
||||
MusicEntry::~MusicEntry()
|
||||
{
|
||||
if (playing)
|
||||
{
|
||||
assert(0);
|
||||
logGlobal->error("Attempt to delete music while playing!");
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
if (loop == 0 && Mix_FadingMusic() != MIX_NO_FADING)
|
||||
{
|
||||
assert(0);
|
||||
logGlobal->error("Attempt to delete music while fading out!");
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
logGlobal->trace("Del-ing music file %s", currentName);
|
||||
if (music)
|
||||
Mix_FreeMusic(music);
|
||||
@ -619,7 +603,7 @@ bool MusicEntry::play()
|
||||
|
||||
bool MusicEntry::stop(int fade_ms)
|
||||
{
|
||||
if (playing)
|
||||
if (Mix_PlayingMusic())
|
||||
{
|
||||
playing = false;
|
||||
loop = 0;
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CSoundBase.h"
|
||||
#include "../lib/Terrain.h"
|
||||
|
||||
struct _Mix_Music;
|
||||
struct SDL_RWops;
|
||||
@ -61,7 +60,6 @@ public:
|
||||
CSoundHandler();
|
||||
|
||||
void init() override;
|
||||
void loadHorseSounds();
|
||||
void release() override;
|
||||
|
||||
void setVolume(ui32 percent) override;
|
||||
@ -84,7 +82,6 @@ public:
|
||||
// Sets
|
||||
std::vector<soundBase::soundID> pickupSounds;
|
||||
std::vector<soundBase::soundID> battleIntroSounds;
|
||||
std::map<TerrainId, soundBase::soundID> horseSounds;
|
||||
};
|
||||
|
||||
// Helper //now it looks somewhat useless
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "battle/BattleWindow.h"
|
||||
#include "../CCallback.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "gui/CCursorHandler.h"
|
||||
#include "gui/CursorHandler.h"
|
||||
#include "windows/CKingdomInterface.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "windows/CHeroWindow.h"
|
||||
@ -61,6 +61,8 @@
|
||||
#include "windows/InfoWindows.h"
|
||||
#include "../lib/UnlockGuard.h"
|
||||
#include "../lib/CPathfinder.h"
|
||||
#include "../lib/RoadHandler.h"
|
||||
#include "../lib/TerrainHandler.h"
|
||||
#include <SDL.h>
|
||||
#include "CServerHandler.h"
|
||||
// FIXME: only needed for CGameState::mutex
|
||||
@ -156,7 +158,6 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
|
||||
cb = CB;
|
||||
env = ENV;
|
||||
|
||||
CCS->soundh->loadHorseSounds();
|
||||
CCS->musich->loadTerrainMusicThemes();
|
||||
|
||||
initializeHeroTownList();
|
||||
@ -260,7 +261,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
|
||||
{
|
||||
updateAmbientSounds();
|
||||
//We may need to change music - select new track, music handler will change it if needed
|
||||
CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->name, true, false);
|
||||
CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->getJsonKey(), true, false);
|
||||
|
||||
if(details.result == TryMoveHero::TELEPORTATION)
|
||||
{
|
||||
@ -436,7 +437,7 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
|
||||
adventureInt->select(newSelection, true);
|
||||
else if (adventureInt->selection == hero)
|
||||
adventureInt->selection = nullptr;
|
||||
|
||||
|
||||
if (vstd::contains(paths, hero))
|
||||
paths.erase(hero);
|
||||
}
|
||||
@ -1623,7 +1624,7 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
|
||||
else
|
||||
for (directory_iterator dir(gamesDir); dir != enddir; ++dir)
|
||||
{
|
||||
if (is_regular(dir->status()))
|
||||
if (is_regular_file(dir->status()))
|
||||
{
|
||||
std::string name = dir->path().filename().string();
|
||||
if (starts_with(name, namePrefix) && ends_with(name, ".vcgm1"))
|
||||
@ -2372,8 +2373,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
for (auto & elem : path.nodes)
|
||||
elem.coord = h->convertFromVisitablePos(elem.coord);
|
||||
|
||||
TerrainId currentTerrain = Terrain::BORDER; // not init yet
|
||||
TerrainId currentTerrain = ETerrainId::NONE;
|
||||
TerrainId newTerrain;
|
||||
bool wasOnRoad = true;
|
||||
int sh = -1;
|
||||
|
||||
auto canStop = [&](CGPathNode * node) -> bool
|
||||
@ -2389,13 +2391,18 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
|
||||
for (i=(int)path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--)
|
||||
{
|
||||
int3 currentCoord = path.nodes[i].coord;
|
||||
int3 prevCoord = path.nodes[i].coord;
|
||||
int3 nextCoord = path.nodes[i-1].coord;
|
||||
|
||||
auto currentObject = getObj(currentCoord, currentCoord == h->pos);
|
||||
auto prevRoad = cb->getTile(h->convertToVisitablePos(prevCoord))->roadType;
|
||||
auto nextRoad = cb->getTile(h->convertToVisitablePos(nextCoord))->roadType;
|
||||
|
||||
bool movingOnRoad = prevRoad->getId() != Road::NO_ROAD && nextRoad->getId() != Road::NO_ROAD;
|
||||
|
||||
auto prevObject = getObj(prevCoord, prevCoord == h->pos);
|
||||
auto nextObjectTop = getObj(nextCoord, false);
|
||||
auto nextObject = getObj(nextCoord, true);
|
||||
auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
|
||||
auto destTeleportObj = getDestTeleportObj(prevObject, nextObjectTop, nextObject);
|
||||
if (isTeleportAction(path.nodes[i-1].action) && destTeleportObj != nullptr)
|
||||
{
|
||||
CCS->soundh->stopSound(sh);
|
||||
@ -2410,7 +2417,10 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
}
|
||||
if(i != path.nodes.size() - 1)
|
||||
{
|
||||
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
|
||||
if (movingOnRoad)
|
||||
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSound, -1);
|
||||
else
|
||||
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSoundPenalty, -1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -2428,12 +2438,16 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
sh = CCS->soundh->playSound(soundBase::horseFlying, -1);
|
||||
#endif
|
||||
{
|
||||
newTerrain = cb->getTile(h->convertToVisitablePos(currentCoord))->terType->id;
|
||||
if(newTerrain != currentTerrain)
|
||||
newTerrain = cb->getTile(h->convertToVisitablePos(prevCoord))->terType->getId();
|
||||
if(newTerrain != currentTerrain || wasOnRoad != movingOnRoad)
|
||||
{
|
||||
CCS->soundh->stopSound(sh);
|
||||
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
|
||||
if (movingOnRoad)
|
||||
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSound, -1);
|
||||
else
|
||||
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSoundPenalty, -1);
|
||||
currentTerrain = newTerrain;
|
||||
wasOnRoad = movingOnRoad;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2473,6 +2487,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
// (i == 0) means hero went through all the path
|
||||
adventureInt->updateMoveHero(h, (i != 0));
|
||||
adventureInt->updateNextHero(h);
|
||||
|
||||
// ugly workaround to force instant update of adventure map
|
||||
adventureInt->animValHitCount = 8;
|
||||
}
|
||||
|
||||
setMovementStatus(false);
|
||||
|
@ -46,9 +46,9 @@
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/registerTypes/RegisterTypes.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "CMT.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "../lib/ScriptHandler.h"
|
||||
#include "windows/CAdvmapInterface.h"
|
||||
#include <vcmi/events/EventBus.h>
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
@ -761,8 +761,29 @@ void CClient::reinitScripting()
|
||||
#endif
|
||||
}
|
||||
|
||||
void CClient::removeGUI()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
adventureInt.reset();
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar.reset();
|
||||
logGlobal->info("Removed GUI.");
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_clientSetupJNI(JNIEnv * env, jobject cls)
|
||||
{
|
||||
logNetwork->info("Received clientSetupJNI");
|
||||
|
||||
CAndroidVMHelper::cacheVM(env);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jobject cls)
|
||||
{
|
||||
logNetwork->info("Received server closed signal");
|
||||
|
@ -240,6 +240,7 @@ public:
|
||||
|
||||
void showInfoDialog(InfoWindow * iw) override {};
|
||||
void showInfoDialog(const std::string & msg, PlayerColor player) override {};
|
||||
void removeGUI();
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
scripting::Pool * getGlobalContextPool() const override;
|
||||
|
494
client/ClientCommandManager.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
/*
|
||||
* ClientCommandManager.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
#include "Client.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "gui/CAnimation.h"
|
||||
#include "windows/CAdvmapInterface.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
|
||||
#ifdef SCRIPTING_ENABLED
|
||||
#include "../lib/ScriptHandler.h"
|
||||
#endif
|
||||
|
||||
void ClientCommandManager::handleGoSolo()
|
||||
{
|
||||
Settings session = settings.write["session"];
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
printCommandMessage("Game is not in playing state");
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(session["aiSolo"].Bool())
|
||||
{
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = LOCPLINT->playerID;
|
||||
CSH->client->removeGUI();
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
{
|
||||
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
|
||||
printCommandMessage("Player " + elem.first.getStr() + " will be lead by " + AiToGive, ELogLevel::INFO);
|
||||
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
}
|
||||
}
|
||||
GH.totalRedraw();
|
||||
giveTurn(color);
|
||||
}
|
||||
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleControlAi(const std::string &colorName)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
printCommandMessage("Game is not in playing state");
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(LOCPLINT)
|
||||
color = LOCPLINT->playerID;
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human || (colorName.length() &&
|
||||
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CSH->client->removeGUI();
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
GH.totalRedraw();
|
||||
if(color != PlayerColor::NEUTRAL)
|
||||
giveTurn(color);
|
||||
}
|
||||
|
||||
void ClientCommandManager::processCommand(const std::string &message, bool calledFromIngameConsole)
|
||||
{
|
||||
std::istringstream singleWordBuffer;
|
||||
singleWordBuffer.str(message);
|
||||
std::string commandName;
|
||||
singleWordBuffer >> commandName;
|
||||
currentCallFromIngameConsole = calledFromIngameConsole;
|
||||
|
||||
if(message==std::string("die, fool"))
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if(commandName == std::string("activate"))
|
||||
{
|
||||
int what;
|
||||
singleWordBuffer >> what;
|
||||
switch (what)
|
||||
{
|
||||
case 0:
|
||||
GH.topInt()->activate();
|
||||
break;
|
||||
case 1:
|
||||
adventureInt->activate();
|
||||
break;
|
||||
case 2:
|
||||
LOCPLINT->castleInt->activate();
|
||||
break;
|
||||
default:
|
||||
printCommandMessage("Wrong argument specified!", ELogLevel::ERROR);
|
||||
}
|
||||
}
|
||||
else if(commandName == "redraw")
|
||||
{
|
||||
GH.totalRedraw();
|
||||
}
|
||||
else if(commandName == "screen")
|
||||
{
|
||||
printCommandMessage("Screenbuf points to ");
|
||||
|
||||
if(screenBuf == screen)
|
||||
printCommandMessage("screen", ELogLevel::ERROR);
|
||||
else if(screenBuf == screen2)
|
||||
printCommandMessage("screen2", ELogLevel::ERROR);
|
||||
else
|
||||
printCommandMessage("?!?", ELogLevel::ERROR);
|
||||
|
||||
SDL_SaveBMP(screen, "Screen_c.bmp");
|
||||
SDL_SaveBMP(screen2, "Screen2_c.bmp");
|
||||
}
|
||||
else if(commandName == "save")
|
||||
{
|
||||
if(!CSH->client)
|
||||
{
|
||||
printCommandMessage("Game is not in playing state");
|
||||
return;
|
||||
}
|
||||
std::string fname;
|
||||
singleWordBuffer >> fname;
|
||||
CSH->client->save(fname);
|
||||
}
|
||||
// else if(commandName=="load")
|
||||
// {
|
||||
// // TODO: this code should end the running game and manage to call startGame instead
|
||||
// std::string fname;
|
||||
// singleWordBuffer >> fname;
|
||||
// CSH->client->loadGame(fname);
|
||||
// }
|
||||
else if(message=="convert txt")
|
||||
{
|
||||
VLC->generaltexth->dumpAllTexts();
|
||||
}
|
||||
else if(message=="get config")
|
||||
{
|
||||
printCommandMessage("Command accepted.\t");
|
||||
|
||||
const boost::filesystem::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "configuration";
|
||||
|
||||
boost::filesystem::create_directories(outPath);
|
||||
|
||||
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
|
||||
|
||||
for(auto contentName : contentNames)
|
||||
{
|
||||
auto & content = (*VLC->modh->content)[contentName];
|
||||
|
||||
auto contentOutPath = outPath / contentName;
|
||||
boost::filesystem::create_directories(contentOutPath);
|
||||
|
||||
for(auto & iter : content.modData)
|
||||
{
|
||||
const JsonNode & modData = iter.second.modData;
|
||||
|
||||
for(auto & nameAndObject : modData.Struct())
|
||||
{
|
||||
const JsonNode & object = nameAndObject.second;
|
||||
|
||||
std::string name = CModHandler::normalizeIdentifier(object.meta, CModHandler::scopeBuiltin(), nameAndObject.first);
|
||||
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const boost::filesystem::path filePath = contentOutPath / (name + ".json");
|
||||
boost::filesystem::ofstream file(filePath);
|
||||
file << object.toJson();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printCommandMessage("\rExtracting done :)\n");
|
||||
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
|
||||
}
|
||||
#if SCRIPTING_ENABLED
|
||||
else if(message=="get scripts")
|
||||
{
|
||||
printCommandMessage("Command accepted.\t");
|
||||
|
||||
const boost::filesystem::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "scripts";
|
||||
|
||||
boost::filesystem::create_directories(outPath);
|
||||
|
||||
for(auto & kv : VLC->scriptHandler->objects)
|
||||
{
|
||||
std::string name = kv.first;
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const scripting::ScriptImpl * script = kv.second.get();
|
||||
boost::filesystem::path filePath = outPath / (name + ".lua");
|
||||
boost::filesystem::ofstream file(filePath);
|
||||
file << script->getSource();
|
||||
}
|
||||
printCommandMessage("\rExtracting done :)\n");
|
||||
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
|
||||
}
|
||||
#endif
|
||||
else if(message=="get txt")
|
||||
{
|
||||
printCommandMessage("Command accepted.\t");
|
||||
|
||||
const boost::filesystem::path outPath =
|
||||
VCMIDirs::get().userExtractedPath();
|
||||
|
||||
auto list =
|
||||
CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
|
||||
});
|
||||
|
||||
for (auto & filename : list)
|
||||
{
|
||||
const boost::filesystem::path filePath = outPath / (filename.getName() + ".TXT");
|
||||
|
||||
boost::filesystem::create_directories(filePath.parent_path());
|
||||
|
||||
boost::filesystem::ofstream file(filePath);
|
||||
auto text = CResourceHandler::get()->load(filename)->readAll();
|
||||
|
||||
file.write((char*)text.first.get(), text.second);
|
||||
}
|
||||
|
||||
printCommandMessage("\rExtracting done :)\n");
|
||||
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
|
||||
}
|
||||
else if(commandName == "crash")
|
||||
{
|
||||
int *ptr = nullptr;
|
||||
*ptr = 666;
|
||||
//disaster!
|
||||
}
|
||||
else if(commandName == "mp" && adventureInt)
|
||||
{
|
||||
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
||||
printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
|
||||
}
|
||||
else if(commandName == "bonuses")
|
||||
{
|
||||
bool jsonFormat = (message == "bonuses json");
|
||||
auto format = [jsonFormat](const BonusList & b) -> std::string
|
||||
{
|
||||
if(jsonFormat)
|
||||
return b.toJsonNode().toJson(true);
|
||||
std::ostringstream ss;
|
||||
ss << b;
|
||||
return ss.str();
|
||||
};
|
||||
printCommandMessage("Bonuses of " + adventureInt->selection->getObjectName() + "\n");
|
||||
printCommandMessage(format(adventureInt->selection->getBonusList()) + "\n");
|
||||
|
||||
printCommandMessage("\nInherited bonuses:\n");
|
||||
TCNodes parents;
|
||||
adventureInt->selection->getParents(parents);
|
||||
for(const CBonusSystemNode *parent : parents)
|
||||
{
|
||||
printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||
}
|
||||
}
|
||||
else if(commandName == "not dialog")
|
||||
{
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
}
|
||||
else if(commandName == "gui")
|
||||
{
|
||||
for(auto & child : GH.listInt)
|
||||
{
|
||||
const auto childPtr = child.get();
|
||||
if(const CIntObject * obj = dynamic_cast<const CIntObject *>(childPtr))
|
||||
printInfoAboutInterfaceObject(obj, 0);
|
||||
else
|
||||
printCommandMessage(std::string(typeid(childPtr).name()) + "\n");
|
||||
}
|
||||
}
|
||||
else if(commandName == "tell")
|
||||
{
|
||||
std::string what;
|
||||
int id1, id2;
|
||||
singleWordBuffer >> what >> id1 >> id2;
|
||||
if(what == "hs")
|
||||
{
|
||||
for(const CGHeroInstance *h : LOCPLINT->cb->getHeroesInfo())
|
||||
if(h->type->ID.getNum() == id1)
|
||||
if(const CArtifactInstance *a = h->getArt(ArtifactPosition(id2)))
|
||||
printCommandMessage(a->nodeName());
|
||||
}
|
||||
}
|
||||
else if (commandName == "set")
|
||||
{
|
||||
std::string what, value;
|
||||
singleWordBuffer >> what;
|
||||
|
||||
Settings config = settings.write["session"][what];
|
||||
|
||||
singleWordBuffer >> value;
|
||||
|
||||
if (value == "on")
|
||||
{
|
||||
config->Bool() = true;
|
||||
printCommandMessage("Option " + what + " enabled!", ELogLevel::INFO);
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
config->Bool() = false;
|
||||
printCommandMessage("Option " + what + " disabled!", ELogLevel::INFO);
|
||||
}
|
||||
}
|
||||
else if(commandName == "unlock")
|
||||
{
|
||||
std::string mxname;
|
||||
singleWordBuffer >> mxname;
|
||||
if(mxname == "pim" && LOCPLINT)
|
||||
LOCPLINT->pim->unlock();
|
||||
}
|
||||
else if(commandName == "def2bmp")
|
||||
{
|
||||
std::string URI;
|
||||
singleWordBuffer >> URI;
|
||||
std::unique_ptr<CAnimation> anim = std::make_unique<CAnimation>(URI);
|
||||
anim->preload();
|
||||
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||
}
|
||||
else if(commandName == "extract")
|
||||
{
|
||||
std::string URI;
|
||||
singleWordBuffer >> URI;
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||
{
|
||||
const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / URI;
|
||||
|
||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||
|
||||
boost::filesystem::create_directories(outPath.parent_path());
|
||||
boost::filesystem::ofstream outFile(outPath, boost::filesystem::ofstream::binary);
|
||||
outFile.write((char*)data.first.get(), data.second);
|
||||
}
|
||||
else
|
||||
printCommandMessage("File not found!", ELogLevel::ERROR);
|
||||
}
|
||||
else if(commandName == "setBattleAI")
|
||||
{
|
||||
std::string fname;
|
||||
singleWordBuffer >> fname;
|
||||
printCommandMessage("Will try loading that AI to see if it is correct name...\n");
|
||||
try
|
||||
{
|
||||
if(auto ai = CDynLibHandler::getNewBattleAI(fname)) //test that given AI is indeed available... heavy but it is easy to make a typo and break the game
|
||||
{
|
||||
Settings neutralAI = settings.write["server"]["neutralAI"];
|
||||
neutralAI->String() = fname;
|
||||
printCommandMessage("Setting changed, from now the battle ai will be " + fname + "!\n");
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
printCommandMessage("Failed opening " + fname + ": " + e.what(), ELogLevel::WARN);
|
||||
printCommandMessage("Setting not changed, AI not found or invalid!", ELogLevel::WARN);
|
||||
}
|
||||
}
|
||||
else if(commandName == "autoskip")
|
||||
{
|
||||
Settings session = settings.write["session"];
|
||||
session["autoSkip"].Bool() = !session["autoSkip"].Bool();
|
||||
}
|
||||
else if(commandName == "gosolo")
|
||||
{
|
||||
ClientCommandManager::handleGoSolo();
|
||||
}
|
||||
else if(commandName == "controlai")
|
||||
{
|
||||
std::string colorName;
|
||||
singleWordBuffer >> colorName;
|
||||
boost::to_lower(colorName);
|
||||
|
||||
ClientCommandManager::handleControlAi(colorName);
|
||||
}
|
||||
else
|
||||
{
|
||||
printCommandMessage("Command not found :(", ELogLevel::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
|
||||
{
|
||||
YourTurn yt;
|
||||
yt.player = colorIdentifier;
|
||||
yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
|
||||
yt.applyCl(CSH->client);
|
||||
}
|
||||
|
||||
void ClientCommandManager::printInfoAboutInterfaceObject(const CIntObject *obj, int level)
|
||||
{
|
||||
std::stringstream sbuffer;
|
||||
sbuffer << std::string(level, '\t');
|
||||
|
||||
sbuffer << typeid(*obj).name() << " *** ";
|
||||
if (obj->active)
|
||||
{
|
||||
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
|
||||
PRINT(LCLICK, 'L');
|
||||
PRINT(RCLICK, 'R');
|
||||
PRINT(HOVER, 'H');
|
||||
PRINT(MOVE, 'M');
|
||||
PRINT(KEYBOARD, 'K');
|
||||
PRINT(TIME, 'T');
|
||||
PRINT(GENERAL, 'A');
|
||||
PRINT(WHEEL, 'W');
|
||||
PRINT(DOUBLECLICK, 'D');
|
||||
#undef PRINT
|
||||
}
|
||||
else
|
||||
sbuffer << "inactive";
|
||||
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
|
||||
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
|
||||
printCommandMessage(sbuffer.str(), ELogLevel::INFO);
|
||||
|
||||
for(const CIntObject *child : obj->children)
|
||||
printInfoAboutInterfaceObject(child, level+1);
|
||||
}
|
||||
|
||||
void ClientCommandManager::printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType)
|
||||
{
|
||||
switch(messageType)
|
||||
{
|
||||
case ELogLevel::NOT_SET:
|
||||
std::cout << commandMessage;
|
||||
break;
|
||||
case ELogLevel::TRACE:
|
||||
logGlobal->trace(commandMessage);
|
||||
break;
|
||||
case ELogLevel::DEBUG:
|
||||
logGlobal->debug(commandMessage);
|
||||
break;
|
||||
case ELogLevel::INFO:
|
||||
logGlobal->info(commandMessage);
|
||||
break;
|
||||
case ELogLevel::WARN:
|
||||
logGlobal->warn(commandMessage);
|
||||
break;
|
||||
case ELogLevel::ERROR:
|
||||
logGlobal->error(commandMessage);
|
||||
break;
|
||||
default:
|
||||
std::cout << commandMessage;
|
||||
break;
|
||||
}
|
||||
|
||||
if(currentCallFromIngameConsole)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(LOCPLINT && LOCPLINT->cingconsole)
|
||||
{
|
||||
LOCPLINT->cingconsole->print(commandMessage);
|
||||
}
|
||||
}
|
||||
}
|
31
client/ClientCommandManager.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ClientCommandManager.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class PlayerColor;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
class CIntObject;
|
||||
|
||||
class ClientCommandManager //take mantis #2292 issue about account if thinking about handling cheats from command-line
|
||||
{
|
||||
bool currentCallFromIngameConsole;
|
||||
|
||||
void giveTurn(const PlayerColor &color);
|
||||
void printInfoAboutInterfaceObject(const CIntObject *obj, int level);
|
||||
void printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType = ELogLevel::NOT_SET);
|
||||
void handleGoSolo();
|
||||
void handleControlAi(const std::string &colorName);
|
||||
|
||||
public:
|
||||
ClientCommandManager() = default;
|
||||
void processCommand(const std::string &message, bool calledFromIngameConsole);
|
||||
};
|
@ -63,7 +63,7 @@ void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHa
|
||||
|
||||
void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
|
||||
{
|
||||
if(lobby)
|
||||
if(lobby && lobby->card)
|
||||
{
|
||||
lobby->card->chat->addNewMessage(playerName + ": " + message);
|
||||
lobby->card->setChat(true);
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../windows/CCreatureWindow.h"
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/Canvas.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/BattleFieldHandler.h"
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/Canvas.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../windows/CAdvmapInterface.h"
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/NetPacks.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
|
||||
CondSh<BattleAction *> BattleInterface::givenCommand(nullptr);
|
||||
|
||||
@ -136,8 +137,8 @@ BattleInterface::~BattleInterface()
|
||||
if (adventureInt && adventureInt->selection)
|
||||
{
|
||||
//FIXME: this should be moved to adventureInt which should restore correct track based on selection/active player
|
||||
const auto & terrain = *(LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType);
|
||||
CCS->musich->playMusicFromSet("terrain", terrain.name, true, false);
|
||||
const auto * terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType;
|
||||
CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
|
||||
}
|
||||
|
||||
// may happen if user decided to close game while in battle
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "../Graphics.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/Canvas.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../widgets/AdventureMapClasses.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
|
@ -83,10 +83,10 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
|
||||
amountNegative = IImage::createFromFile("CMNUMWIN.BMP");
|
||||
amountEffNeutral = IImage::createFromFile("CMNUMWIN.BMP");
|
||||
|
||||
static const auto shifterNormal = ColorFilter::genRangeShifter( 0,0,0, 0.6, 0.2, 1.0 );
|
||||
static const auto shifterPositive = ColorFilter::genRangeShifter( 0,0,0, 0.2, 1.0, 0.2 );
|
||||
static const auto shifterNegative = ColorFilter::genRangeShifter( 0,0,0, 1.0, 0.2, 0.2 );
|
||||
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0,0,0, 1.0, 1.0, 0.2 );
|
||||
static const auto shifterNormal = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f );
|
||||
static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f );
|
||||
static const auto shifterNegative = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 0.2f, 0.2f );
|
||||
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 1.0f, 0.2f );
|
||||
|
||||
amountNormal->adjustPalette(shifterNormal);
|
||||
amountPositive->adjustPalette(shifterPositive);
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../gui/Canvas.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../windows/CSpellWindow.h"
|
||||
|
@ -94,8 +94,8 @@ public:
|
||||
// Keep the original palette, in order to do color switching operation
|
||||
void savePalette();
|
||||
|
||||
void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr, ui8 alpha=255) const override;
|
||||
void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha=255) const override;
|
||||
void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr) const override;
|
||||
void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src) const override;
|
||||
std::shared_ptr<IImage> scaleFast(float scale) const override;
|
||||
void exportBitmap(const boost::filesystem::path & path) const override;
|
||||
void playerColored(PlayerColor player) override;
|
||||
@ -642,17 +642,16 @@ SDLImage::SDLImage(std::string filename)
|
||||
}
|
||||
}
|
||||
|
||||
void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src, ui8 alpha) const
|
||||
void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src) const
|
||||
{
|
||||
if(!surf)
|
||||
return;
|
||||
|
||||
Rect destRect(posX, posY, surf->w, surf->h);
|
||||
|
||||
draw(where, &destRect, src);
|
||||
}
|
||||
|
||||
void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src, ui8 alpha) const
|
||||
void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src) const
|
||||
{
|
||||
if (!surf)
|
||||
return;
|
||||
|
@ -40,8 +40,8 @@ public:
|
||||
using SpecialPalette = std::array<SDL_Color, 7>;
|
||||
|
||||
//draws image on surface "where" at position
|
||||
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr, ui8 alpha = 255) const=0;
|
||||
virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha = 255) const = 0;
|
||||
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0;
|
||||
virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src) const = 0;
|
||||
|
||||
virtual std::shared_ptr<IImage> scaleFast(float scale) const = 0;
|
||||
|
||||
|
@ -1,317 +0,0 @@
|
||||
/*
|
||||
* CCursorHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CCursorHandler.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "SDL_Extensions.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "../widgets/Images.h"
|
||||
|
||||
#include "../CMT.h"
|
||||
|
||||
void CCursorHandler::clearBuffer()
|
||||
{
|
||||
Uint32 fillColor = SDL_MapRGBA(buffer->format, 0, 0, 0, 0);
|
||||
CSDL_Ext::fillRect(buffer, nullptr, fillColor);
|
||||
}
|
||||
|
||||
void CCursorHandler::updateBuffer(CIntObject * payload)
|
||||
{
|
||||
payload->moveTo(Point(0,0));
|
||||
payload->showAll(buffer);
|
||||
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
void CCursorHandler::replaceBuffer(CIntObject * payload)
|
||||
{
|
||||
clearBuffer();
|
||||
updateBuffer(payload);
|
||||
}
|
||||
|
||||
CCursorHandler::CCursorHandler()
|
||||
: needUpdate(true)
|
||||
, buffer(nullptr)
|
||||
, cursorLayer(nullptr)
|
||||
, frameTime(0.f)
|
||||
, showing(false)
|
||||
{
|
||||
cursorLayer = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 40, 40);
|
||||
SDL_SetTextureBlendMode(cursorLayer, SDL_BLENDMODE_BLEND);
|
||||
|
||||
xpos = ypos = 0;
|
||||
type = Cursor::Type::DEFAULT;
|
||||
dndObject = nullptr;
|
||||
|
||||
cursors =
|
||||
{
|
||||
std::make_unique<CAnimImage>("CRADVNTR", 0),
|
||||
std::make_unique<CAnimImage>("CRCOMBAT", 0),
|
||||
std::make_unique<CAnimImage>("CRDEFLT", 0),
|
||||
std::make_unique<CAnimImage>("CRSPELL", 0)
|
||||
};
|
||||
|
||||
currentCursor = cursors.at(static_cast<size_t>(Cursor::Type::DEFAULT)).get();
|
||||
|
||||
buffer = CSDL_Ext::newSurface(40,40);
|
||||
|
||||
SDL_SetSurfaceBlendMode(buffer, SDL_BLENDMODE_NONE);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
Point CCursorHandler::position() const
|
||||
{
|
||||
return Point(xpos, ypos);
|
||||
}
|
||||
|
||||
void CCursorHandler::changeGraphic(Cursor::Type type, size_t index)
|
||||
{
|
||||
assert(dndObject == nullptr);
|
||||
|
||||
if(type != this->type)
|
||||
{
|
||||
this->type = type;
|
||||
this->frame = index;
|
||||
currentCursor = cursors.at(static_cast<size_t>(type)).get();
|
||||
currentCursor->setFrame(index);
|
||||
}
|
||||
else if(index != this->frame)
|
||||
{
|
||||
this->frame = index;
|
||||
currentCursor->setFrame(index);
|
||||
}
|
||||
|
||||
replaceBuffer(currentCursor);
|
||||
}
|
||||
|
||||
void CCursorHandler::set(Cursor::Default index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CCursorHandler::set(Cursor::Map index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CCursorHandler::set(Cursor::Combat index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CCursorHandler::set(Cursor::Spellcast index)
|
||||
{
|
||||
//Note: this is animated cursor, ignore specified frame and only change type
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, frame);
|
||||
}
|
||||
|
||||
void CCursorHandler::dragAndDropCursor(std::unique_ptr<CAnimImage> object)
|
||||
{
|
||||
dndObject = std::move(object);
|
||||
if(dndObject)
|
||||
replaceBuffer(dndObject.get());
|
||||
else
|
||||
replaceBuffer(currentCursor);
|
||||
}
|
||||
|
||||
void CCursorHandler::cursorMove(const int & x, const int & y)
|
||||
{
|
||||
xpos = x;
|
||||
ypos = y;
|
||||
}
|
||||
|
||||
void CCursorHandler::shiftPos( int &x, int &y )
|
||||
{
|
||||
if(( type == Cursor::Type::COMBAT && frame != static_cast<size_t>(Cursor::Combat::POINTER)) || type == Cursor::Type::SPELLBOOK)
|
||||
{
|
||||
x-=16;
|
||||
y-=16;
|
||||
|
||||
// Properly align the melee attack cursors.
|
||||
if (type == Cursor::Type::COMBAT)
|
||||
{
|
||||
switch (static_cast<Cursor::Combat>(frame))
|
||||
{
|
||||
case Cursor::Combat::HIT_NORTHEAST:
|
||||
x -= 6;
|
||||
y += 16;
|
||||
break;
|
||||
case Cursor::Combat::HIT_EAST:
|
||||
x -= 16;
|
||||
y += 10;
|
||||
break;
|
||||
case Cursor::Combat::HIT_SOUTHEAST:
|
||||
x -= 6;
|
||||
y -= 6;
|
||||
break;
|
||||
case Cursor::Combat::HIT_SOUTHWEST:
|
||||
x += 16;
|
||||
y -= 6;
|
||||
break;
|
||||
case Cursor::Combat::HIT_WEST:
|
||||
x += 16;
|
||||
y += 11;
|
||||
break;
|
||||
case Cursor::Combat::HIT_NORTHWEST:
|
||||
x += 16;
|
||||
y += 16;
|
||||
break;
|
||||
case Cursor::Combat::HIT_NORTH:
|
||||
x += 9;
|
||||
y += 16;
|
||||
break;
|
||||
case Cursor::Combat::HIT_SOUTH:
|
||||
x += 9;
|
||||
y -= 15;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(type == Cursor::Type::ADVENTURE)
|
||||
{
|
||||
if (frame == 0)
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
else if(frame == 2)
|
||||
{
|
||||
x -= 12;
|
||||
y -= 10;
|
||||
}
|
||||
else if(frame == 3)
|
||||
{
|
||||
x -= 12;
|
||||
y -= 12;
|
||||
}
|
||||
else if(frame < 27)
|
||||
{
|
||||
int hlpNum = (frame - 4)%6;
|
||||
if(hlpNum == 0)
|
||||
{
|
||||
x -= 15;
|
||||
y -= 13;
|
||||
}
|
||||
else if(hlpNum == 1)
|
||||
{
|
||||
x -= 13;
|
||||
y -= 13;
|
||||
}
|
||||
else if(hlpNum == 2)
|
||||
{
|
||||
x -= 20;
|
||||
y -= 20;
|
||||
}
|
||||
else if(hlpNum == 3)
|
||||
{
|
||||
x -= 13;
|
||||
y -= 16;
|
||||
}
|
||||
else if(hlpNum == 4)
|
||||
{
|
||||
x -= 8;
|
||||
y -= 9;
|
||||
}
|
||||
else if(hlpNum == 5)
|
||||
{
|
||||
x -= 14;
|
||||
y -= 16;
|
||||
}
|
||||
}
|
||||
else if(frame == 41)
|
||||
{
|
||||
x -= 14;
|
||||
y -= 16;
|
||||
}
|
||||
else if(frame < 31 || frame == 42)
|
||||
{
|
||||
x -= 20;
|
||||
y -= 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCursorHandler::centerCursor()
|
||||
{
|
||||
this->xpos = static_cast<int>((screen->w / 2.) - (currentCursor->pos.w / 2.));
|
||||
this->ypos = static_cast<int>((screen->h / 2.) - (currentCursor->pos.h / 2.));
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
|
||||
SDL_WarpMouse(this->xpos, this->ypos);
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
|
||||
}
|
||||
|
||||
void CCursorHandler::render()
|
||||
{
|
||||
if(!showing)
|
||||
return;
|
||||
|
||||
if (type == Cursor::Type::SPELLBOOK)
|
||||
{
|
||||
static const float frameDisplayDuration = 0.1f;
|
||||
|
||||
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
size_t newFrame = frame;
|
||||
|
||||
while (frameTime > frameDisplayDuration)
|
||||
{
|
||||
frameTime -= frameDisplayDuration;
|
||||
newFrame++;
|
||||
}
|
||||
|
||||
auto & animation = cursors.at(static_cast<size_t>(type));
|
||||
|
||||
while (newFrame > animation->size())
|
||||
newFrame -= animation->size();
|
||||
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
|
||||
}
|
||||
|
||||
//the must update texture in the main (renderer) thread, but changes to cursor type may come from other threads
|
||||
updateTexture();
|
||||
|
||||
int x = xpos;
|
||||
int y = ypos;
|
||||
shiftPos(x, y);
|
||||
|
||||
if(dndObject)
|
||||
{
|
||||
x -= dndObject->pos.w/2;
|
||||
y -= dndObject->pos.h/2;
|
||||
}
|
||||
|
||||
SDL_Rect destRect;
|
||||
destRect.x = x;
|
||||
destRect.y = y;
|
||||
destRect.w = 40;
|
||||
destRect.h = 40;
|
||||
|
||||
SDL_RenderCopy(mainRenderer, cursorLayer, nullptr, &destRect);
|
||||
}
|
||||
|
||||
void CCursorHandler::updateTexture()
|
||||
{
|
||||
if(needUpdate)
|
||||
{
|
||||
SDL_UpdateTexture(cursorLayer, nullptr, buffer->pixels, buffer->pitch);
|
||||
needUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
CCursorHandler::~CCursorHandler()
|
||||
{
|
||||
if(buffer)
|
||||
SDL_FreeSurface(buffer);
|
||||
|
||||
if(cursorLayer)
|
||||
SDL_DestroyTexture(cursorLayer);
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
#include <SDL.h>
|
||||
|
||||
#include "CIntObject.h"
|
||||
#include "CCursorHandler.h"
|
||||
#include "CursorHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../../lib/CThreadHelper.h"
|
||||
|
@ -70,13 +70,6 @@ void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect &
|
||||
image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect);
|
||||
}
|
||||
|
||||
void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect, uint8_t alpha)
|
||||
{
|
||||
assert(image);
|
||||
if (image)
|
||||
image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect, alpha);
|
||||
}
|
||||
|
||||
void Canvas::draw(Canvas & image, const Point & pos)
|
||||
{
|
||||
blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface);
|
||||
|
@ -51,10 +51,6 @@ public:
|
||||
/// renders section of image bounded by sourceRect at specified position
|
||||
void draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect);
|
||||
|
||||
/// renders section of image bounded by sourceRect at specified position at specific transparency value
|
||||
void draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect, uint8_t alpha);
|
||||
|
||||
|
||||
/// renders another canvas onto this canvas
|
||||
void draw(Canvas & image, const Point & pos);
|
||||
|
||||
|
402
client/gui/CursorHandler.cpp
Normal file
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* CCursorHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CursorHandler.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "SDL_Extensions.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "CAnimation.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
//#include "../CMT.h"
|
||||
|
||||
std::unique_ptr<ICursor> CursorHandler::createCursor()
|
||||
{
|
||||
if (settings["video"]["softwareCursor"].Bool())
|
||||
return std::make_unique<CursorSoftware>();
|
||||
else
|
||||
return std::make_unique<CursorHardware>();
|
||||
}
|
||||
|
||||
CursorHandler::CursorHandler()
|
||||
: cursor(createCursor())
|
||||
, frameTime(0.f)
|
||||
, showing(false)
|
||||
, pos(0,0)
|
||||
{
|
||||
|
||||
type = Cursor::Type::DEFAULT;
|
||||
dndObject = nullptr;
|
||||
|
||||
cursors =
|
||||
{
|
||||
std::make_unique<CAnimation>("CRADVNTR"),
|
||||
std::make_unique<CAnimation>("CRCOMBAT"),
|
||||
std::make_unique<CAnimation>("CRDEFLT"),
|
||||
std::make_unique<CAnimation>("CRSPELL")
|
||||
};
|
||||
|
||||
for (auto & cursor : cursors)
|
||||
cursor->preload();
|
||||
|
||||
set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
Point CursorHandler::position() const
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
|
||||
{
|
||||
assert(dndObject == nullptr);
|
||||
|
||||
if (type == this->type && index == this->frame)
|
||||
return;
|
||||
|
||||
this->type = type;
|
||||
this->frame = index;
|
||||
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Default index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Map index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Combat index)
|
||||
{
|
||||
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
|
||||
}
|
||||
|
||||
void CursorHandler::set(Cursor::Spellcast index)
|
||||
{
|
||||
//Note: this is animated cursor, ignore specified frame and only change type
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, frame);
|
||||
}
|
||||
|
||||
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
|
||||
{
|
||||
dndObject = image;
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
||||
void CursorHandler::dragAndDropCursor (std::string path, size_t index)
|
||||
{
|
||||
CAnimation anim(path);
|
||||
anim.load(index);
|
||||
dragAndDropCursor(anim.getImage(index));
|
||||
}
|
||||
|
||||
void CursorHandler::cursorMove(const int & x, const int & y)
|
||||
{
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
|
||||
cursor->setCursorPosition(pos);
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetDefault(size_t index)
|
||||
{
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetMap(size_t index)
|
||||
{
|
||||
static const std::array<Point, 43> offsets = {{
|
||||
{ 0, 0}, // POINTER = 0,
|
||||
{ 0, 0}, // HOURGLASS = 1,
|
||||
{ 12, 10}, // HERO = 2,
|
||||
{ 12, 12}, // TOWN = 3,
|
||||
|
||||
{ 15, 13}, // T1_MOVE = 4,
|
||||
{ 13, 13}, // T1_ATTACK = 5,
|
||||
{ 16, 32}, // T1_SAIL = 6,
|
||||
{ 13, 20}, // T1_DISEMBARK = 7,
|
||||
{ 8, 9}, // T1_EXCHANGE = 8,
|
||||
{ 14, 16}, // T1_VISIT = 9,
|
||||
|
||||
{ 15, 13}, // T2_MOVE = 10,
|
||||
{ 13, 13}, // T2_ATTACK = 11,
|
||||
{ 16, 32}, // T2_SAIL = 12,
|
||||
{ 13, 20}, // T2_DISEMBARK = 13,
|
||||
{ 8, 9}, // T2_EXCHANGE = 14,
|
||||
{ 14, 16}, // T2_VISIT = 15,
|
||||
|
||||
{ 15, 13}, // T3_MOVE = 16,
|
||||
{ 13, 13}, // T3_ATTACK = 17,
|
||||
{ 16, 32}, // T3_SAIL = 18,
|
||||
{ 13, 20}, // T3_DISEMBARK = 19,
|
||||
{ 8, 9}, // T3_EXCHANGE = 20,
|
||||
{ 14, 16}, // T3_VISIT = 21,
|
||||
|
||||
{ 15, 13}, // T4_MOVE = 22,
|
||||
{ 13, 13}, // T4_ATTACK = 23,
|
||||
{ 16, 32}, // T4_SAIL = 24,
|
||||
{ 13, 20}, // T4_DISEMBARK = 25,
|
||||
{ 8, 9}, // T4_EXCHANGE = 26,
|
||||
{ 14, 16}, // T4_VISIT = 27,
|
||||
|
||||
{ 16, 32}, // T1_SAIL_VISIT = 28,
|
||||
{ 16, 32}, // T2_SAIL_VISIT = 29,
|
||||
{ 16, 32}, // T3_SAIL_VISIT = 30,
|
||||
{ 16, 32}, // T4_SAIL_VISIT = 31,
|
||||
|
||||
{ 6, 1}, // SCROLL_NORTH = 32,
|
||||
{ 16, 2}, // SCROLL_NORTHEAST = 33,
|
||||
{ 21, 6}, // SCROLL_EAST = 34,
|
||||
{ 16, 16}, // SCROLL_SOUTHEAST = 35,
|
||||
{ 6, 21}, // SCROLL_SOUTH = 36,
|
||||
{ 1, 16}, // SCROLL_SOUTHWEST = 37,
|
||||
{ 1, 5}, // SCROLL_WEST = 38,
|
||||
{ 2, 1}, // SCROLL_NORTHWEST = 39,
|
||||
|
||||
{ 0, 0}, // POINTER_COPY = 40,
|
||||
{ 14, 16}, // TELEPORT = 41,
|
||||
{ 20, 20}, // SCUTTLE_BOAT = 42
|
||||
}};
|
||||
|
||||
assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor
|
||||
assert(index < offsets.size());
|
||||
return offsets[index];
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetCombat(size_t index)
|
||||
{
|
||||
static const std::array<Point, 20> offsets = {{
|
||||
{ 12, 12 }, // BLOCKED = 0,
|
||||
{ 10, 14 }, // MOVE = 1,
|
||||
{ 14, 14 }, // FLY = 2,
|
||||
{ 12, 12 }, // SHOOT = 3,
|
||||
{ 12, 12 }, // HERO = 4,
|
||||
{ 8, 12 }, // QUERY = 5,
|
||||
{ 0, 0 }, // POINTER = 6,
|
||||
{ 21, 0 }, // HIT_NORTHEAST = 7,
|
||||
{ 31, 5 }, // HIT_EAST = 8,
|
||||
{ 21, 21 }, // HIT_SOUTHEAST = 9,
|
||||
{ 0, 21 }, // HIT_SOUTHWEST = 10,
|
||||
{ 0, 5 }, // HIT_WEST = 11,
|
||||
{ 0, 0 }, // HIT_NORTHWEST = 12,
|
||||
{ 6, 0 }, // HIT_NORTH = 13,
|
||||
{ 6, 31 }, // HIT_SOUTH = 14,
|
||||
{ 14, 0 }, // SHOOT_PENALTY = 15,
|
||||
{ 12, 12 }, // SHOOT_CATAPULT = 16,
|
||||
{ 12, 12 }, // HEAL = 17,
|
||||
{ 12, 12 }, // SACRIFICE = 18,
|
||||
{ 14, 20 }, // TELEPORT = 19
|
||||
}};
|
||||
|
||||
assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor
|
||||
assert(index < offsets.size());
|
||||
return offsets[index];
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffsetSpellcast()
|
||||
{
|
||||
return { 18, 28};
|
||||
}
|
||||
|
||||
Point CursorHandler::getPivotOffset()
|
||||
{
|
||||
if (dndObject)
|
||||
return dndObject->dimensions() / 2;
|
||||
|
||||
switch (type) {
|
||||
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
|
||||
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
|
||||
case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
|
||||
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
|
||||
};
|
||||
|
||||
assert(0);
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
|
||||
{
|
||||
if (dndObject)
|
||||
return dndObject;
|
||||
|
||||
return cursors[static_cast<size_t>(type)]->getImage(frame);
|
||||
}
|
||||
|
||||
void CursorHandler::centerCursor()
|
||||
{
|
||||
Point screenSize {screen->w, screen->h};
|
||||
pos = screenSize / 2 - getPivotOffset();
|
||||
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
|
||||
SDL_WarpMouse(pos.x, pos.y);
|
||||
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
|
||||
|
||||
cursor->setCursorPosition(pos);
|
||||
}
|
||||
|
||||
void CursorHandler::updateSpellcastCursor()
|
||||
{
|
||||
static const float frameDisplayDuration = 0.1f;
|
||||
|
||||
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
|
||||
size_t newFrame = frame;
|
||||
|
||||
while (frameTime >= frameDisplayDuration)
|
||||
{
|
||||
frameTime -= frameDisplayDuration;
|
||||
newFrame++;
|
||||
}
|
||||
|
||||
auto & animation = cursors.at(static_cast<size_t>(type));
|
||||
|
||||
while (newFrame >= animation->size())
|
||||
newFrame -= animation->size();
|
||||
|
||||
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
|
||||
}
|
||||
|
||||
void CursorHandler::render()
|
||||
{
|
||||
if(!showing)
|
||||
return;
|
||||
|
||||
if (type == Cursor::Type::SPELLBOOK)
|
||||
updateSpellcastCursor();
|
||||
|
||||
cursor->render();
|
||||
}
|
||||
|
||||
void CursorSoftware::render()
|
||||
{
|
||||
//texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
|
||||
if (needUpdate)
|
||||
updateTexture();
|
||||
|
||||
Point renderPos = pos - pivot;
|
||||
|
||||
SDL_Rect destRect;
|
||||
destRect.x = renderPos.x;
|
||||
destRect.y = renderPos.y;
|
||||
destRect.w = 40;
|
||||
destRect.h = 40;
|
||||
|
||||
SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect);
|
||||
}
|
||||
|
||||
void CursorSoftware::createTexture(const Point & dimensions)
|
||||
{
|
||||
if(cursorTexture)
|
||||
SDL_DestroyTexture(cursorTexture);
|
||||
|
||||
if (cursorSurface)
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
|
||||
cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y);
|
||||
cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y);
|
||||
|
||||
SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE);
|
||||
SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
|
||||
void CursorSoftware::updateTexture()
|
||||
{
|
||||
Point dimensions(-1, -1);
|
||||
|
||||
if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions())
|
||||
createTexture(cursorImage->dimensions());
|
||||
|
||||
Uint32 fillColor = SDL_MapRGBA(cursorSurface->format, 0, 0, 0, 0);
|
||||
CSDL_Ext::fillRect(cursorSurface, nullptr, fillColor);
|
||||
|
||||
cursorImage->draw(cursorSurface);
|
||||
SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch);
|
||||
needUpdate = false;
|
||||
}
|
||||
|
||||
void CursorSoftware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
assert(image != nullptr);
|
||||
cursorImage = image;
|
||||
pivot = pivotOffset;
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
void CursorSoftware::setCursorPosition( const Point & newPos )
|
||||
{
|
||||
pos = newPos;
|
||||
}
|
||||
|
||||
CursorSoftware::CursorSoftware():
|
||||
cursorTexture(nullptr),
|
||||
cursorSurface(nullptr),
|
||||
needUpdate(false),
|
||||
pivot(0,0)
|
||||
{
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
CursorSoftware::~CursorSoftware()
|
||||
{
|
||||
if(cursorTexture)
|
||||
SDL_DestroyTexture(cursorTexture);
|
||||
|
||||
if (cursorSurface)
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
|
||||
}
|
||||
|
||||
CursorHardware::CursorHardware():
|
||||
cursor(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CursorHardware::~CursorHardware()
|
||||
{
|
||||
if(cursor)
|
||||
SDL_FreeCursor(cursor);
|
||||
}
|
||||
|
||||
void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y);
|
||||
|
||||
Uint32 fillColor = SDL_MapRGBA(cursorSurface->format, 0, 0, 0, 0);
|
||||
CSDL_Ext::fillRect(cursorSurface, nullptr, fillColor);
|
||||
|
||||
image->draw(cursorSurface);
|
||||
|
||||
auto oldCursor = cursor;
|
||||
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);
|
||||
|
||||
if (!cursor)
|
||||
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
|
||||
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
SDL_SetCursor(cursor);
|
||||
|
||||
if (oldCursor)
|
||||
SDL_FreeCursor(oldCursor);
|
||||
}
|
||||
|
||||
void CursorHardware::setCursorPosition( const Point & newPos )
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
|
||||
void CursorHardware::render()
|
||||
{
|
||||
//no-op
|
||||
}
|
@ -8,11 +8,14 @@
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
class CIntObject;
|
||||
class CAnimImage;
|
||||
|
||||
class CAnimation;
|
||||
class IImage;
|
||||
struct SDL_Surface;
|
||||
struct SDL_Texture;
|
||||
struct Point;
|
||||
struct SDL_Cursor;
|
||||
|
||||
#include "Geometries.h"
|
||||
|
||||
namespace Cursor
|
||||
{
|
||||
@ -51,7 +54,9 @@ namespace Cursor
|
||||
SHOOT_CATAPULT = 16,
|
||||
HEAL = 17,
|
||||
SACRIFICE = 18,
|
||||
TELEPORT = 19
|
||||
TELEPORT = 19,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Map {
|
||||
@ -97,7 +102,9 @@ namespace Cursor
|
||||
SCROLL_NORTHWEST = 39,
|
||||
//POINTER_COPY = 40, // probably unused
|
||||
TELEPORT = 41,
|
||||
SCUTTLE_BOAT = 42
|
||||
SCUTTLE_BOAT = 42,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum class Spellcast {
|
||||
@ -105,49 +112,92 @@ namespace Cursor
|
||||
};
|
||||
}
|
||||
|
||||
/// handles mouse cursor
|
||||
class CCursorHandler final
|
||||
class ICursor
|
||||
{
|
||||
public:
|
||||
virtual ~ICursor() = default;
|
||||
|
||||
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
|
||||
virtual void setCursorPosition( const Point & newPos ) = 0;
|
||||
virtual void render() = 0;
|
||||
};
|
||||
|
||||
class CursorHardware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Cursor * cursor;
|
||||
|
||||
public:
|
||||
CursorHardware();
|
||||
~CursorHardware();
|
||||
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
};
|
||||
|
||||
class CursorSoftware : public ICursor
|
||||
{
|
||||
std::shared_ptr<IImage> cursorImage;
|
||||
|
||||
SDL_Texture * cursorTexture;
|
||||
SDL_Surface * cursorSurface;
|
||||
|
||||
Point pos;
|
||||
Point pivot;
|
||||
bool needUpdate;
|
||||
SDL_Texture * cursorLayer;
|
||||
|
||||
SDL_Surface * buffer;
|
||||
CAnimImage * currentCursor;
|
||||
void createTexture(const Point & dimensions);
|
||||
void updateTexture();
|
||||
public:
|
||||
CursorSoftware();
|
||||
~CursorSoftware();
|
||||
|
||||
std::unique_ptr<CAnimImage> dndObject; //if set, overrides currentCursor
|
||||
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
|
||||
void setCursorPosition( const Point & newPos ) override;
|
||||
void render() override;
|
||||
};
|
||||
|
||||
std::array<std::unique_ptr<CAnimImage>, 4> cursors;
|
||||
/// handles mouse cursor
|
||||
class CursorHandler final
|
||||
{
|
||||
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
|
||||
|
||||
std::array<std::unique_ptr<CAnimation>, 4> cursors;
|
||||
|
||||
bool showing;
|
||||
|
||||
void clearBuffer();
|
||||
void updateBuffer(CIntObject * payload);
|
||||
void replaceBuffer(CIntObject * payload);
|
||||
void shiftPos( int &x, int &y );
|
||||
|
||||
void updateTexture();
|
||||
|
||||
/// Current cursor
|
||||
Cursor::Type type;
|
||||
size_t frame;
|
||||
float frameTime;
|
||||
Point pos;
|
||||
|
||||
void changeGraphic(Cursor::Type type, size_t index);
|
||||
|
||||
/// position of cursor
|
||||
int xpos, ypos;
|
||||
Point getPivotOffsetDefault(size_t index);
|
||||
Point getPivotOffsetMap(size_t index);
|
||||
Point getPivotOffsetCombat(size_t index);
|
||||
Point getPivotOffsetSpellcast();
|
||||
Point getPivotOffset();
|
||||
|
||||
void updateSpellcastCursor();
|
||||
|
||||
std::shared_ptr<IImage> getCurrentImage();
|
||||
|
||||
std::unique_ptr<ICursor> cursor;
|
||||
|
||||
static std::unique_ptr<ICursor> createCursor();
|
||||
public:
|
||||
CCursorHandler();
|
||||
~CCursorHandler();
|
||||
CursorHandler();
|
||||
~CursorHandler();
|
||||
|
||||
/**
|
||||
* Replaces the cursor with a custom image.
|
||||
*
|
||||
* @param image Image to replace cursor with or nullptr to use the normal
|
||||
* cursor. CursorHandler takes ownership of object
|
||||
*/
|
||||
void dragAndDropCursor (std::unique_ptr<CAnimImage> image);
|
||||
/// Replaces the cursor with a custom image.
|
||||
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
|
||||
void dragAndDropCursor(std::shared_ptr<IImage> image);
|
||||
|
||||
void dragAndDropCursor(std::string path, size_t index);
|
||||
|
||||
/// Returns current position of the cursor
|
||||
Point position() const;
|
@ -29,6 +29,7 @@
|
||||
#include "../../lib/rmg/CMapGenOptions.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/rmg/CRmgTemplateStorage.h"
|
||||
#include "../../lib/RoadHandler.h"
|
||||
|
||||
RandomMapTab::RandomMapTab():
|
||||
InterfaceObjectConfigurable()
|
||||
@ -108,12 +109,12 @@ RandomMapTab::RandomMapTab():
|
||||
GH.pushIntT<TeamAlignmentsWidget>(*this);
|
||||
});
|
||||
|
||||
for(auto road : VLC->terrainTypeHandler->roads())
|
||||
for(auto road : VLC->roadTypeHandler->objects)
|
||||
{
|
||||
std::string cbRoadType = "selectRoad_" + road.name;
|
||||
std::string cbRoadType = "selectRoad_" + road->getJsonKey();
|
||||
addCallback(cbRoadType, [&, road](bool on)
|
||||
{
|
||||
mapGenOptions->setRoadEnabled(road.name, on);
|
||||
mapGenOptions->setRoadEnabled(road->getJsonKey(), on);
|
||||
updateMapInfoByHost();
|
||||
});
|
||||
}
|
||||
@ -283,11 +284,11 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
|
||||
else
|
||||
w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL);
|
||||
}
|
||||
for(auto r : VLC->terrainTypeHandler->roads())
|
||||
for(auto r : VLC->roadTypeHandler->objects)
|
||||
{
|
||||
if(auto w = widget<CToggleButton>(r.name))
|
||||
if(auto w = widget<CToggleButton>(r->getJsonKey()))
|
||||
{
|
||||
w->setSelected(opts->isRoadEnabled(r.name));
|
||||
w->setSelected(opts->isRoadEnabled(r->getJsonKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "../../lib/filesystem/CCompressedStream.h"
|
||||
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
|
@ -30,7 +30,9 @@
|
||||
#include "CMT.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
#include "../lib/Terrain.h"
|
||||
#include "../lib/RoadHandler.h"
|
||||
#include "../lib/RiverHandler.h"
|
||||
#include "../lib/TerrainHandler.h"
|
||||
#include "../lib/filesystem/ResourceID.h"
|
||||
#include "../lib/JsonDetail.h"
|
||||
|
||||
@ -175,17 +177,17 @@ void CMapHandler::initTerrainGraphics()
|
||||
std::map<std::string, std::string> terrainFiles;
|
||||
std::map<std::string, std::string> riverFiles;
|
||||
std::map<std::string, std::string> roadFiles;
|
||||
for(const auto & terrain : VLC->terrainTypeHandler->terrains())
|
||||
for(const auto & terrain : VLC->terrainTypeHandler->objects)
|
||||
{
|
||||
terrainFiles[terrain.name] = terrain.tilesFilename;
|
||||
terrainFiles[terrain->getJsonKey()] = terrain->tilesFilename;
|
||||
}
|
||||
for(const auto & river : VLC->terrainTypeHandler->rivers())
|
||||
for(const auto & river : VLC->riverTypeHandler->objects)
|
||||
{
|
||||
riverFiles[river.fileName] = river.fileName;
|
||||
riverFiles[river->getJsonKey()] = river->tilesFilename;
|
||||
}
|
||||
for(const auto & road : VLC->terrainTypeHandler->roads())
|
||||
for(const auto & road : VLC->roadTypeHandler->objects)
|
||||
{
|
||||
roadFiles[road.fileName] = road.fileName;
|
||||
roadFiles[road->getJsonKey()] = road->tilesFilename;
|
||||
}
|
||||
|
||||
loadFlipped(terrainAnimations, terrainImages, terrainFiles);
|
||||
@ -606,7 +608,7 @@ void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const T
|
||||
ui8 rotation = tinfo.extTileFlags % 4;
|
||||
|
||||
//TODO: use ui8 instead of string key
|
||||
auto terrainName = tinfo.terType->name;
|
||||
auto terrainName = tinfo.terType->getJsonKey();
|
||||
|
||||
if(parent->terrainImages[terrainName].size()<=tinfo.terView)
|
||||
return;
|
||||
@ -786,21 +788,21 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
|
||||
|
||||
void CMapHandler::CMapBlitter::drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const
|
||||
{
|
||||
if (tinfoUpper && tinfoUpper->roadType->id != Road::NO_ROAD)
|
||||
if (tinfoUpper && tinfoUpper->roadType->getId() != Road::NO_ROAD)
|
||||
{
|
||||
ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4;
|
||||
Rect source(0, tileSize / 2, tileSize, tileSize / 2);
|
||||
Rect dest(realPos.x, realPos.y, tileSize, tileSize / 2);
|
||||
drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType->fileName][tinfoUpper->roadDir][rotation],
|
||||
drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType->getJsonKey()][tinfoUpper->roadDir][rotation],
|
||||
&source, targetSurf, &dest);
|
||||
}
|
||||
|
||||
if(tinfo.roadType->id != Road::NO_ROAD) //print road from this tile
|
||||
if(tinfo.roadType->getId() != Road::NO_ROAD) //print road from this tile
|
||||
{
|
||||
ui8 rotation = (tinfo.extTileFlags >> 4) % 4;
|
||||
Rect source(0, 0, tileSize, halfTileSizeCeil);
|
||||
Rect dest(realPos.x, realPos.y + tileSize / 2, tileSize, tileSize / 2);
|
||||
drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType->fileName][tinfo.roadDir][rotation],
|
||||
drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType->getJsonKey()][tinfo.roadDir][rotation],
|
||||
&source, targetSurf, &dest);
|
||||
}
|
||||
}
|
||||
@ -809,7 +811,7 @@ void CMapHandler::CMapBlitter::drawRiver(SDL_Surface * targetSurf, const Terrain
|
||||
{
|
||||
Rect destRect(realTileRect);
|
||||
ui8 rotation = (tinfo.extTileFlags >> 2) % 4;
|
||||
drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->fileName][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect);
|
||||
drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->getJsonKey()][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect);
|
||||
}
|
||||
|
||||
void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const
|
||||
@ -860,7 +862,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
||||
if(isVisible || info->showAllTerrain)
|
||||
{
|
||||
drawTileTerrain(targetSurf, tinfo, tile);
|
||||
if(tinfo.riverType->id != River::NO_RIVER)
|
||||
if(tinfo.riverType->getId() != River::NO_RIVER)
|
||||
drawRiver(targetSurf, tinfo);
|
||||
drawRoad(targetSurf, tinfo, tinfoUpper);
|
||||
}
|
||||
@ -1388,8 +1390,9 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isTile2Terrain || out.empty())
|
||||
out = CGI->generaltexth->terrainNames[t.terType->id];
|
||||
out = t.terType->getNameTranslated();
|
||||
|
||||
if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG)
|
||||
{
|
||||
@ -1485,4 +1488,3 @@ TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, SDL_Rect rec
|
||||
TerrainTileObject::~TerrainTileObject()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -41,13 +41,14 @@
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/Terrain.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/JsonNode.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
#include "../../lib/StringConstants.h"
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
CList::CListItem::CListItem(CList * Parent)
|
||||
: CIntObject(LCLICK | RCLICK | HOVER),
|
||||
@ -390,7 +391,7 @@ const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos)
|
||||
}
|
||||
|
||||
// else - use terrain color (blocked version or normal)
|
||||
const auto & colorPair = parent->colors.find(tile->terType->id)->second;
|
||||
const auto & colorPair = parent->colors.find(tile->terType->getId())->second;
|
||||
if (tile->blocked && (!tile->visitable))
|
||||
return colorPair.second;
|
||||
else
|
||||
@ -499,25 +500,25 @@ std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors()
|
||||
{
|
||||
std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > ret;
|
||||
|
||||
for(const auto & terrain : CGI->terrainTypeHandler->terrains())
|
||||
for(const auto & terrain : CGI->terrainTypeHandler->objects)
|
||||
{
|
||||
SDL_Color normal =
|
||||
{
|
||||
ui8(terrain.minimapUnblocked[0]),
|
||||
ui8(terrain.minimapUnblocked[1]),
|
||||
ui8(terrain.minimapUnblocked[2]),
|
||||
ui8(terrain->minimapUnblocked[0]),
|
||||
ui8(terrain->minimapUnblocked[1]),
|
||||
ui8(terrain->minimapUnblocked[2]),
|
||||
ui8(255)
|
||||
};
|
||||
|
||||
SDL_Color blocked =
|
||||
{
|
||||
ui8(terrain.minimapBlocked[0]),
|
||||
ui8(terrain.minimapBlocked[1]),
|
||||
ui8(terrain.minimapBlocked[2]),
|
||||
ui8(terrain->minimapBlocked[0]),
|
||||
ui8(terrain->minimapBlocked[1]),
|
||||
ui8(terrain->minimapBlocked[2]),
|
||||
ui8(255)
|
||||
};
|
||||
|
||||
ret[terrain.id] = std::make_pair(normal, blocked);
|
||||
ret[terrain->getId()] = std::make_pair(normal, blocked);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -1141,15 +1142,29 @@ void CInGameConsole::startEnteringText()
|
||||
GH.statusbar->setEnteredText(enteredText);
|
||||
}
|
||||
|
||||
void CInGameConsole::endEnteringText(bool printEnteredText)
|
||||
void CInGameConsole::endEnteringText(bool processEnteredText)
|
||||
{
|
||||
captureAllKeys = false;
|
||||
prevEntDisp = -1;
|
||||
if(printEnteredText)
|
||||
if(processEnteredText)
|
||||
{
|
||||
std::string txt = enteredText.substr(0, enteredText.size()-1);
|
||||
LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection());
|
||||
previouslyEntered.push_back(txt);
|
||||
|
||||
if(txt.at(0) == '/')
|
||||
{
|
||||
//some commands like gosolo don't work when executed from GUI thread
|
||||
auto threadFunction = [=]()
|
||||
{
|
||||
ClientCommandManager commandController;
|
||||
commandController.processCommand(txt.substr(1), true);
|
||||
};
|
||||
|
||||
boost::thread clientCommandThread(threadFunction);
|
||||
clientCommandThread.detach();
|
||||
}
|
||||
else
|
||||
LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection());
|
||||
}
|
||||
enteredText.clear();
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include "ObjectLists.h"
|
||||
#include "../../lib/FunctionList.h"
|
||||
#include "Terrain.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -424,7 +423,7 @@ public:
|
||||
void textEdited(const SDL_TextEditingEvent & event) override;
|
||||
|
||||
void startEnteringText();
|
||||
void endEnteringText(bool printEnteredText);
|
||||
void endEnteringText(bool processEnteredText);
|
||||
void refreshEnteredText();
|
||||
|
||||
CInGameConsole();
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "CArtifactHolder.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "Buttons.h"
|
||||
#include "CComponent.h"
|
||||
@ -257,7 +257,7 @@ void CHeroArtPlace::clickRight(tribool down, bool previousState)
|
||||
void CArtifactsOfHero::activate()
|
||||
{
|
||||
if (commonInfo->src.AOH == this && commonInfo->src.art)
|
||||
CCS->curh->dragAndDropCursor(std::make_unique<CAnimImage>("artifact", commonInfo->src.art->artType->getIconIndex()));
|
||||
CCS->curh->dragAndDropCursor("artifact", commonInfo->src.art->artType->getIconIndex());
|
||||
|
||||
CIntObject::activate();
|
||||
}
|
||||
@ -278,19 +278,18 @@ void CHeroArtPlace::select ()
|
||||
if (locked)
|
||||
return;
|
||||
|
||||
selectSlot(true);
|
||||
pickSlot(true);
|
||||
if(ourArt->canBeDisassembled() && slotID < GameConstants::BACKPACK_START) //worn combined artifact -> locks have to disappear
|
||||
{
|
||||
for(int i = 0; i < GameConstants::BACKPACK_START; i++)
|
||||
for(auto slot : ArtifactUtils::constituentWornSlots())
|
||||
{
|
||||
auto ap = ourOwner->getArtPlace(i);
|
||||
auto ap = ourOwner->getArtPlace(slot);
|
||||
if(ap)//getArtPlace may return null
|
||||
ap->pickSlot(ourArt->isPart(ap->ourArt));
|
||||
}
|
||||
}
|
||||
|
||||
CCS->curh->dragAndDropCursor(std::make_unique<CAnimImage>("artifact", ourArt->artType->getIconIndex()));
|
||||
CCS->curh->dragAndDropCursor("artifact", ourArt->artType->getIconIndex());
|
||||
ourOwner->commonInfo->src.setTo(this, false);
|
||||
ourOwner->markPossibleSlots(ourArt);
|
||||
|
||||
@ -309,9 +308,9 @@ void CHeroArtPlace::deselect ()
|
||||
pickSlot(false);
|
||||
if(ourArt && ourArt->canBeDisassembled()) //combined art returned to its slot -> restore locks
|
||||
{
|
||||
for(int i = 0; i < GameConstants::BACKPACK_START; i++)
|
||||
for(auto slot : ArtifactUtils::constituentWornSlots())
|
||||
{
|
||||
auto place = ourOwner->getArtPlace(i);
|
||||
auto place = ourOwner->getArtPlace(slot);
|
||||
|
||||
if(nullptr != place)//getArtPlace may return null
|
||||
place->pickSlot(false);
|
||||
@ -670,6 +669,16 @@ CArtifactsOfHero::CArtifactsOfHero(const Point & position, bool createCommonPart
|
||||
CArtifactsOfHero::~CArtifactsOfHero()
|
||||
{
|
||||
dispose();
|
||||
// Artifact located in artifactsTransitionPos should be returned
|
||||
if(!curHero->artifactsTransitionPos.empty())
|
||||
{
|
||||
auto artPlace = getArtPlace(
|
||||
ArtifactUtils::getArtifactDstPosition(curHero->artifactsTransitionPos.begin()->artifact, curHero, curHero->bearerType()));
|
||||
assert(artPlace);
|
||||
assert(artPlace->ourOwner);
|
||||
artPlace->setMeAsDest();
|
||||
artPlace->ourOwner->realizeCurrentTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactsOfHero::updateParentWindow()
|
||||
@ -716,85 +725,76 @@ void CArtifactsOfHero::realizeCurrentTransaction()
|
||||
ArtifactLocation(commonInfo->dst.AOH->curHero, commonInfo->dst.slotID));
|
||||
}
|
||||
|
||||
void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
|
||||
void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst)
|
||||
{
|
||||
bool isCurHeroSrc = src.isHolder(curHero),
|
||||
isCurHeroDst = dst.isHolder(curHero);
|
||||
if(isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
|
||||
if(isCurHeroSrc && ArtifactUtils::isSlotBackpack(src.slot))
|
||||
updateSlot(src.slot);
|
||||
if(isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START)
|
||||
if(isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot))
|
||||
updateSlot(dst.slot);
|
||||
if(isCurHeroSrc || isCurHeroDst) //we need to update all slots, artifact might be combined and affect more slots
|
||||
// We need to update all slots, artifact might be combined and affect more slots
|
||||
if(isCurHeroSrc || isCurHeroDst)
|
||||
updateWornSlots(false);
|
||||
|
||||
if (!src.isHolder(curHero) && !isCurHeroDst)
|
||||
if(!isCurHeroSrc && !isCurHeroDst)
|
||||
return;
|
||||
|
||||
if(commonInfo->src == src) //artifact was taken from us
|
||||
// When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst
|
||||
// however after first movement we pick the art from TRANSITION_POS and the second movement coming when
|
||||
// we have a different artifact may look surprising... but it's valid.
|
||||
|
||||
// Used when doing dragAndDrop and artifact swap multiple times
|
||||
if(src.slot == ArtifactPosition::TRANSITION_POS &&
|
||||
commonInfo->src.slotID == ArtifactPosition::TRANSITION_POS &&
|
||||
commonInfo->dst.slotID == ArtifactPosition::PRE_FIRST &&
|
||||
isCurHeroDst)
|
||||
{
|
||||
assert(commonInfo->dst == dst //expected movement from slot ot slot
|
||||
|| dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START //artifact moved back to backpack (eg. to make place for art we are moving)
|
||||
auto art = curHero->getArt(ArtifactPosition::TRANSITION_POS);
|
||||
assert(art);
|
||||
CCS->curh->dragAndDropCursor("artifact", art->artType->getIconIndex());
|
||||
markPossibleSlots(art);
|
||||
|
||||
commonInfo->src.art = art;
|
||||
commonInfo->src.slotID = src.slot;
|
||||
}
|
||||
// Artifact was taken from us
|
||||
else if(commonInfo->src == src)
|
||||
{
|
||||
// Expected movement from slot ot slot
|
||||
assert(commonInfo->dst == dst
|
||||
// Artifact moved back to backpack (eg. to make place for art we are moving)
|
||||
|| dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START
|
||||
|| dst.getHolderArtSet()->bearerType() != ArtBearer::HERO);
|
||||
commonInfo->reset();
|
||||
unmarkSlots();
|
||||
}
|
||||
else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it
|
||||
// The dest artifact was moved after the swap -> we are picking it
|
||||
else if(commonInfo->dst == src)
|
||||
{
|
||||
assert(dst.slot >= GameConstants::BACKPACK_START);
|
||||
assert(dst.slot == ArtifactPosition::TRANSITION_POS);
|
||||
commonInfo->reset();
|
||||
|
||||
CArtifactsOfHero::ArtPlacePtr ap;
|
||||
for(CArtifactsOfHero *aoh : commonInfo->participants)
|
||||
for(CArtifactsOfHero * aoh : commonInfo->participants)
|
||||
{
|
||||
if(dst.isHolder(aoh->curHero))
|
||||
{
|
||||
commonInfo->src.AOH = aoh;
|
||||
if((ap = aoh->getArtPlace(dst.slot)))//getArtPlace may return null
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ap)
|
||||
{
|
||||
ap->select();
|
||||
}
|
||||
else
|
||||
{
|
||||
commonInfo->src.art = dst.getArt();
|
||||
commonInfo->src.slotID = dst.slot;
|
||||
assert(commonInfo->src.AOH);
|
||||
CCS->curh->dragAndDropCursor(std::make_unique<CAnimImage>("artifact", dst.getArt()->artType->getIconIndex()));
|
||||
markPossibleSlots(dst.getArt());
|
||||
}
|
||||
}
|
||||
else if(src.slot >= GameConstants::BACKPACK_START &&
|
||||
src.slot < commonInfo->src.slotID &&
|
||||
src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one
|
||||
{
|
||||
//int fixedSlot = src.hero->getArtPos(commonInfo->src.art);
|
||||
vstd::advance(commonInfo->src.slotID, -1);
|
||||
assert(commonInfo->src.valid());
|
||||
}
|
||||
else
|
||||
{
|
||||
//when moving one artifact onto another it leads to two art movements: dst->backapck; src->dst
|
||||
// however after first movement we pick the art from backpack and the second movement coming when
|
||||
// we have a different artifact may look surprising... but it's valid.
|
||||
commonInfo->src.art = dst.getArt();
|
||||
commonInfo->src.slotID = dst.slot;
|
||||
assert(commonInfo->src.AOH);
|
||||
CCS->curh->dragAndDropCursor("artifact", dst.getArt()->artType->getIconIndex());
|
||||
}
|
||||
|
||||
updateParentWindow();
|
||||
int shift = 0;
|
||||
// if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos)
|
||||
// shift++;
|
||||
//
|
||||
if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos)
|
||||
shift++;
|
||||
if(dst.slot < GameConstants::BACKPACK_START && src.slot - GameConstants::BACKPACK_START < backpackPos)
|
||||
shift--;
|
||||
|
||||
if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START)
|
||||
|| (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) )
|
||||
scrollBackpack(shift); //update backpack slots
|
||||
// If backpack is changed, update it
|
||||
if((isCurHeroSrc && ArtifactUtils::isSlotBackpack(src.slot))
|
||||
|| (isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot)))
|
||||
scrollBackpack(0);
|
||||
}
|
||||
|
||||
void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
|
||||
@ -808,11 +808,15 @@ void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al)
|
||||
}
|
||||
}
|
||||
|
||||
CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(int slot)
|
||||
CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(ArtifactPosition slot)
|
||||
{
|
||||
if(slot == ArtifactPosition::TRANSITION_POS)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if(slot < GameConstants::BACKPACK_START)
|
||||
{
|
||||
if(artWorn.find(ArtifactPosition(slot)) == artWorn.end())
|
||||
if(artWorn.find(slot) == artWorn.end())
|
||||
{
|
||||
logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot);
|
||||
return nullptr;
|
||||
|
@ -141,7 +141,7 @@ public:
|
||||
void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst);
|
||||
void artifactRemoved(const ArtifactLocation &al);
|
||||
void artifactUpdateSlots(const ArtifactLocation &al);
|
||||
ArtPlacePtr getArtPlace(int slot);//may return null
|
||||
ArtPlacePtr getArtPlace(ArtifactPosition slot);//may return null
|
||||
|
||||
void setHero(const CGHeroInstance * hero);
|
||||
const CGHeroInstance *getHero() const;
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <vcmi/spells/Spell.h>
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../CMessage.h"
|
||||
#include "../CGameInfo.h"
|
||||
|
@ -15,7 +15,8 @@
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/SDL_Pixels.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/ColorFilter.h"
|
||||
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
@ -339,7 +340,7 @@ void CAnimImage::playerColored(PlayerColor currPlayer)
|
||||
anim->getImage(0, group)->playerColored(player);
|
||||
}
|
||||
|
||||
CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group):
|
||||
CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group, uint8_t alpha):
|
||||
anim(std::make_shared<CAnimation>(name)),
|
||||
group(Group),
|
||||
frame(0),
|
||||
@ -349,7 +350,7 @@ CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Del
|
||||
flags(Flags),
|
||||
xOffset(0),
|
||||
yOffset(0),
|
||||
alpha(255)
|
||||
alpha(alpha)
|
||||
{
|
||||
anim->loadGroup(group);
|
||||
last = anim->size(group);
|
||||
@ -454,7 +455,12 @@ void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to)
|
||||
Rect src( xOffset, yOffset, pos.w, pos.h);
|
||||
auto img = anim->getImage(frame, group);
|
||||
if(img)
|
||||
img->draw(to, pos.x, pos.y, &src, alpha);
|
||||
{
|
||||
const ColorFilter alphaFilter = ColorFilter::genAlphaShifter(vstd::lerp(0.0f, 1.0f, alpha/255.0f));
|
||||
img->adjustPalette(alphaFilter);
|
||||
|
||||
img->draw(to, pos.x, pos.y, &src);
|
||||
}
|
||||
}
|
||||
|
||||
void CShowableAnim::rotate(bool on, bool vertical)
|
||||
|
@ -142,7 +142,7 @@ public:
|
||||
//Set per-surface alpha, 0 = transparent, 255 = opaque
|
||||
void setAlpha(ui32 alphaValue);
|
||||
|
||||
CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0);
|
||||
CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0, uint8_t alpha = UINT8_MAX);
|
||||
~CShowableAnim();
|
||||
|
||||
//set animation to group or part of group
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "CComponent.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CMessage.h"
|
||||
|
@ -524,9 +524,6 @@ CKeyboardFocusListener::CKeyboardFocusListener(CTextInput * textInput)
|
||||
void CKeyboardFocusListener::focusGot()
|
||||
{
|
||||
CSDL_Ext::startTextInput(&textInput->pos);
|
||||
#ifdef VCMI_ANDROID
|
||||
textInput->notifyAndroidTextInputChanged(textInput->text);
|
||||
#endif
|
||||
usageIndex++;
|
||||
}
|
||||
|
||||
@ -588,9 +585,6 @@ void CTextInput::keyPressed(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
redraw();
|
||||
cb(text);
|
||||
#ifdef VCMI_ANDROID
|
||||
notifyAndroidTextInputChanged(text);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,10 +598,6 @@ void CTextInput::setText(const std::string & nText, bool callCb)
|
||||
CLabel::setText(nText);
|
||||
if(callCb)
|
||||
cb(text);
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
notifyAndroidTextInputChanged(text);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
|
||||
@ -633,10 +623,6 @@ void CTextInput::textInputed(const SDL_TextInputEvent & event)
|
||||
cb(text);
|
||||
}
|
||||
newText.clear();
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
notifyAndroidTextInputChanged(text);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTextInput::textEdited(const SDL_TextEditingEvent & event)
|
||||
@ -647,11 +633,6 @@ void CTextInput::textEdited(const SDL_TextEditingEvent & event)
|
||||
newText = event.text;
|
||||
redraw();
|
||||
cb(text + newText);
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
auto editedText = text + newText;
|
||||
notifyAndroidTextInputChanged(editedText);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CTextInput::filenameFilter(std::string & text, const std::string &)
|
||||
@ -698,24 +679,6 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
void CTextInput::notifyAndroidTextInputChanged(std::string & text)
|
||||
{
|
||||
if(!focus)
|
||||
return;
|
||||
|
||||
auto fun = [&text](JNIEnv * env, jclass cls, jmethodID method)
|
||||
{
|
||||
auto jtext = env->NewStringUTF(text.c_str());
|
||||
env->CallStaticVoidMethod(cls, method, jtext);
|
||||
env->DeleteLocalRef(jtext);
|
||||
};
|
||||
CAndroidVMHelper vmHelper;
|
||||
vmHelper.callCustomMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "notifyTextInputChanged",
|
||||
"(Ljava/lang/String;)V", fun, true);
|
||||
}
|
||||
#endif //VCMI_ANDROID
|
||||
|
||||
CFocusable::CFocusable()
|
||||
:CFocusable(std::make_shared<IFocusListener>())
|
||||
{
|
||||
|
@ -212,9 +212,6 @@ class CTextInput : public CLabel, public CFocusable
|
||||
protected:
|
||||
std::string visibleText() override;
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
void notifyAndroidTextInputChanged(std::string & text);
|
||||
#endif
|
||||
public:
|
||||
CFunctionList<void(const std::string &)> cb;
|
||||
CFunctionList<void(std::string &, const std::string &)> filters;
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "../mapHandler.h"
|
||||
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
@ -53,6 +53,7 @@
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
|
||||
#define ADVOPT (conf.go()->ac)
|
||||
using namespace CSDL_Ext;
|
||||
@ -1038,7 +1039,7 @@ void CAdvMapInt::show(SDL_Surface * to)
|
||||
{
|
||||
++heroAnim;
|
||||
}
|
||||
if(animValHitCount == 8)
|
||||
if(animValHitCount >= 8)
|
||||
{
|
||||
CGI->mh->updateWater();
|
||||
animValHitCount = 0;
|
||||
@ -1414,7 +1415,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView)
|
||||
auto pos = sel->visitablePos();
|
||||
auto tile = LOCPLINT->cb->getTile(pos);
|
||||
if(tile)
|
||||
CCS->musich->playMusicFromSet("terrain", tile->terType->name, true, false);
|
||||
CCS->musich->playMusicFromSet("terrain", tile->terType->getJsonKey(), true, false);
|
||||
}
|
||||
if(centerView)
|
||||
centerOn(sel);
|
||||
|
@ -44,15 +44,29 @@
|
||||
|
||||
CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town, const CStructure * Str)
|
||||
: CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE),
|
||||
parent(Par),
|
||||
town(Town),
|
||||
str(Str),
|
||||
stateCounter(80)
|
||||
parent(Par),
|
||||
town(Town),
|
||||
str(Str),
|
||||
stateTimeCounter(BUILD_ANIMATION_FINISHED_TIMEPOINT)
|
||||
{
|
||||
addUsedEvents(LCLICK | RCLICK | HOVER);
|
||||
pos.x += str->pos.x;
|
||||
pos.y += str->pos.y;
|
||||
|
||||
// special animation frame manipulation for castle shipyard with and without ship
|
||||
// done due to .def used in special way, not to animate building - first image is for shipyard without citadel moat, 2nd image is for including moat
|
||||
if(Town->town->faction->getId() == FactionID::CASTLE && Str->building &&
|
||||
(Str->building->bid == BuildingID::SHIPYARD || Str->building->bid == BuildingID::SHIP))
|
||||
{
|
||||
if(Town->hasBuilt(BuildingID::CITADEL))
|
||||
{
|
||||
this->first = 1;
|
||||
this->frame = 1;
|
||||
}
|
||||
else
|
||||
this->last = 0;
|
||||
}
|
||||
|
||||
if(!str->borderName.empty())
|
||||
border = BitmapHandler::loadBitmap(str->borderName);
|
||||
else
|
||||
@ -154,16 +168,11 @@ SDL_Color multiplyColors(const SDL_Color & b, const SDL_Color & a, double f)
|
||||
|
||||
void CBuildingRect::show(SDL_Surface * to)
|
||||
{
|
||||
const ui32 stageDelay = 16;
|
||||
uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT;
|
||||
|
||||
const ui32 S1_TRANSP = 16; //0.5 sec building appear 0->100 transparency
|
||||
const ui32 S2_WHITE_B = 32; //0.5 sec border glows from white to yellow
|
||||
const ui32 S3_YELLOW_B= 48; //0.5 sec border glows from yellow to normal
|
||||
const ui32 BUILDED = 80; // 1 sec delay, nothing happens
|
||||
|
||||
if(stateCounter < S1_TRANSP)
|
||||
if(stateTimeCounter < BUILDING_APPEAR_TIMEPOINT)
|
||||
{
|
||||
setAlpha(255*stateCounter/stageDelay);
|
||||
setAlpha(255 * stateTimeCounter / stageDelay);
|
||||
CShowableAnim::show(to);
|
||||
}
|
||||
else
|
||||
@ -172,9 +181,9 @@ void CBuildingRect::show(SDL_Surface * to)
|
||||
CShowableAnim::show(to);
|
||||
}
|
||||
|
||||
if(border && stateCounter > S1_TRANSP)
|
||||
if(border && stateTimeCounter > BUILDING_APPEAR_TIMEPOINT)
|
||||
{
|
||||
if(stateCounter == BUILDED)
|
||||
if(stateTimeCounter >= BUILD_ANIMATION_FINISHED_TIMEPOINT)
|
||||
{
|
||||
if(parent->selectedBuilding == this)
|
||||
blitAtLoc(border,0,0,to);
|
||||
@ -191,11 +200,11 @@ void CBuildingRect::show(SDL_Surface * to)
|
||||
SDL_Color oldColor = border->format->palette->colors[colorID];
|
||||
SDL_Color newColor;
|
||||
|
||||
if (stateCounter < S2_WHITE_B)
|
||||
newColor = multiplyColors(c1, c2, static_cast<double>(stateCounter % stageDelay) / stageDelay);
|
||||
if (stateTimeCounter < BUILDING_WHITE_BORDER_TIMEPOINT)
|
||||
newColor = multiplyColors(c1, c2, static_cast<double>(stateTimeCounter % stageDelay) / stageDelay);
|
||||
else
|
||||
if (stateCounter < S3_YELLOW_B)
|
||||
newColor = multiplyColors(c2, c3, static_cast<double>(stateCounter % stageDelay) / stageDelay);
|
||||
if (stateTimeCounter < BUILDING_YELLOW_BORDER_TIMEPOINT)
|
||||
newColor = multiplyColors(c2, c3, static_cast<double>(stateTimeCounter % stageDelay) / stageDelay);
|
||||
else
|
||||
newColor = oldColor;
|
||||
|
||||
@ -204,13 +213,13 @@ void CBuildingRect::show(SDL_Surface * to)
|
||||
SDL_SetColors(border, &oldColor, colorID, 1);
|
||||
}
|
||||
}
|
||||
if(stateCounter < BUILDED)
|
||||
stateCounter++;
|
||||
if(stateTimeCounter < BUILD_ANIMATION_FINISHED_TIMEPOINT)
|
||||
stateTimeCounter += GH.mainFPSmng->getElapsedMilliseconds();
|
||||
}
|
||||
|
||||
void CBuildingRect::showAll(SDL_Surface * to)
|
||||
{
|
||||
if (stateCounter == 0)
|
||||
if (stateTimeCounter == 0)
|
||||
return;
|
||||
|
||||
CShowableAnim::showAll(to);
|
||||
@ -632,9 +641,9 @@ void CCastleBuildings::addBuilding(BuildingID building)
|
||||
{
|
||||
//reset animation
|
||||
if(structures.size() == 1)
|
||||
buildingRect->stateCounter = 0; // transparency -> fully visible stage
|
||||
buildingRect->stateTimeCounter = 0; // transparency -> fully visible stage
|
||||
else
|
||||
buildingRect->stateCounter = 16; // already in fully visible stage
|
||||
buildingRect->stateTimeCounter = CBuildingRect::BUILDING_APPEAR_TIMEPOINT; // already in fully visible stage
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,14 @@ class CBuildingRect : public CShowableAnim
|
||||
{
|
||||
std::string getSubtitle();
|
||||
public:
|
||||
enum EBuildingCreationAnimationPhases : uint32_t
|
||||
{
|
||||
BUILDING_APPEAR_TIMEPOINT = 500, //500 msec building appears: 0->100% transparency
|
||||
BUILDING_WHITE_BORDER_TIMEPOINT = 1000, //500 msec border glows from white to yellow
|
||||
BUILDING_YELLOW_BORDER_TIMEPOINT = 1500, //500 msec border glows from yellow to normal
|
||||
BUILD_ANIMATION_FINISHED_TIMEPOINT = 2500 //1000 msec delay, nothing happens
|
||||
};
|
||||
|
||||
/// returns building associated with this structure
|
||||
const CBuilding * getBuilding();
|
||||
|
||||
@ -51,7 +59,7 @@ public:
|
||||
SDL_Surface* border;
|
||||
SDL_Surface* area;
|
||||
|
||||
ui32 stateCounter;//For building construction - current stage in animation
|
||||
ui32 stateTimeCounter;//For building construction - current stage in animation
|
||||
|
||||
CBuildingRect(CCastleBuildings * Par, const CGTownInstance *Town, const CStructure *Str);
|
||||
~CBuildingRect();
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "CAdvmapInterface.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../widgets/Images.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
@ -188,7 +188,7 @@ void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState)
|
||||
aw->arts->markPossibleSlots(art);
|
||||
|
||||
//aw->arts->commonInfo->dst.AOH = aw->arts;
|
||||
CCS->curh->dragAndDropCursor(std::make_unique<CAnimImage>("artifact", art->artType->iconIndex));
|
||||
CCS->curh->dragAndDropCursor("artifact", art->artType->iconIndex);
|
||||
|
||||
aw->arts->artifactsOnAltar.erase(art);
|
||||
setID(-1);
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "../gui/SDL_Pixels.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
@ -1247,12 +1247,6 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
updateWidgets();
|
||||
}
|
||||
|
||||
CExchangeWindow::~CExchangeWindow()
|
||||
{
|
||||
artifs[0]->commonInfo = nullptr;
|
||||
artifs[1]->commonInfo = nullptr;
|
||||
}
|
||||
|
||||
const CGarrisonSlot * CExchangeWindow::getSelectedSlotID() const
|
||||
{
|
||||
return garr->getSelection();
|
||||
|
@ -379,7 +379,6 @@ public:
|
||||
const CGarrisonSlot * getSelectedSlotID() const;
|
||||
|
||||
CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID);
|
||||
~CExchangeWindow();
|
||||
};
|
||||
|
||||
/// Here you can buy ships
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "../gui/SDL_Pixels.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
|
@ -192,8 +192,10 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/ObstacleHandler.cpp
|
||||
${MAIN_LIB_DIR}/StartInfo.cpp
|
||||
${MAIN_LIB_DIR}/ResourceSet.cpp
|
||||
${MAIN_LIB_DIR}/RiverHandler.cpp
|
||||
${MAIN_LIB_DIR}/RoadHandler.cpp
|
||||
${MAIN_LIB_DIR}/ScriptHandler.cpp
|
||||
${MAIN_LIB_DIR}/Terrain.cpp
|
||||
${MAIN_LIB_DIR}/TerrainHandler.cpp
|
||||
${MAIN_LIB_DIR}/VCMIDirs.cpp
|
||||
${MAIN_LIB_DIR}/VCMI_Lib.cpp
|
||||
|
||||
@ -441,11 +443,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/ObstacleHandler.h
|
||||
${MAIN_LIB_DIR}/PathfinderUtil.h
|
||||
${MAIN_LIB_DIR}/ResourceSet.h
|
||||
${MAIN_LIB_DIR}/RiverHandler.h
|
||||
${MAIN_LIB_DIR}/RoadHandler.h
|
||||
${MAIN_LIB_DIR}/ScriptHandler.h
|
||||
${MAIN_LIB_DIR}/ScopeGuard.h
|
||||
${MAIN_LIB_DIR}/StartInfo.h
|
||||
${MAIN_LIB_DIR}/StringConstants.h
|
||||
${MAIN_LIB_DIR}/Terrain.h
|
||||
${MAIN_LIB_DIR}/TerrainHandler.h
|
||||
${MAIN_LIB_DIR}/UnlockGuard.h
|
||||
${MAIN_LIB_DIR}/VCMIDirs.h
|
||||
${MAIN_LIB_DIR}/vcmi_endian.h
|
||||
|
@ -13,7 +13,7 @@ class VCMI(ConanFile):
|
||||
"minizip/[~1.2.12]",
|
||||
"onetbb/[^2021.3]", # Nullkiller AI
|
||||
"qt/[~5.15.2]", # launcher
|
||||
"sdl/[~2.24.0]",
|
||||
"sdl/[~2.26.1 || >=2.0.20 <=2.22.0]", # versions in between have broken sound
|
||||
"sdl_image/[~2.0.5]",
|
||||
"sdl_mixer/[~2.0.4]",
|
||||
"sdl_ttf/[~2.0.18]",
|
||||
|
@ -10,6 +10,9 @@
|
||||
"hero" : 156,
|
||||
"spell" : 81,
|
||||
"object" : 256,
|
||||
"terrain" : 10,
|
||||
"river" : 5,
|
||||
"road" : 4,
|
||||
"mapVersion" : 28 // max supported version, SoD
|
||||
},
|
||||
|
||||
|
@ -84,7 +84,7 @@
|
||||
"mageGuild3": { "animation" : "TBCSMAG3.def", "x" : 704, "y" : 107, "z" : 1, "border" : "TOCSM301.bmp", "area" : "TZCSM301.bmp" },
|
||||
"mageGuild4": { "animation" : "TBCSMAG4.def", "x" : 704, "y" : 76, "z" : 1, "border" : "TOCSM401.bmp", "area" : "TZCSM401.bmp" },
|
||||
"tavern": { "animation" : "TBCSTVRN.def", "x" : 0, "y" : 230, "z" : 1, "border" : "TOCSTAV1.bmp", "area" : "TZCSTAV1.bmp" },
|
||||
"shipyard": { "animation" : "TBCSDOCK.def", "x" : 478, "y" : 134, "border" : "TOCSDKMS.bmp", "area" : "TZCSDKMS.bmp" },
|
||||
"shipyard": { "animation" : "TBCSDOCK.def", "x" : 478, "y" : 134, "z" : 1, "border" : "TOCSDKMS.bmp", "area" : "TZCSDKMS.bmp" },
|
||||
"fort": { "animation" : "TBCSCSTL.def", "x" : 595, "y" : 66, "border" : "TOCSCAS1.bmp", "area" : "TZCSCAS1.bmp" },
|
||||
"citadel": { "animation" : "TBCSCAS2.def", "x" : 478, "y" : 66, "border" : "TOCSCAS2.bmp", "area" : "TZCSCAS2.bmp" },
|
||||
"castle": { "animation" : "TBCSCAS3.def", "x" : 478, "y" : 37, "border" : "TOCSCAS3.bmp", "area" : "TZCSCAS3.bmp" },
|
||||
@ -98,7 +98,7 @@
|
||||
"special1": { "animation" : "TBCSSPEC.def", "x" : 533, "y" : 71, "border" : "TOCSLT01.bmp", "area" : "TZCSLT01.bmp" },
|
||||
"horde1": { "animation" : "TBCSHRD1.def", "x" : 76, "y" : 53, "z" : -1, "border" : "TOCSGR1H.bmp", "area" : "TZCSGR1H.bmp", "hidden" : true },
|
||||
"horde1Upgr": { "animation" : "TBCSHRD2.def", "x" : 76, "y" : 35, "z" : -1, "border" : "TOCSGR2H.bmp", "area" : "TZCSGR2H.bmp", "hidden" : true, "builds" : "horde1" },
|
||||
"ship": { "animation" : "TBCSBOAT.def", "x" : 478, "y" : 134, "border" : "TOCSDKMN.bmp", "area" : "TZCSDKMN.bmp", "hidden" : true },
|
||||
"ship": { "animation" : "TBCSBOAT.def", "x" : 478, "y" : 134, "z" : 1, "border" : "TOCSDKMN.bmp", "area" : "TZCSDKMN.bmp", "hidden" : true },
|
||||
"special2": { "animation" : "TBCSEXT0.def", "x" : 384, "y" : 193, "z" : -2, "border" : "TOCSCAVM.bmp", "area" : "TZCSCAVM.bmp" },
|
||||
"special3": { "animation" : "TBCSEXT1.def", "x" : 0, "y" : 198, "z" : 1, "border" : "TOCSTAV2.bmp", "area" : "TZCSTAV2.bmp" },
|
||||
"grail": { "animation" : "TBCSHOLY.def", "x" : 456, "y" : 109, "z" : -1, "border" : "TOCSHOLY.bmp", "area" : "TZCSHOLY.bmp" },
|
||||
|
@ -3,6 +3,7 @@
|
||||
{
|
||||
"name" : "Neutral",
|
||||
"index" : 9,
|
||||
"nativeTerrain" : "none",
|
||||
"alignment" : "neutral",
|
||||
"creatureBackground" :
|
||||
{
|
||||
|
@ -85,6 +85,14 @@
|
||||
[
|
||||
"config/terrains.json"
|
||||
],
|
||||
"roads":
|
||||
[
|
||||
"config/roads.json"
|
||||
],
|
||||
"rivers":
|
||||
[
|
||||
"config/rivers.json"
|
||||
],
|
||||
"battlefields":
|
||||
[
|
||||
"config/battlefields.json"
|
||||
|
@ -42,4 +42,4 @@
|
||||
"value" : [2000, 5333, 8666, 12000],
|
||||
"rewardValue" : [5000, 10000, 15000, 20000]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,34 @@
|
||||
{
|
||||
"waterRiver":
|
||||
{
|
||||
"originalRiverId": 1,
|
||||
"code": "rw", //must be 2 characters
|
||||
"animation": "clrrvr",
|
||||
"index": 1,
|
||||
"text" : "Water river",
|
||||
"shortIdentifier": "rw", //must be 2 characters
|
||||
"tilesFilename": "clrrvr",
|
||||
"delta": "clrdelt"
|
||||
},
|
||||
"iceRiver":
|
||||
{
|
||||
"originalRiverId": 2,
|
||||
"code": "ri",
|
||||
"animation": "icyrvr",
|
||||
"index": 2,
|
||||
"text" : "Ice river",
|
||||
"shortIdentifier": "ri",
|
||||
"tilesFilename": "icyrvr",
|
||||
"delta": "icedelt"
|
||||
},
|
||||
"mudRiver":
|
||||
{
|
||||
"originalRiverId": 3,
|
||||
"code": "rm",
|
||||
"animation": "mudrvr",
|
||||
"index": 3,
|
||||
"text" : "Mud river",
|
||||
"shortIdentifier": "rm",
|
||||
"tilesFilename": "mudrvr",
|
||||
"delta": "muddelt"
|
||||
},
|
||||
"lavaRiver":
|
||||
{
|
||||
"originalRiverId": 4,
|
||||
"code": "rl",
|
||||
"animation": "lavrvr",
|
||||
"index": 4,
|
||||
"text" : "Lava river",
|
||||
"shortIdentifier": "rl",
|
||||
"tilesFilename": "lavrvr",
|
||||
"delta": "lavdelt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,26 @@
|
||||
{
|
||||
"dirtRoad":
|
||||
{
|
||||
"originalRoadId": 1,
|
||||
"code": "pd", //must be 2 characters
|
||||
"animation": "dirtrd",
|
||||
"index": 1,
|
||||
"text" : "Dirt road",
|
||||
"shortIdentifier": "pd", //must be 2 characters
|
||||
"tilesFilename": "dirtrd",
|
||||
"moveCost": 75
|
||||
},
|
||||
"gravelRoad":
|
||||
{
|
||||
"originalRoadId": 2,
|
||||
"code": "pg",
|
||||
"animation": "gravrd",
|
||||
"index": 2,
|
||||
"text" : "Gravel road",
|
||||
"shortIdentifier": "pg",
|
||||
"tilesFilename": "gravrd",
|
||||
"moveCost": 65
|
||||
},
|
||||
"cobblestoneRoad":
|
||||
{
|
||||
"originalRoadId": 3,
|
||||
"code": "pc",
|
||||
"animation": "cobbrd",
|
||||
"index": 3,
|
||||
"text" : "Cobblestone road",
|
||||
"shortIdentifier": "pc",
|
||||
"tilesFilename": "cobbrd",
|
||||
"moveCost": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"title" : "VCMI faction format",
|
||||
"description": "Json format for defining new faction (aka towns) in VCMI",
|
||||
"required" : [ "name", "alignment", "creatureBackground" ],
|
||||
"required" : [ "name", "alignment", "creatureBackground", "nativeTerrain" ],
|
||||
"dependencies" : {
|
||||
"town" : [ "puzzleMap" ]
|
||||
},
|
||||
|
@ -4,7 +4,41 @@
|
||||
"title" : "VCMI mod file format",
|
||||
"description" : "Format used to define main mod file (mod.json) in VCMI",
|
||||
"required" : [ "name", "description", "version", "author", "contact", "modType" ],
|
||||
"definitions" : {
|
||||
"localizable" : {
|
||||
"type":"object",
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "name", "description", "author", "modType" ],
|
||||
"properties":{
|
||||
"name": {
|
||||
"type":"string",
|
||||
"description": "Short name of your mod. No more than 2-3 words"
|
||||
},
|
||||
"description": {
|
||||
"type":"string",
|
||||
"description": "More lengthy description of mod. No hard limit"
|
||||
},
|
||||
|
||||
"modType" : {
|
||||
"type":"string",
|
||||
"description": "Type of mod, e.g. Town, Artifacts, Graphical."
|
||||
},
|
||||
"author" : {
|
||||
"type":"string",
|
||||
"description": "Author of the mod. Can be nickname, real name or name of team"
|
||||
},
|
||||
"changelog" : {
|
||||
"type":"object",
|
||||
"description": "List of changes/new features in each version",
|
||||
"additionalProperties" : {
|
||||
"type" : "array",
|
||||
"items" : { "type":"string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"additionalProperties" : false,
|
||||
"properties":{
|
||||
"name": {
|
||||
@ -78,6 +112,26 @@
|
||||
"type":"boolean",
|
||||
"description": "If set to true, mod will not be enabled automatically on install"
|
||||
},
|
||||
|
||||
"english" : {
|
||||
"$ref" : "#/definitions/localizable"
|
||||
},
|
||||
|
||||
"german" : {
|
||||
"$ref" : "#/definitions/localizable"
|
||||
},
|
||||
|
||||
"polish" : {
|
||||
"$ref" : "#/definitions/localizable"
|
||||
},
|
||||
|
||||
"russian" : {
|
||||
"$ref" : "#/definitions/localizable"
|
||||
},
|
||||
|
||||
"ukrainian" : {
|
||||
"$ref" : "#/definitions/localizable"
|
||||
},
|
||||
|
||||
"artifacts": {
|
||||
"type":"array",
|
||||
|
37
config/schemas/river.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"type":"object",
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"title" : "VCMI river format",
|
||||
"description" : "Format used to define new rivers in VCMI",
|
||||
"required" : [ "text", "shortIdentifier", "tilesFilename", "delta" ],
|
||||
|
||||
"additionalProperties" : false,
|
||||
"properties":{
|
||||
"index" :
|
||||
{
|
||||
"type": "number",
|
||||
"description": "Internal, do not use"
|
||||
},
|
||||
"text":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Human-readable name of the river"
|
||||
},
|
||||
"shortIdentifier":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Two-letters unique indentifier for this road. Used in map format"
|
||||
},
|
||||
"tilesFilename":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Name of file with river graphics",
|
||||
"format": "defFile"
|
||||
},
|
||||
"delta":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Name of file with river delta graphics"
|
||||
}
|
||||
}
|
||||
}
|
37
config/schemas/road.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"type":"object",
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"title" : "VCMI road format",
|
||||
"description" : "Format used to define new roads in VCMI",
|
||||
"required" : [ "text", "shortIdentifier", "tilesFilename", "moveCost" ],
|
||||
|
||||
"additionalProperties" : false,
|
||||
"properties":{
|
||||
"index" :
|
||||
{
|
||||
"type": "number",
|
||||
"description": "Internal, do not use"
|
||||
},
|
||||
"text":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Human-readable name of the road"
|
||||
},
|
||||
"shortIdentifier":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Two-letters unique indentifier for this road. Used in map format"
|
||||
},
|
||||
"tilesFilename":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Name of file with road graphics",
|
||||
"format": "defFile"
|
||||
},
|
||||
"moveCost":
|
||||
{
|
||||
"type": "number",
|
||||
"description": "How many movement points needed to move hero"
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@
|
||||
},
|
||||
"encoding" : {
|
||||
"type" : "string",
|
||||
"default" : "CP1252"
|
||||
"default" : "auto"
|
||||
},
|
||||
"swipe" : {
|
||||
"type" : "boolean",
|
||||
@ -53,6 +53,7 @@
|
||||
},
|
||||
"language" : {
|
||||
"type":"string",
|
||||
"enum" : [ "english", "german", "polish", "russian", "ukrainian" ],
|
||||
"default" : "english"
|
||||
},
|
||||
"lastSave" : {
|
||||
@ -81,7 +82,7 @@
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"default": {},
|
||||
"required" : [ "screenRes", "bitsPerPixel", "fullscreen", "realFullscreen", "spellbookAnimation","driver", "showIntro", "displayIndex" ],
|
||||
"required" : [ "screenRes", "bitsPerPixel", "fullscreen", "realFullscreen", "softwareCursor", "spellbookAnimation", "driver", "showIntro", "displayIndex" ],
|
||||
"properties" : {
|
||||
"screenRes" : {
|
||||
"type" : "object",
|
||||
@ -105,6 +106,10 @@
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
"softwareCursor" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
},
|
||||
"showIntro" : {
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
|
@ -3,10 +3,20 @@
|
||||
"$schema": "http://json-schema.org/draft-04/schema",
|
||||
"title" : "VCMI terrain format",
|
||||
"description" : "Format used to define new terrains in VCMI",
|
||||
"required" : [ "tiles", "code", "moveCost" ],
|
||||
"required" : [ "text", "moveCost", "minimapUnblocked", "minimapBlocked", "music", "tiles", "type", "horseSound", "horseSoundPenalty", "shortIdentifier", "battleFields" ],
|
||||
|
||||
"additionalProperties" : false,
|
||||
"properties":{
|
||||
"index" :
|
||||
{
|
||||
"type": "number",
|
||||
"description": "Internal, do not use"
|
||||
},
|
||||
"text":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Human-readable name of this terrain"
|
||||
},
|
||||
"moveCost":
|
||||
{
|
||||
"type": "number",
|
||||
@ -47,35 +57,38 @@
|
||||
},
|
||||
"type":
|
||||
{
|
||||
"type": "string",
|
||||
"type": "array",
|
||||
"description": "Type of this terrain. Can be land, water, subterranean or rock",
|
||||
"enum": ["LAND", "WATER", "SUB", "ROCK"]
|
||||
"items":
|
||||
{
|
||||
"enum": ["LAND", "WATER", "SUB", "ROCK", "SURFACE"],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"rockTerrain":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The name of tock type terrain which will be used as borders in the underground"
|
||||
"description": "The name of rock type terrain which will be used as borders in the underground"
|
||||
},
|
||||
"river":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "River type which should be used for that terrain",
|
||||
"enum": ["", "rw", "ri", "rm", "rl"]
|
||||
"description": "River type which should be used for that terrain"
|
||||
},
|
||||
"horseSoundId":
|
||||
{
|
||||
"type": "number",
|
||||
"description": "Id of horse sound to be played when hero is moving across terrain"
|
||||
},
|
||||
"text":
|
||||
"horseSound":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Text to be shown when mouse if over terrain"
|
||||
"description": "Hero movement sound for this terrain, version for moving on tiles with road"
|
||||
},
|
||||
"code":
|
||||
"horseSoundPenalty":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Two-letters unique indentifier for this terrain. Used for terrain serializaion"
|
||||
"description": "Hero movement sound for this terrain, version for moving on tiles without road"
|
||||
},
|
||||
"shortIdentifier":
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Two-letters unique indentifier for this terrain. Used for map format"
|
||||
},
|
||||
"battleFields":
|
||||
{
|
||||
@ -86,6 +99,24 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"sounds":
|
||||
{
|
||||
"type": "object",
|
||||
"description": "list of sounds for this terrain",
|
||||
"additionalProperties" : false,
|
||||
"properties":
|
||||
{
|
||||
"ambient" :
|
||||
{
|
||||
"type": "array",
|
||||
"description": "list of ambient sounds for this terrain",
|
||||
"items":
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prohibitTransitions":
|
||||
{
|
||||
"type": "array",
|
||||
|
@ -1,146 +1,162 @@
|
||||
{
|
||||
"dirt" :
|
||||
{
|
||||
"originalTerrainId": 0,
|
||||
"index": 0,
|
||||
"moveCost" : 100,
|
||||
"minimapUnblocked" : [ 82, 56, 8 ],
|
||||
"minimapBlocked" : [ 57, 40, 8 ],
|
||||
"music" : "Dirt.mp3",
|
||||
"tiles" : "DIRTTL",
|
||||
"code" : "dt",
|
||||
"river" : "rm",
|
||||
"type" : ["SURFACE"],
|
||||
"shortIdentifier" : "dt",
|
||||
"river" : "mudRiver",
|
||||
"battleFields" : ["dirt_birches", "dirt_hills", "dirt_pines"],
|
||||
"terrainViewPatterns" : "dirt",
|
||||
"horseSoundId" : 0
|
||||
"horseSound" : "horse00",
|
||||
"horseSoundPenalty" : "horse20"
|
||||
},
|
||||
"sand" :
|
||||
{
|
||||
"originalTerrainId": 1,
|
||||
"index": 1,
|
||||
"moveCost" : 150,
|
||||
"minimapUnblocked" : [ 222, 207, 140 ],
|
||||
"minimapBlocked" : [ 165, 158, 107 ],
|
||||
"music" : "Sand.mp3",
|
||||
"tiles" : "SANDTL",
|
||||
"code" : "sa",
|
||||
"river" : "rm",
|
||||
"type" : ["SURFACE"],
|
||||
"shortIdentifier" : "sa",
|
||||
"river" : "mudRiver",
|
||||
"battleFields" : ["sand_mesas"],
|
||||
"transitionRequired" : true,
|
||||
"terrainViewPatterns" : "sand",
|
||||
"horseSoundId" : 1
|
||||
"horseSound" : "horse01",
|
||||
"horseSoundPenalty" : "horse21"
|
||||
},
|
||||
"grass" :
|
||||
{
|
||||
"originalTerrainId": 2,
|
||||
"index": 2,
|
||||
"moveCost" : 100,
|
||||
"minimapUnblocked" : [ 0, 65, 0 ],
|
||||
"minimapBlocked" : [ 0, 48, 0 ],
|
||||
"music" : "Grass.mp3",
|
||||
"tiles" : "GRASTL",
|
||||
"code" : "gr",
|
||||
"river" : "rw",
|
||||
"type" : ["SURFACE"],
|
||||
"shortIdentifier" : "gr",
|
||||
"river" : "waterRiver",
|
||||
"battleFields" : ["grass_hills", "grass_pines"],
|
||||
"horseSoundId" : 2
|
||||
"horseSound" : "horse02",
|
||||
"horseSoundPenalty" : "horse22"
|
||||
},
|
||||
"snow" :
|
||||
{
|
||||
"originalTerrainId": 3,
|
||||
"index": 3,
|
||||
"moveCost" : 150,
|
||||
"minimapUnblocked" : [ 181, 199, 198 ],
|
||||
"minimapBlocked" : [ 140, 158, 156 ],
|
||||
"music" : "Snow.mp3",
|
||||
"tiles" : "SNOWTL",
|
||||
"code" : "sn",
|
||||
"river" : "ri",
|
||||
"type" : ["SURFACE"],
|
||||
"shortIdentifier" : "sn",
|
||||
"river" : "iceRiver",
|
||||
"battleFields" : ["snow_mountains", "snow_trees"],
|
||||
"horseSoundId" : 3
|
||||
"horseSound" : "horse03",
|
||||
"horseSoundPenalty" : "horse23"
|
||||
},
|
||||
"swamp" :
|
||||
{
|
||||
"originalTerrainId": 4,
|
||||
"index": 4,
|
||||
"moveCost" : 175,
|
||||
"minimapUnblocked" : [ 74, 134, 107 ],
|
||||
"minimapBlocked" : [ 33, 89, 66 ],
|
||||
"music" : "Swamp.mp3",
|
||||
"tiles" : "SWMPTL",
|
||||
"code" : "sw",
|
||||
"river" : "rw",
|
||||
"type" : ["SURFACE"],
|
||||
"shortIdentifier" : "sw",
|
||||
"river" : "waterRiver",
|
||||
"battleFields" : ["swamp_trees"],
|
||||
"horseSoundId" : 4
|
||||
"horseSound" : "horse04",
|
||||
"horseSoundPenalty" : "horse24"
|
||||
},
|
||||
"rough" :
|
||||
{
|
||||
"originalTerrainId": 5,
|
||||
"index": 5,
|
||||
"moveCost" : 125,
|
||||
"minimapUnblocked" : [ 132, 113, 49 ],
|
||||
"minimapBlocked" : [ 99, 81, 33 ],
|
||||
"music" : "Rough.mp3",
|
||||
"tiles" : "ROUGTL",
|
||||
"code" : "rg",
|
||||
"river" : "rm",
|
||||
"type" : ["SURFACE"],
|
||||
"shortIdentifier" : "rg",
|
||||
"river" : "mudRiver",
|
||||
"battleFields" : ["rough"],
|
||||
"horseSoundId" : 5
|
||||
"horseSound" : "horse05",
|
||||
"horseSoundPenalty" : "horse25"
|
||||
},
|
||||
"subterra" :
|
||||
{
|
||||
"originalTerrainId": 6,
|
||||
"index": 6,
|
||||
"moveCost" : 100,
|
||||
"minimapUnblocked" : [ 132, 48, 0 ],
|
||||
"minimapBlocked" : [ 90, 8, 0 ],
|
||||
"music" : "Underground.mp3",
|
||||
"tiles" : "SUBBTL",
|
||||
"type" : "SUB",
|
||||
"code" : "sb",
|
||||
"river" : "rw",
|
||||
"type" : [ "SUB" ],
|
||||
"shortIdentifier" : "sb",
|
||||
"river" : "waterRiver",
|
||||
"battleFields" : ["subterranean"],
|
||||
"rockTerrain" : "rock",
|
||||
"horseSoundId" : 6
|
||||
"horseSound" : "horse06",
|
||||
"horseSoundPenalty" : "horse26"
|
||||
},
|
||||
"lava" :
|
||||
{
|
||||
"originalTerrainId": 7,
|
||||
"index": 7,
|
||||
"moveCost" : 100,
|
||||
"minimapUnblocked" : [ 74, 73, 74 ],
|
||||
"minimapBlocked" : [ 41, 40, 41 ],
|
||||
"music" : "Lava.mp3",
|
||||
"tiles" : "LAVATL",
|
||||
"type" : ["SUB", "SURFACE"],
|
||||
"code" : "lv",
|
||||
"river" : "rl",
|
||||
"shortIdentifier" : "lv",
|
||||
"river" : "lavaRiver",
|
||||
"battleFields" : ["lava"],
|
||||
"rockTerrain" : "rock",
|
||||
"horseSoundId" : 7
|
||||
"horseSound" : "horse07",
|
||||
"horseSoundPenalty" : "horse27"
|
||||
},
|
||||
"water" :
|
||||
{
|
||||
"originalTerrainId": 8,
|
||||
"index": 8,
|
||||
"moveCost" : 100,
|
||||
"minimapUnblocked" : [ 8, 81, 148 ],
|
||||
"minimapBlocked" : [ 8, 81, 148 ],
|
||||
"music" : "Water.mp3",
|
||||
"tiles" : "WATRTL",
|
||||
"type" : "WATER",
|
||||
"code" : "wt",
|
||||
"type" : [ "WATER" ],
|
||||
"shortIdentifier" : "wt",
|
||||
"battleFields" : ["ship"],
|
||||
"transitionRequired" : true,
|
||||
"terrainViewPatterns" : "water",
|
||||
"horseSoundId" : 8,
|
||||
"horseSound" : "horse08",
|
||||
"horseSoundPenalty" : "horse28",
|
||||
"sounds": {
|
||||
"ambient": ["LOOPOCEA"]
|
||||
}
|
||||
},
|
||||
"rock" :
|
||||
{
|
||||
"originalTerrainId": 9,
|
||||
"index": 9,
|
||||
"moveCost" : -1,
|
||||
"minimapUnblocked" : [ 0, 0, 0 ],
|
||||
"minimapBlocked" : [ 0, 0, 0 ],
|
||||
"music" : "Underground.mp3", // Impossible in H3
|
||||
"tiles" : "ROCKTL",
|
||||
"type" : "ROCK",
|
||||
"code" : "rc",
|
||||
"type" : [ "ROCK" ],
|
||||
"shortIdentifier" : "rc",
|
||||
"battleFields" : ["rocklands"],
|
||||
"transitionRequired" : true,
|
||||
"terrainViewPatterns" : "rock",
|
||||
"horseSoundId" : 9
|
||||
"horseSound" : "horse09",
|
||||
"horseSoundPenalty" : "horse29"
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ The following platforms are supported and known to work, others might require ch
|
||||
|
||||
1. Check if your build environment can use the prebuilt binaries: basically, that your compiler version (or Xcode major version) matches the information below. If you're unsure, simply advance to the next step.
|
||||
|
||||
- macOS: libraries are built with Apple clang 13 (Xcode 13.4.1), should be consumable by Xcode and Xcode CLT 13.x
|
||||
- iOS: libraries are built with Apple clang 13 (Xcode 13.4.1), should be consumable by Xcode 13.x
|
||||
- macOS: libraries are built with Apple clang 14 (Xcode 14.2), should be consumable by Xcode and Xcode CLT 14.x (older library versions are also available for Xcode 13, see Releases in the respective repo)
|
||||
- iOS: libraries are built with Apple clang 14 (Xcode 14.2), should be consumable by Xcode 14.x (older library versions are also available for Xcode 13, see Releases in the respective repo)
|
||||
|
||||
2. Download the binaries archive and unpack it to `~/.conan` directory:
|
||||
|
||||
@ -85,7 +85,8 @@ conan install . \
|
||||
--no-imports \
|
||||
--build=never \
|
||||
--profile:build=default \
|
||||
--profile:host=CI/conan/macos-intel
|
||||
--profile:host=CI/conan/macos-intel \
|
||||
-o with_apple_system_libs=True
|
||||
|
||||
cmake -S . -B build -G Xcode \
|
||||
--toolchain conan-generated/conan_toolchain.cmake
|
||||
@ -116,7 +117,8 @@ conan install . \
|
||||
--no-imports \
|
||||
--build=never \
|
||||
--profile:build=default \
|
||||
--profile:host=CI/conan/ios-arm64
|
||||
--profile:host=CI/conan/ios-arm64 \
|
||||
-o with_apple_system_libs=True
|
||||
|
||||
cmake --preset ios-conan
|
||||
```
|
||||
|
@ -62,6 +62,13 @@ set(launcher_FORMS
|
||||
lobby/lobbyroomrequest_moc.ui
|
||||
)
|
||||
|
||||
set(launcher_TS
|
||||
translation/english.ts
|
||||
translation/german.ts
|
||||
translation/polish.ts
|
||||
translation/russian.ts
|
||||
translation/ukrainian.ts)
|
||||
|
||||
if(APPLE_IOS)
|
||||
list(APPEND launcher_SRCS
|
||||
ios/main.m
|
||||
@ -83,18 +90,28 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
if(TARGET Qt6::Core)
|
||||
qt_wrap_ui(launcher_UI_HEADERS ${launcher_FORMS})
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
set_source_files_properties(${launcher_TS} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/translation)
|
||||
# TODO: consider using qt_add_translations: https://doc.qt.io/qt-6/qtlinguist-cmake-qt-add-translations.html
|
||||
qt_add_translation( launcher_QM ${launcher_TS} )
|
||||
endif()
|
||||
else()
|
||||
qt5_wrap_ui(launcher_UI_HEADERS ${launcher_FORMS})
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
set_source_files_properties(${launcher_TS} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/translation)
|
||||
qt5_add_translation( launcher_QM ${launcher_TS} )
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(WIN32)
|
||||
set(launcher_ICON VCMI_launcher.rc)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SINGLE_APP_BUILD)
|
||||
add_library(vcmilauncher STATIC ${launcher_SRCS} ${launcher_HEADERS} ${launcher_UI_HEADERS})
|
||||
add_library(vcmilauncher STATIC ${launcher_QM} ${launcher_SRCS} ${launcher_HEADERS} ${launcher_UI_HEADERS})
|
||||
else()
|
||||
add_executable(vcmilauncher WIN32 ${launcher_SRCS} ${launcher_HEADERS} ${launcher_UI_HEADERS} ${launcher_ICON})
|
||||
add_executable(vcmilauncher WIN32 ${launcher_QM} ${launcher_SRCS} ${launcher_HEADERS} ${launcher_UI_HEADERS} ${launcher_ICON})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@ -126,7 +143,12 @@ vcmi_set_output_dir(vcmilauncher "")
|
||||
enable_pch(vcmilauncher)
|
||||
|
||||
if(APPLE_IOS)
|
||||
set(ICONS_DESTINATION ${DATA_DIR})
|
||||
set(RESOURCES_DESTINATION ${DATA_DIR})
|
||||
|
||||
# TODO: remove after fixing Conan's Qt recipe
|
||||
if(XCODE_VERSION VERSION_GREATER_EQUAL 14.0)
|
||||
target_link_libraries(vcmilauncher "-framework IOKit")
|
||||
endif()
|
||||
|
||||
# workaround https://github.com/conan-io/conan-center-index/issues/13332
|
||||
if(USING_CONAN)
|
||||
@ -140,12 +162,14 @@ if(APPLE_IOS)
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(ICONS_DESTINATION ${DATA_DIR}/launcher)
|
||||
set(RESOURCES_DESTINATION ${DATA_DIR}/launcher)
|
||||
|
||||
# Copy to build directory for easier debugging
|
||||
add_custom_command(TARGET vcmilauncher POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/launcher/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/launcher/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/icons
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/launcher/translation
|
||||
|
||||
)
|
||||
|
||||
install(TARGETS vcmilauncher DESTINATION ${BIN_DIR})
|
||||
@ -156,4 +180,8 @@ else()
|
||||
install(FILES "eu.vcmi.VCMI.metainfo.xml" DESTINATION share/metainfo)
|
||||
endif()
|
||||
endif()
|
||||
install(DIRECTORY icons DESTINATION ${ICONS_DESTINATION})
|
||||
|
||||
install(DIRECTORY icons DESTINATION ${RESOURCES_DESTINATION})
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
install(FILES ${launcher_QM} DESTINATION ${RESOURCES_DESTINATION}/translation)
|
||||
endif()
|
||||
|
BIN
launcher/icons/menu-editor.png
Normal file
After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 27 KiB |
@ -12,7 +12,7 @@
|
||||
#include <QTcpSocket>
|
||||
#include <QAbstractSocket>
|
||||
|
||||
const unsigned int ProtocolVersion = 3;
|
||||
const unsigned int ProtocolVersion = 4;
|
||||
const std::string ProtocolEncoding = "utf8";
|
||||
|
||||
class ProtocolError: public std::runtime_error
|
||||
@ -24,10 +24,10 @@ public:
|
||||
enum ProtocolConsts
|
||||
{
|
||||
//client consts
|
||||
GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART,
|
||||
GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART, HERE, ALIVE, HOSTMODE,
|
||||
|
||||
//server consts
|
||||
SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, START, STATUS, HOST, MODS, CLIENTMODS
|
||||
SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, START, STATUS, HOST, MODS, CLIENTMODS, USERS, HEALTH, GAMEMODE
|
||||
};
|
||||
|
||||
const QMap<ProtocolConsts, QString> ProtocolStrings
|
||||
@ -78,6 +78,16 @@ const QMap<ProtocolConsts, QString> ProtocolStrings
|
||||
//[unsupported] start session immediately
|
||||
//%1: room name
|
||||
{FORCESTART, "<FORCESTART>%1"},
|
||||
|
||||
//request user list
|
||||
{HERE, "<HERE>"},
|
||||
|
||||
//used as reponse to healcheck
|
||||
{ALIVE, "<ALIVE>"},
|
||||
|
||||
//host sets game mode (new game or load game)
|
||||
//%1: game mode - 0 for new game, 1 for load game
|
||||
{HOSTMODE, "<HOSTMODE>%1"},
|
||||
|
||||
//=== server commands ===
|
||||
//server commands are started from :>>, arguments are enumerated by : symbol
|
||||
@ -140,7 +150,20 @@ const QMap<ProtocolConsts, QString> ProtocolStrings
|
||||
//received chat message
|
||||
//arg[0]: sender username
|
||||
//arg[1]: message text
|
||||
{CHAT, "MSG"}
|
||||
{CHAT, "MSG"},
|
||||
|
||||
//list of users currently in lobby
|
||||
//arg[0]: amount of players, following arguments depend on it
|
||||
//arg[x]: username
|
||||
//arg[x+1]: room (empty if not in the room)
|
||||
{USERS, "USERS"},
|
||||
|
||||
//healthcheck from server
|
||||
{HEALTH, "HEALTH"},
|
||||
|
||||
//game mode (new game or load game) set by host
|
||||
//arg[0]: game mode
|
||||
{GAMEMODE, "GAMEMODE"},
|
||||
};
|
||||
|
||||
class ServerCommand
|
||||
|
@ -16,6 +16,18 @@
|
||||
#include "../modManager/cmodlist.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
enum GameMode
|
||||
{
|
||||
NEW_GAME = 0, LOAD_GAME = 1
|
||||
};
|
||||
|
||||
enum ModResolutionRoles
|
||||
{
|
||||
ModNameRole = Qt::UserRole + 1,
|
||||
ModEnableRole,
|
||||
ModResolvableRole
|
||||
};
|
||||
|
||||
Lobby::Lobby(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::Lobby)
|
||||
@ -35,6 +47,15 @@ Lobby::Lobby(QWidget *parent) :
|
||||
ui->kickButton->setVisible(false);
|
||||
}
|
||||
|
||||
void Lobby::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
Lobby::~Lobby()
|
||||
{
|
||||
delete ui;
|
||||
@ -134,10 +155,14 @@ void Lobby::serverCommand(const ServerCommand & command) try
|
||||
|
||||
if(args[1] == username)
|
||||
{
|
||||
hostModsMap.clear();
|
||||
ui->buttonReady->setText("Ready");
|
||||
ui->chat->clear(); //cleanup the chat
|
||||
ui->optNewGame->setChecked(true);
|
||||
sysMessage(joinStr.arg("you", args[0]));
|
||||
session = args[0];
|
||||
bool isHost = command.command == JOINED && hostSession == session;
|
||||
ui->optNewGame->setEnabled(isHost);
|
||||
ui->optLoadGame->setEnabled(isHost);
|
||||
ui->stackedWidget->setCurrentWidget(command.command == JOINED ? ui->roomPage : ui->sessionsPage);
|
||||
}
|
||||
else
|
||||
@ -152,33 +177,15 @@ void Lobby::serverCommand(const ServerCommand & command) try
|
||||
protocolAssert(amount * 2 == (args.size() - 1));
|
||||
|
||||
tagPoint = 1;
|
||||
ui->modsList->clear();
|
||||
auto enabledMods = buildModsMap();
|
||||
for(int i = 0; i < amount; ++i, tagPoint += 2)
|
||||
{
|
||||
if(enabledMods.contains(args[tagPoint]))
|
||||
{
|
||||
if(enabledMods[args[tagPoint]] == args[tagPoint + 1])
|
||||
enabledMods.remove(args[tagPoint]);
|
||||
else
|
||||
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-update.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1])));
|
||||
}
|
||||
else if(isModAvailable(args[tagPoint], args[tagPoint + 1]))
|
||||
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-enabled.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1])));
|
||||
else
|
||||
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-delete.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1])));
|
||||
}
|
||||
for(auto & remainMod : enabledMods.keys())
|
||||
{
|
||||
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-disabled.png"), QString("%1 (v%2)").arg(remainMod, enabledMods[remainMod])));
|
||||
}
|
||||
if(!ui->modsList->count())
|
||||
ui->modsList->addItem("No issues detected");
|
||||
hostModsMap[args[tagPoint]] = args[tagPoint + 1];
|
||||
|
||||
updateMods();
|
||||
break;
|
||||
}
|
||||
|
||||
case CLIENTMODS: {
|
||||
protocolAssert(args.size() > 1);
|
||||
protocolAssert(args.size() >= 1);
|
||||
amount = args[1].toInt();
|
||||
protocolAssert(amount * 2 == (args.size() - 2));
|
||||
|
||||
@ -217,6 +224,8 @@ void Lobby::serverCommand(const ServerCommand & command) try
|
||||
gameArgs << "--lobby";
|
||||
gameArgs << "--lobby-address" << serverUrl;
|
||||
gameArgs << "--lobby-port" << QString::number(serverPort);
|
||||
gameArgs << "--lobby-username" << username;
|
||||
gameArgs << "--lobby-gamemode" << QString::number(isLoadGameMode);
|
||||
gameArgs << "--uuid" << args[0];
|
||||
startGame(gameArgs);
|
||||
break;
|
||||
@ -238,6 +247,34 @@ void Lobby::serverCommand(const ServerCommand & command) try
|
||||
chatMessage(args[0], msg);
|
||||
break;
|
||||
}
|
||||
|
||||
case HEALTH: {
|
||||
socketLobby.send(ProtocolStrings[ALIVE]);
|
||||
break;
|
||||
}
|
||||
|
||||
case USERS: {
|
||||
protocolAssert(args.size() > 0);
|
||||
amount = args[0].toInt();
|
||||
|
||||
protocolAssert(amount == (args.size() - 1));
|
||||
ui->listUsers->clear();
|
||||
for(int i = 0; i < amount; ++i)
|
||||
{
|
||||
ui->listUsers->addItem(new QListWidgetItem(args[i + 1]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAMEMODE: {
|
||||
protocolAssert(args.size() == 1);
|
||||
isLoadGameMode = args[0].toInt();
|
||||
if(isLoadGameMode)
|
||||
ui->optLoadGame->setChecked(true);
|
||||
else
|
||||
ui->optNewGame->setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
sysMessage("Unknown server command");
|
||||
@ -291,7 +328,7 @@ void Lobby::onDisconnected()
|
||||
ui->userEdit->setEnabled(true);
|
||||
ui->newButton->setEnabled(false);
|
||||
ui->joinButton->setEnabled(false);
|
||||
ui->sessionsTable->clear();
|
||||
ui->sessionsTable->setRowCount(0);
|
||||
}
|
||||
|
||||
void Lobby::chatMessage(QString title, QString body, bool isSystem)
|
||||
@ -329,6 +366,7 @@ void Lobby::on_connectButton_toggled(bool checked)
|
||||
{
|
||||
if(checked)
|
||||
{
|
||||
ui->connectButton->setText(tr("Disconnect"));
|
||||
authentificationStatus = AuthStatus::AUTH_NONE;
|
||||
username = ui->userEdit->text();
|
||||
const int connectionTimeout = settings["launcher"]["connectionTimeout"].Integer();
|
||||
@ -360,12 +398,74 @@ void Lobby::on_connectButton_toggled(bool checked)
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->connectButton->setText(tr("Connect"));
|
||||
ui->serverEdit->setEnabled(true);
|
||||
ui->userEdit->setEnabled(true);
|
||||
ui->listUsers->clear();
|
||||
hostModsMap.clear();
|
||||
updateMods();
|
||||
socketLobby.disconnectServer();
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::updateMods()
|
||||
{
|
||||
ui->modsList->clear();
|
||||
if(hostModsMap.empty())
|
||||
return;
|
||||
|
||||
auto createModListWidget = [](const QIcon & icon, const QString & label, const QString & name, bool enableFlag, bool resolveFlag)
|
||||
{
|
||||
auto * lw = new QListWidgetItem(icon, label);
|
||||
lw->setData(ModResolutionRoles::ModNameRole, name);
|
||||
lw->setData(ModResolutionRoles::ModEnableRole, enableFlag);
|
||||
lw->setData(ModResolutionRoles::ModResolvableRole, resolveFlag);
|
||||
return lw;
|
||||
};
|
||||
|
||||
auto enabledMods = buildModsMap();
|
||||
for(const auto & mod : hostModsMap.keys())
|
||||
{
|
||||
auto & modValue = hostModsMap[mod];
|
||||
auto modName = QString("%1 (v%2)").arg(mod, modValue);
|
||||
if(enabledMods.contains(mod))
|
||||
{
|
||||
if(enabledMods[mod] == modValue)
|
||||
enabledMods.remove(mod); //mod fully matches, remove from list
|
||||
else
|
||||
{
|
||||
//mod version mismatch
|
||||
ui->modsList->addItem(createModListWidget(QIcon("icons:mod-update.png"), modName, mod, true, false));
|
||||
}
|
||||
}
|
||||
else if(isModAvailable(mod, modValue))
|
||||
{
|
||||
//mod is available and needs to be enabled
|
||||
ui->modsList->addItem(createModListWidget(QIcon("icons:mod-enabled.png"), modName, mod, true, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
//mod is not available and needs to be installed
|
||||
ui->modsList->addItem(createModListWidget(QIcon("icons:mod-delete.png"), modName, mod, true, false));
|
||||
}
|
||||
}
|
||||
for(const auto & remainMod : enabledMods.keys())
|
||||
{
|
||||
auto modName = QString("%1 (v%2)").arg(remainMod, enabledMods[remainMod]);
|
||||
//mod needs to be disabled
|
||||
ui->modsList->addItem(createModListWidget(QIcon("icons:mod-disabled.png"), modName, remainMod, false, true));
|
||||
}
|
||||
if(!ui->modsList->count())
|
||||
{
|
||||
ui->buttonResolve->setEnabled(false);
|
||||
ui->modsList->addItem(tr("No issues detected"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->buttonResolve->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::on_newButton_clicked()
|
||||
{
|
||||
new LobbyRoomRequest(socketLobby, "", buildModsMap(), this);
|
||||
@ -417,3 +517,50 @@ void Lobby::on_kickButton_clicked()
|
||||
socketLobby.send(ProtocolStrings[KICK].arg(ui->playersList->currentItem()->text()));
|
||||
}
|
||||
|
||||
|
||||
void Lobby::on_buttonResolve_clicked()
|
||||
{
|
||||
QStringList toEnableList, toDisableList;
|
||||
for(auto * item : ui->modsList->selectedItems())
|
||||
{
|
||||
auto modName = item->data(ModResolutionRoles::ModNameRole);
|
||||
if(modName.isNull())
|
||||
continue;
|
||||
|
||||
bool modToEnable = item->data(ModResolutionRoles::ModEnableRole).toBool();
|
||||
bool modToResolve = item->data(ModResolutionRoles::ModResolvableRole).toBool();
|
||||
|
||||
if(!modToResolve)
|
||||
continue;
|
||||
|
||||
if(modToEnable)
|
||||
toEnableList << modName.toString();
|
||||
else
|
||||
toDisableList << modName.toString();
|
||||
}
|
||||
|
||||
//disabling first, then enabling
|
||||
for(auto & mod : toDisableList)
|
||||
emit disableMod(mod);
|
||||
for(auto & mod : toEnableList)
|
||||
emit enableMod(mod);
|
||||
}
|
||||
|
||||
void Lobby::on_optNewGame_toggled(bool checked)
|
||||
{
|
||||
if(checked)
|
||||
{
|
||||
if(isLoadGameMode)
|
||||
socketLobby.send(ProtocolStrings[HOSTMODE].arg(GameMode::NEW_GAME));
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::on_optLoadGame_toggled(bool checked)
|
||||
{
|
||||
if(checked)
|
||||
{
|
||||
if(!isLoadGameMode)
|
||||
socketLobby.send(ProtocolStrings[HOSTMODE].arg(GameMode::LOAD_GAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,18 @@ class Lobby : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void changeEvent(QEvent *event) override;
|
||||
public:
|
||||
explicit Lobby(QWidget *parent = nullptr);
|
||||
~Lobby();
|
||||
|
||||
signals:
|
||||
|
||||
void enableMod(QString mod);
|
||||
void disableMod(QString mod);
|
||||
|
||||
public slots:
|
||||
void updateMods();
|
||||
|
||||
private slots:
|
||||
void on_messageEdit_returnPressed();
|
||||
@ -49,9 +58,16 @@ private slots:
|
||||
|
||||
void on_kickButton_clicked();
|
||||
|
||||
void on_buttonResolve_clicked();
|
||||
|
||||
void on_optNewGame_toggled(bool checked);
|
||||
|
||||
void on_optLoadGame_toggled(bool checked);
|
||||
|
||||
private:
|
||||
QString serverUrl;
|
||||
int serverPort;
|
||||
bool isLoadGameMode = false;
|
||||
|
||||
Ui::Lobby *ui;
|
||||
SocketLobby socketLobby;
|
||||
@ -59,6 +75,7 @@ private:
|
||||
QString session;
|
||||
QString username;
|
||||
QStringList gameArgs;
|
||||
QMap<QString, QString> hostModsMap;
|
||||
|
||||
enum AuthStatus
|
||||
{
|
||||
|
@ -7,13 +7,26 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>652</width>
|
||||
<height>329</height>
|
||||
<height>383</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QPushButton" name="connectButton">
|
||||
<property name="sizePolicy">
|
||||
@ -30,36 +43,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QLineEdit" name="messageEdit"/>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLineEdit" name="userEdit"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="serverEdit">
|
||||
<property name="text">
|
||||
<string>127.0.0.1:5002</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QPlainTextEdit" name="chat">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
<string notr="true">127.0.0.1:5002</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -70,144 +57,250 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="3" rowspan="2" colspan="3">
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<item row="0" column="4">
|
||||
<widget class="QLineEdit" name="userEdit"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="6">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="sessionsPage">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QTableWidget" name="sessionsTable">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>80</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>20</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>20</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Session</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Players</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="newButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>New room</string>
|
||||
<string>People in lobby</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="joinButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listUsers">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Join room</string>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>96</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="roomPage">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="buttonReady">
|
||||
<property name="text">
|
||||
<string>Ready</string>
|
||||
<property name="midLineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Mods mismatch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QPushButton" name="buttonLeave">
|
||||
<property name="text">
|
||||
<string>Leave</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QListWidget" name="modsList">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QListWidget" name="playersList">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::SinglePass</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="kickButton">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Kick player</string>
|
||||
<string>Lobby chat</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Players in the room</string>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="chat">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="messageEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="sessionsPage">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="newButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New room</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="joinButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Join room</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QTableWidget" name="sessionsTable">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>80</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderMinimumSectionSize">
|
||||
<number>20</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>20</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Session</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Players</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="roomPage">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="kickButton">
|
||||
<property name="text">
|
||||
<string>Kick player</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QListWidget" name="playersList">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Players in the room</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QPushButton" name="buttonLeave">
|
||||
<property name="text">
|
||||
<string>Leave</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Mods mismatch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QListWidget" name="modsList">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QPushButton" name="buttonReady">
|
||||
<property name="text">
|
||||
<string>Ready</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="buttonResolve">
|
||||
<property name="text">
|
||||
<string>Resolve</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="optNewGame">
|
||||
<property name="text">
|
||||
<string>New game</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="optLoadGame">
|
||||
<property name="text">
|
||||
<string>Load game</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -27,6 +27,15 @@ LobbyRoomRequest::LobbyRoomRequest(SocketLobby & socket, const QString & room, c
|
||||
show();
|
||||
}
|
||||
|
||||
void LobbyRoomRequest::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
QDialog::changeEvent(event);
|
||||
}
|
||||
|
||||
LobbyRoomRequest::~LobbyRoomRequest()
|
||||
{
|
||||
delete ui;
|
||||
|
@ -21,6 +21,7 @@ class LobbyRoomRequest : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void changeEvent(QEvent *event) override;
|
||||
public:
|
||||
explicit LobbyRoomRequest(SocketLobby & socket, const QString & room, const QMap<QString, QString> & mods, QWidget *parent = nullptr);
|
||||
~LobbyRoomRequest();
|
||||
|
@ -9,7 +9,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>193</width>
|
||||
<width>227</width>
|
||||
<height>188</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -51,39 +51,42 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
<string notr="true">3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
<string notr="true">4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
<string notr="true">5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
<string notr="true">6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
<string notr="true">7</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
<string notr="true">8</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
|
@ -32,6 +32,7 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
#endif
|
||||
QApplication vcmilauncher(argc, argv);
|
||||
|
||||
MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
result = vcmilauncher.exec();
|
||||
@ -65,6 +66,13 @@ void startGame(const QStringList & args)
|
||||
#endif
|
||||
}
|
||||
|
||||
void startEditor(const QStringList & args)
|
||||
{
|
||||
#ifdef ENABLE_EDITOR
|
||||
startExecutable(pathToQString(VCMIDirs::get().mapEditorPath()), args);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef Q_OS_IOS
|
||||
void startExecutable(QString name, const QStringList & args)
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
void startGame(const QStringList & args);
|
||||
void startEditor(const QStringList & args);
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
extern "C" void launchGame(int argc, char * argv[]);
|
||||
|
@ -47,12 +47,40 @@ void MainWindow::load()
|
||||
settings.init();
|
||||
}
|
||||
|
||||
void MainWindow::computeSidePanelSizes()
|
||||
{
|
||||
QVector<QToolButton*> widgets = {
|
||||
ui->modslistButton,
|
||||
ui->settingsButton,
|
||||
ui->lobbyButton,
|
||||
ui->startEditorButton,
|
||||
ui->startGameButton
|
||||
};
|
||||
|
||||
for(auto & widget : widgets)
|
||||
{
|
||||
QFontMetrics metrics(widget->font());
|
||||
QSize iconSize = widget->iconSize();
|
||||
|
||||
// this is minimal space that is needed for our button to avoid text clipping
|
||||
int buttonHeight = iconSize.height() + metrics.height() + 4;
|
||||
|
||||
widget->setMinimumHeight(buttonHeight);
|
||||
widget->setMaximumHeight(buttonHeight * 1.2);
|
||||
}
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(QWidget * parent)
|
||||
: QMainWindow(parent), ui(new Ui::MainWindow)
|
||||
{
|
||||
load(); // load FS before UI
|
||||
updateTranslation(); // load translation
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->lobbyView, &Lobby::enableMod, ui->modlistView, &CModListView::enableModByName);
|
||||
connect(ui->lobbyView, &Lobby::disableMod, ui->modlistView, &CModListView::disableModByName);
|
||||
connect(ui->modlistView, &CModListView::modsChanged, ui->lobbyView, &Lobby::updateMods);
|
||||
|
||||
//load window settings
|
||||
QSettings s(Ui::teamName, Ui::appName);
|
||||
@ -68,45 +96,32 @@ MainWindow::MainWindow(QWidget * parent)
|
||||
move(position);
|
||||
}
|
||||
|
||||
//set default margins
|
||||
#ifndef ENABLE_EDITOR
|
||||
ui->startEditorButton->hide();
|
||||
#endif
|
||||
|
||||
computeSidePanelSizes();
|
||||
|
||||
auto width = ui->startGameTitle->fontMetrics().boundingRect(ui->startGameTitle->text()).width();
|
||||
if(ui->startGameButton->iconSize().width() < width)
|
||||
{
|
||||
ui->startGameButton->setIconSize(QSize(width, width));
|
||||
}
|
||||
auto tab_icon_size = ui->tabSelectList->iconSize();
|
||||
if(tab_icon_size.width() < width)
|
||||
{
|
||||
ui->tabSelectList->setIconSize(QSize(width, width + tab_icon_size.height() - tab_icon_size.width()));
|
||||
ui->tabSelectList->setGridSize(QSize(width, width));
|
||||
// 4 is a dirty hack to make it look right
|
||||
ui->tabSelectList->setMaximumWidth(width + 4);
|
||||
}
|
||||
ui->tabListWidget->setCurrentIndex(0);
|
||||
|
||||
ui->settingsView->isExtraResolutionsModEnabled = ui->modlistView->isExtraResolutionsModEnabled();
|
||||
ui->settingsView->setDisplayList();
|
||||
connect(ui->modlistView, &CModListView::extraResolutionsEnabledChanged,
|
||||
ui->settingsView, &CSettingsView::fillValidResolutions);
|
||||
|
||||
connect(ui->tabSelectList, &QListWidget::currentRowChanged, [this](int i) {
|
||||
#ifdef Q_OS_IOS
|
||||
if(auto widget = qApp->focusWidget())
|
||||
widget->clearFocus();
|
||||
#endif
|
||||
ui->tabListWidget->setCurrentIndex(i);
|
||||
});
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
QScroller::grabGesture(ui->tabSelectList, QScroller::LeftMouseButtonGesture);
|
||||
ui->tabSelectList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
#endif
|
||||
|
||||
if(settings["launcher"]["updateOnStartup"].Bool())
|
||||
UpdateDialog::showUpdateDialog(false);
|
||||
}
|
||||
|
||||
void MainWindow::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
QMainWindow::changeEvent(event);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
//save window settings
|
||||
@ -122,12 +137,63 @@ void MainWindow::on_startGameButton_clicked()
|
||||
startGame({});
|
||||
}
|
||||
|
||||
void MainWindow::on_tabSelectList_currentRowChanged(int currentRow)
|
||||
void MainWindow::on_startEditorButton_clicked()
|
||||
{
|
||||
ui->startGameButton->setEnabled(currentRow != TabRows::LOBBY);
|
||||
startEditor({});
|
||||
}
|
||||
|
||||
const CModList & MainWindow::getModList() const
|
||||
{
|
||||
return ui->modlistView->getModList();
|
||||
}
|
||||
|
||||
void MainWindow::on_modslistButton_clicked()
|
||||
{
|
||||
ui->startGameButton->setEnabled(true);
|
||||
ui->tabListWidget->setCurrentIndex(TabRows::MODS);
|
||||
}
|
||||
|
||||
void MainWindow::on_settingsButton_clicked()
|
||||
{
|
||||
ui->startGameButton->setEnabled(true);
|
||||
ui->tabListWidget->setCurrentIndex(TabRows::SETTINGS);
|
||||
}
|
||||
|
||||
void MainWindow::on_lobbyButton_clicked()
|
||||
{
|
||||
ui->startGameButton->setEnabled(false);
|
||||
ui->tabListWidget->setCurrentIndex(TabRows::LOBBY);
|
||||
}
|
||||
|
||||
void MainWindow::updateTranslation()
|
||||
{
|
||||
#ifdef ENABLE_QT_TRANSLATIONS
|
||||
std::string translationFile = settings["general"]["language"].String() + ".qm";
|
||||
logGlobal->info("Loading translation '%s'", translationFile);
|
||||
|
||||
QVector<QString> searchPaths;
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
searchPaths.push_back(pathToQString(VCMIDirs::get().binaryPath() / "translation" / translationFile));
|
||||
#else
|
||||
for(auto const & string : VCMIDirs::get().dataPaths())
|
||||
searchPaths.push_back(pathToQString(string / "launcher" / "translation" / translationFile));
|
||||
searchPaths.push_back(pathToQString(VCMIDirs::get().userDataPath() / "launcher" / "translation" / translationFile));
|
||||
#endif
|
||||
|
||||
for(auto const & string : boost::adaptors::reverse(searchPaths))
|
||||
{
|
||||
logGlobal->info("Searching for translation at '%s'", string.toStdString());
|
||||
if (translator.load(string))
|
||||
{
|
||||
logGlobal->info("Translation found");
|
||||
if (!qApp->installTranslator(&translator))
|
||||
logGlobal->error("Failed to install translator");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
logGlobal->error("Failed to find translation");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
#include <QMainWindow>
|
||||
#include <QStringList>
|
||||
#include <QTranslator>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@ -26,6 +27,9 @@ class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
#ifdef ENABLE_QT_TRANSLATIONS
|
||||
QTranslator translator;
|
||||
#endif
|
||||
private:
|
||||
Ui::MainWindow * ui;
|
||||
void load();
|
||||
@ -35,16 +39,22 @@ private:
|
||||
MODS = 0, SETTINGS = 1, LOBBY = 2
|
||||
};
|
||||
|
||||
void changeEvent(QEvent *event) override;
|
||||
public:
|
||||
explicit MainWindow(QWidget * parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
const CModList & getModList() const;
|
||||
|
||||
void updateTranslation();
|
||||
void computeSidePanelSizes();
|
||||
|
||||
public slots:
|
||||
void on_startGameButton_clicked();
|
||||
|
||||
private slots:
|
||||
void on_tabSelectList_currentRowChanged(int currentRow);
|
||||
void on_modslistButton_clicked();
|
||||
void on_settingsButton_clicked();
|
||||
void on_lobbyButton_clicked();
|
||||
void on_startEditorButton_clicked();
|
||||
};
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>480</height>
|
||||
<height>410</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -30,127 +30,288 @@
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="tabSelectList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
|
||||
<horstretch>89</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>89</width>
|
||||
<height>89</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::NoDragDrop</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>89</width>
|
||||
<height>89</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
</property>
|
||||
<property name="flow">
|
||||
<enum>QListView::TopToBottom</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Adjust</enum>
|
||||
</property>
|
||||
<property name="gridSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mods</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-mods.png</normaloff>icons:menu-mods.png</iconset>
|
||||
</property>
|
||||
<widget class="QToolButton" name="modslistButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Mods</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-mods.png</normaloff>icons:menu-mods.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-settings.png</normaloff>icons:menu-settings.png</iconset>
|
||||
</property>
|
||||
<widget class="QToolButton" name="settingsButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-settings.png</normaloff>icons:menu-settings.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Lobby</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-lobby.png</normaloff>icons:menu-lobby.png</iconset>
|
||||
</property>
|
||||
<widget class="QToolButton" name="lobbyButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lobby</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-lobby.png</normaloff>icons:menu-lobby.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</widget>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="startEditorButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>5</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Map Editor</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-editor.png</normaloff>icons:menu-editor.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="startGameButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start game</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-game.png</normaloff>icons:menu-game.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextUnderIcon</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="startGameTitle">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start game</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" rowspan="3">
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="tabListWidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<horstretch>10</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
@ -162,44 +323,6 @@
|
||||
<widget class="Lobby" name="lobbyView"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QToolButton" name="startGameButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Maximum">
|
||||
<horstretch>89</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>89</width>
|
||||
<height>89</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Play</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-game.png</normaloff>icons:menu-game.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
@ -224,10 +347,6 @@
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>tabSelectList</tabstop>
|
||||
<tabstop>startGameButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "cmodlist.h"
|
||||
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../../lib/JsonNode.h"
|
||||
#include "../../lib/filesystem/CFileInputStream.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
@ -125,7 +126,7 @@ bool CModEntry::isCompatible() const
|
||||
|
||||
bool CModEntry::isEssential() const
|
||||
{
|
||||
return getValue("storedLocaly").toBool();
|
||||
return getName() == "vcmi";
|
||||
}
|
||||
|
||||
bool CModEntry::isInstalled() const
|
||||
@ -158,23 +159,34 @@ QString CModEntry::getName() const
|
||||
|
||||
QVariant CModEntry::getValue(QString value) const
|
||||
{
|
||||
QString langValue = QString::fromStdString(settings["general"]["language"].String());
|
||||
|
||||
// Priorities
|
||||
// 1) data from newest version
|
||||
// 2) data from preferred language
|
||||
|
||||
bool useRepositoryData = repository.contains(value);
|
||||
|
||||
if(repository.contains(value) && localData.contains(value))
|
||||
{
|
||||
// value is present in both repo and locally installed. Select one from latest version
|
||||
QString installedVer = localData["installedVersion"].toString();
|
||||
QString availableVer = repository["latestVersion"].toString();
|
||||
|
||||
if(compareVersions(installedVer, availableVer))
|
||||
return repository[value];
|
||||
else
|
||||
return localData[value];
|
||||
useRepositoryData = compareVersions(installedVer, availableVer);
|
||||
}
|
||||
|
||||
if(repository.contains(value))
|
||||
return repository[value];
|
||||
auto & storage = useRepositoryData ? repository : localData;
|
||||
|
||||
if(localData.contains(value))
|
||||
return localData[value];
|
||||
if(storage.contains(langValue))
|
||||
{
|
||||
auto langStorage = storage[langValue].toMap();
|
||||
if (langStorage.contains(value))
|
||||
return langStorage[value];
|
||||
}
|
||||
|
||||
if(storage.contains(value))
|
||||
return storage[value];
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
@ -25,16 +25,6 @@ static const QString names[ModFields::COUNT] =
|
||||
"author"
|
||||
};
|
||||
|
||||
static const QString header[ModFields::COUNT] =
|
||||
{
|
||||
"Name",
|
||||
"", // status icon
|
||||
"", // status icon
|
||||
"Type",
|
||||
"Version",
|
||||
"Size",
|
||||
"Author"
|
||||
};
|
||||
}
|
||||
|
||||
namespace ModStatus
|
||||
@ -155,8 +145,19 @@ Qt::ItemFlags CModListModel::flags(const QModelIndex &) const
|
||||
|
||||
QVariant CModListModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
static const QString header[ModFields::COUNT] =
|
||||
{
|
||||
QT_TR_NOOP("Name"),
|
||||
QT_TR_NOOP(""), // status icon
|
||||
QT_TR_NOOP(""), // status icon
|
||||
QT_TR_NOOP("Type"),
|
||||
QT_TR_NOOP("Version"),
|
||||
QT_TR_NOOP("Size"),
|
||||
QT_TR_NOOP("Author")
|
||||
};
|
||||
|
||||
if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
|
||||
return ModFields::header[section];
|
||||
return QCoreApplication::translate("ModFields", header[section].toStdString().c_str());
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,16 @@ void CModListView::setupModModel()
|
||||
this, &CModListView::extraResolutionsEnabledChanged);
|
||||
}
|
||||
|
||||
void CModListView::changeEvent(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::LanguageChange)
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
modModel->reloadRepositories();
|
||||
}
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
void CModListView::setupFilterModel()
|
||||
{
|
||||
filterModel = new CModFilterModel(modModel, this);
|
||||
@ -227,8 +237,8 @@ QString CModListView::genModInfoText(CModEntry & mod)
|
||||
QString textTemplate = prefix + "</p><p align=\"justify\">%2</p>";
|
||||
QString listTemplate = "<p align=\"justify\">%1: %2</p>";
|
||||
QString noteTemplate = "<p align=\"justify\">%1</p>";
|
||||
QString compatibleString = prefix + "Mod is compatible</p>";
|
||||
QString incompatibleString = redPrefix + "Mod is incompatible</p>";
|
||||
QString compatibleString = prefix + tr("Mod is compatible") + "</p>";
|
||||
QString incompatibleString = redPrefix + tr("Mod is incompatible") + "</p>";
|
||||
QString supportedVersions = redPrefix + "%2 %3 %4</p>";
|
||||
|
||||
QString result;
|
||||
@ -496,7 +506,14 @@ QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled)
|
||||
void CModListView::on_enableButton_clicked()
|
||||
{
|
||||
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
|
||||
|
||||
enableModByName(modName);
|
||||
|
||||
checkManagerErrors();
|
||||
}
|
||||
|
||||
void CModListView::enableModByName(QString modName)
|
||||
{
|
||||
assert(findBlockingMods(modName).empty());
|
||||
assert(findInvalidDependencies(modName).empty());
|
||||
|
||||
@ -505,17 +522,24 @@ void CModListView::on_enableButton_clicked()
|
||||
if(modModel->getMod(name).isDisabled())
|
||||
manager->enableMod(name);
|
||||
}
|
||||
checkManagerErrors();
|
||||
emit modsChanged();
|
||||
}
|
||||
|
||||
void CModListView::on_disableButton_clicked()
|
||||
{
|
||||
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
|
||||
|
||||
disableModByName(modName);
|
||||
|
||||
checkManagerErrors();
|
||||
}
|
||||
|
||||
void CModListView::disableModByName(QString modName)
|
||||
{
|
||||
if(modModel->hasMod(modName) && modModel->getMod(modName).isEnabled())
|
||||
manager->disableMod(modName);
|
||||
|
||||
checkManagerErrors();
|
||||
emit modsChanged();
|
||||
}
|
||||
|
||||
void CModListView::on_updateButton_clicked()
|
||||
@ -544,6 +568,8 @@ void CModListView::on_uninstallButton_clicked()
|
||||
manager->disableMod(modName);
|
||||
manager->uninstallMod(modName);
|
||||
}
|
||||
|
||||
emit modsChanged();
|
||||
checkManagerErrors();
|
||||
}
|
||||
|
||||
@ -631,6 +657,8 @@ void CModListView::downloadFinished(QStringList savedFiles, QStringList failedFi
|
||||
|
||||
if(doInstallFiles)
|
||||
installFiles(savedFiles);
|
||||
|
||||
emit modsChanged();
|
||||
}
|
||||
|
||||
void CModListView::hideProgressBar()
|
||||
|
@ -64,8 +64,11 @@ class CModListView : public QWidget
|
||||
QString genChangelogText(CModEntry & mod);
|
||||
QString genModInfoText(CModEntry & mod);
|
||||
|
||||
void changeEvent(QEvent *event) override;
|
||||
signals:
|
||||
void extraResolutionsEnabledChanged(bool enabled);
|
||||
|
||||
void modsChanged();
|
||||
|
||||
public:
|
||||
explicit CModListView(QWidget * parent = 0);
|
||||
@ -82,6 +85,10 @@ public:
|
||||
bool isExtraResolutionsModEnabled() const;
|
||||
|
||||
const CModList & getModList() const;
|
||||
|
||||
public slots:
|
||||
void enableModByName(QString modName);
|
||||
void disableModByName(QString modName);
|
||||
|
||||
private slots:
|
||||
void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight);
|
||||
|