Merge pull request #2076 from IvanSavenko/configurable_adventure_map
Implement scalable adventure map window
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 254 B |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 283 B |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 747 B |
@ -49,10 +49,13 @@
|
||||
"vcmi.systemOptions.fullscreenButton.hover" : "Fullscreen",
|
||||
"vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\nIf selected, VCMI will run in fullscreen mode, otherwise it will run in windowed mode",
|
||||
"vcmi.systemOptions.resolutionButton.hover" : "Resolution: %wx%h",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{Select Resolution}\n\nChange in-game screen resolution. A game restart is required to apply the new resolution.",
|
||||
"vcmi.systemOptions.resolutionButton.help" : "{Select Resolution}\n\nChange in-game screen resolution.",
|
||||
"vcmi.systemOptions.resolutionMenu.hover" : "Select Resolution",
|
||||
"vcmi.systemOptions.resolutionMenu.help" : "Change in-game screen resolution.",
|
||||
"vcmi.systemOptions.fullscreenFailed" : "{Fullscreen}\n\nFailed to switch to fullscreen mode! The current resolution is not supported by the display!",
|
||||
"vcmi.systemOptions.scalingButton.hover" : "Interface Scaling: %p%",
|
||||
"vcmi.systemOptions.scalingButton.help" : "{Interface Scaling}\n\nChanges scaling of in-game interface",
|
||||
"vcmi.systemOptions.scalingMenu.hover" : "Select Interface Scaling",
|
||||
"vcmi.systemOptions.scalingMenu.help" : "Change in-game interface scaling.",
|
||||
"vcmi.systemOptions.framerateButton.hover" : "Show FPS",
|
||||
"vcmi.systemOptions.framerateButton.help" : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window",
|
||||
|
||||
|
526
client/CMT.cpp
@ -7,61 +7,48 @@
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
// CMT.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
#include "StdInc.h"
|
||||
#include "CMT.h"
|
||||
|
||||
#include "CGameInfo.h"
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "lobby/CSelectionBase.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
#include "gui/CursorHandler.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CVideoHandler.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "Client.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/NotificationHandler.h"
|
||||
#include "ClientCommandManager.h"
|
||||
#include "windows/CMessage.h"
|
||||
#include "renderSDL/SDL_Extensions.h"
|
||||
#include "render/IScreenHandler.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/FileStream.h"
|
||||
#include "../lib/CConsoleHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include "mainmenu/CPrologEpilogVideo.h"
|
||||
#include <vstd/StringUtils.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_hints.h>
|
||||
#include <SDL_main.h>
|
||||
|
||||
#ifdef VCMI_WINDOWS
|
||||
#include <SDL_syswm.h>
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "../lib/CAndroidVMHelper.h"
|
||||
#include <SDL_system.h>
|
||||
#endif
|
||||
|
||||
#include "CMT.h"
|
||||
|
||||
#if __MINGW32__
|
||||
#undef main
|
||||
#endif
|
||||
@ -70,44 +57,21 @@ namespace po = boost::program_options;
|
||||
namespace po_style = boost::program_options::command_line_style;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
std::string NAME_AFFIX = "client";
|
||||
std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
CGuiHandler GH;
|
||||
|
||||
int preferredDriverIndex = -1;
|
||||
SDL_Window * mainWindow = nullptr;
|
||||
SDL_Renderer * mainRenderer = nullptr;
|
||||
SDL_Texture * screenTexture = nullptr;
|
||||
|
||||
extern boost::thread_specific_ptr<bool> inGuiThread;
|
||||
|
||||
SDL_Surface *screen = nullptr, //main screen surface
|
||||
*screen2 = nullptr, //and hlp surface (used to store not-active interfaces layer)
|
||||
*screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
|
||||
|
||||
std::queue<SDL_Event> SDLEventsQueue;
|
||||
boost::mutex eventsM;
|
||||
|
||||
static po::variables_map vm;
|
||||
|
||||
//static bool setResolution = false; //set by event handling thread after resolution is adjusted
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
void processCommand(const std::string &message);
|
||||
#endif
|
||||
static void setScreenRes(int w, int h, int bpp, bool fullscreen, int displayIndex, bool resetVideo=true);
|
||||
void playIntro();
|
||||
static void mainLoop();
|
||||
|
||||
static CBasicLogConfigurator *logConfig;
|
||||
|
||||
#ifndef VCMI_WINDOWS
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
void init()
|
||||
{
|
||||
CStopWatch tmh;
|
||||
@ -139,17 +103,6 @@ static void prog_help(const po::options_description &opts)
|
||||
std::cout << opts;
|
||||
}
|
||||
|
||||
static void SDLLogCallback(void* userdata,
|
||||
int category,
|
||||
SDL_LogPriority priority,
|
||||
const char* message)
|
||||
{
|
||||
//todo: convert SDL log priority to vcmi log priority
|
||||
//todo: make separate log domain for SDL
|
||||
|
||||
logGlobal->debug("SDL(category %d; priority %d) %s", category, priority, message);
|
||||
}
|
||||
|
||||
#if defined(VCMI_WINDOWS) && !defined(__GNUC__) && defined(VCMI_WITH_DEBUG_CONSOLE)
|
||||
int wmain(int argc, wchar_t* argv[])
|
||||
#elif defined(VCMI_MOBILE)
|
||||
@ -255,7 +208,7 @@ int main(int argc, char * argv[])
|
||||
const bfs::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Client_log.txt";
|
||||
logConfig = new CBasicLogConfigurator(logPath, console);
|
||||
logConfig->configureDefault();
|
||||
logGlobal->info(NAME);
|
||||
logGlobal->info("Starting client of '%s'", GameConstants::VCMI_VERSION);
|
||||
logGlobal->info("Creating console and configuring logger: %d ms", pomtime.getDiff());
|
||||
logGlobal->info("The log file will be saved to %s", logPath);
|
||||
|
||||
@ -338,73 +291,11 @@ int main(int argc, char * argv[])
|
||||
testFile("VIDEO/GOOD1A.SMK", "campaign movies");
|
||||
testFile("SOUNDS/G1A.WAV", "campaign music"); //technically not a music but voiced intro sounds
|
||||
|
||||
conf.init();
|
||||
logGlobal->info("Loading settings: %d ms", pomtime.getDiff());
|
||||
|
||||
srand ( (unsigned int)time(nullptr) );
|
||||
|
||||
|
||||
const JsonNode& video = settings["video"];
|
||||
const JsonNode& res = video["screenRes"];
|
||||
|
||||
//something is really wrong...
|
||||
if (res["width"].Float() < 100 || res["height"].Float() < 100)
|
||||
{
|
||||
logGlobal->error("Fatal error: failed to load settings!");
|
||||
logGlobal->error("Possible reasons:");
|
||||
logGlobal->error("\tCorrupted local configuration file at %s/settings.json", VCMIDirs::get().userConfigPath());
|
||||
logGlobal->error("\tMissing or corrupted global configuration file at %s/schemas/settings.json", VCMIDirs::get().userConfigPath());
|
||||
logGlobal->error("VCMI will now exit...");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE))
|
||||
{
|
||||
logGlobal->error("Something was wrong: %s", SDL_GetError());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
// manually setting egl pixel format, as a possible solution for sdl2<->android problem
|
||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=2291
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
|
||||
#endif // VCMI_ANDROID
|
||||
|
||||
//(!)init here AFTER SDL_Init() while using SDL for FPS management
|
||||
GH.init();
|
||||
|
||||
SDL_LogSetOutputFunction(&SDLLogCallback, nullptr);
|
||||
|
||||
int driversCount = SDL_GetNumRenderDrivers();
|
||||
std::string preferredDriverName = video["driver"].String();
|
||||
|
||||
logGlobal->info("Found %d render drivers", driversCount);
|
||||
|
||||
for(int it = 0; it < driversCount; it++)
|
||||
{
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRenderDriverInfo(it,&info);
|
||||
|
||||
std::string driverName(info.name);
|
||||
|
||||
if(!preferredDriverName.empty() && driverName == preferredDriverName)
|
||||
{
|
||||
preferredDriverIndex = it;
|
||||
logGlobal->info("\t%s (active)", driverName);
|
||||
}
|
||||
else
|
||||
logGlobal->info("\t%s", driverName);
|
||||
}
|
||||
|
||||
setScreenRes((int)res["width"].Float(), (int)res["height"].Float(), (int)video["bitsPerPixel"].Float(), video["fullscreen"].Bool(), (int)video["displayIndex"].Float());
|
||||
logGlobal->info("\tInitializing screen: %d ms", pomtime.getDiff());
|
||||
}
|
||||
|
||||
CCS = new CClientState();
|
||||
CGI = new CGameInfo(); //contains all global informations about game (texts, lodHandlers, map handler etc.)
|
||||
CSH = new CServerHandler();
|
||||
@ -457,9 +348,7 @@ int main(int argc, char * argv[])
|
||||
{
|
||||
if(!vm.count("battle") && !vm.count("nointro") && settings["video"]["showIntro"].Bool())
|
||||
playIntro();
|
||||
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(mainRenderer);
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
GH.screenHandler().clearScreen();
|
||||
}
|
||||
|
||||
|
||||
@ -490,7 +379,6 @@ int main(int argc, char * argv[])
|
||||
CCS->curh->show();
|
||||
}
|
||||
|
||||
|
||||
logGlobal->info("Initialization of VCMI (together): %d ms", total.getDiff());
|
||||
|
||||
session["autoSkip"].Bool() = vm.count("autoSkip");
|
||||
@ -580,350 +468,6 @@ void playIntro()
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(VCMI_MOBILE)
|
||||
static bool checkVideoMode(int monitorIndex, int w, int h)
|
||||
{
|
||||
//we only check that our desired window size fits on screen
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
if (0 != SDL_GetDesktopDisplayMode(monitorIndex, &mode))
|
||||
{
|
||||
logGlobal->error("SDL_GetDesktopDisplayMode failed");
|
||||
logGlobal->error(SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
logGlobal->info("Check display mode: requested %d x %d; available up to %d x %d ", w, h, mode.w, mode.h);
|
||||
|
||||
if (!mode.w || !mode.h || (w <= mode.w && h <= mode.h))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cleanupRenderer()
|
||||
{
|
||||
screenBuf = nullptr; //it`s a link - just nullify
|
||||
|
||||
if(nullptr != screen2)
|
||||
{
|
||||
SDL_FreeSurface(screen2);
|
||||
screen2 = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != screen)
|
||||
{
|
||||
SDL_FreeSurface(screen);
|
||||
screen = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != screenTexture)
|
||||
{
|
||||
SDL_DestroyTexture(screenTexture);
|
||||
screenTexture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIndex)
|
||||
{
|
||||
// VCMI will only work with 2 or 4 bytes per pixel
|
||||
vstd::amax(bpp, 16);
|
||||
vstd::amin(bpp, 32);
|
||||
if(bpp>16)
|
||||
bpp = 32;
|
||||
|
||||
if(displayIndex < 0)
|
||||
{
|
||||
if (mainWindow != nullptr)
|
||||
displayIndex = SDL_GetWindowDisplayIndex(mainWindow);
|
||||
if (displayIndex < 0)
|
||||
displayIndex = 0;
|
||||
}
|
||||
|
||||
#if defined(VCMI_MOBILE)
|
||||
SDL_GetWindowSize(mainWindow, &w, &h);
|
||||
#else
|
||||
if(!checkVideoMode(displayIndex, w, h))
|
||||
{
|
||||
logGlobal->error("Error: SDL says that %dx%d resolution is not available!", w, h);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bufOnScreen = (screenBuf == screen);
|
||||
bool realFullscreen = settings["video"]["realFullscreen"].Bool();
|
||||
|
||||
/* match best rendering resolution */
|
||||
int renderWidth = 0, renderHeight = 0;
|
||||
auto aspectRatio = (float)w / (float)h;
|
||||
auto minDiff = 10.f;
|
||||
for (const auto& pair : conf.guiOptions)
|
||||
{
|
||||
int pWidth, pHeight;
|
||||
std::tie(pWidth, pHeight) = pair.first;
|
||||
/* filter out resolution which is larger than window */
|
||||
if (pWidth > w || pHeight > h)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto ratio = (float)pWidth / (float)pHeight;
|
||||
auto diff = fabs(aspectRatio - ratio);
|
||||
/* select closest aspect ratio */
|
||||
if (diff < minDiff)
|
||||
{
|
||||
renderWidth = pWidth;
|
||||
renderHeight = pHeight;
|
||||
minDiff = diff;
|
||||
}
|
||||
/* select largest resolution meets prior conditions.
|
||||
* since there are resolutions like 1366x768(not exactly 16:9), a deviation of 0.005 is allowed. */
|
||||
else if (fabs(diff - minDiff) < 0.005f && pWidth > renderWidth)
|
||||
{
|
||||
renderWidth = pWidth;
|
||||
renderHeight = pHeight;
|
||||
}
|
||||
}
|
||||
if (renderWidth == 0)
|
||||
{
|
||||
// no matching resolution for upscaling - complain & fallback to default resolution.
|
||||
logGlobal->error("Failed to match rendering resolution for %dx%d!", w, h);
|
||||
Settings newRes = settings.write["video"]["screenRes"];
|
||||
std::tie(w, h) = conf.guiOptions.begin()->first;
|
||||
newRes["width"].Float() = w;
|
||||
newRes["height"].Float() = h;
|
||||
conf.SetResolution(w, h);
|
||||
logGlobal->error("Falling back to %dx%d", w, h);
|
||||
renderWidth = w;
|
||||
renderHeight = h;
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->info("Set logical rendering resolution to %dx%d", renderWidth, renderHeight);
|
||||
}
|
||||
|
||||
cleanupRenderer();
|
||||
|
||||
if(nullptr == mainWindow)
|
||||
{
|
||||
#if defined(VCMI_MOBILE)
|
||||
auto createWindow = [displayIndex](uint32_t extraFlags) -> bool {
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN | extraFlags);
|
||||
return mainWindow != nullptr;
|
||||
};
|
||||
|
||||
# ifdef VCMI_IOS
|
||||
SDL_SetHint(SDL_HINT_IOS_HIDE_HOME_INDICATOR, "1");
|
||||
SDL_SetHint(SDL_HINT_RETURN_KEY_HIDES_IME, "1");
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
if(!createWindow(windowFlags | SDL_WINDOW_METAL))
|
||||
{
|
||||
logGlobal->warn("Metal unavailable, using OpenGLES");
|
||||
createWindow(windowFlags);
|
||||
}
|
||||
# else
|
||||
createWindow(0);
|
||||
# endif // VCMI_IOS
|
||||
|
||||
// SDL on mobile doesn't do proper letterboxing, and will show an annoying flickering in the blank space in case you're not using the full screen estate
|
||||
// That's why we need to make sure our width and height we'll use below have the same aspect ratio as the screen itself to ensure we fill the full screen estate
|
||||
|
||||
SDL_Rect screenRect;
|
||||
|
||||
if(SDL_GetDisplayBounds(0, &screenRect) == 0)
|
||||
{
|
||||
const auto screenWidth = screenRect.w;
|
||||
const auto screenHeight = screenRect.h;
|
||||
|
||||
const auto aspect = static_cast<double>(screenWidth) / screenHeight;
|
||||
|
||||
logGlobal->info("Screen size and aspect ratio: %dx%d (%lf)", screenWidth, screenHeight, aspect);
|
||||
|
||||
if((double)w / aspect > (double)h)
|
||||
{
|
||||
h = (int)round((double)w / aspect);
|
||||
}
|
||||
else
|
||||
{
|
||||
w = (int)round((double)h * aspect);
|
||||
}
|
||||
|
||||
logGlobal->info("Changing logical screen size to %dx%d", w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Can't fix aspect ratio for screen");
|
||||
}
|
||||
#else
|
||||
if(fullscreen)
|
||||
{
|
||||
if(realFullscreen)
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), renderWidth, renderHeight, SDL_WINDOW_FULLSCREEN);
|
||||
else //in windowed full-screen mode use desktop resolution
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
}
|
||||
else
|
||||
{
|
||||
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0);
|
||||
}
|
||||
#endif // defined(VCMI_MOBILE)
|
||||
|
||||
if(nullptr == mainWindow)
|
||||
{
|
||||
throw std::runtime_error("Unable to create window\n");
|
||||
}
|
||||
|
||||
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
|
||||
mainRenderer = SDL_CreateRenderer(mainWindow,preferredDriverIndex,0);
|
||||
|
||||
if(nullptr == mainRenderer)
|
||||
{
|
||||
throw std::runtime_error("Unable to create renderer\n");
|
||||
}
|
||||
|
||||
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRendererInfo(mainRenderer, &info);
|
||||
logGlobal->info("Created renderer %s", info.name);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !defined(VCMI_MOBILE)
|
||||
|
||||
if(fullscreen)
|
||||
{
|
||||
if(realFullscreen)
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN);
|
||||
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetDesktopDisplayMode(displayIndex, &mode);
|
||||
mode.w = renderWidth;
|
||||
mode.h = renderHeight;
|
||||
|
||||
SDL_SetWindowDisplayMode(mainWindow, &mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
}
|
||||
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex));
|
||||
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, 0);
|
||||
SDL_SetWindowSize(mainWindow, w, h);
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!(fullscreen && realFullscreen))
|
||||
{
|
||||
SDL_RenderSetLogicalSize(mainRenderer, renderWidth, renderHeight);
|
||||
|
||||
//following line is bugged not only on android, do not re-enable without checking
|
||||
//#ifndef VCMI_ANDROID
|
||||
// // on android this stretches the game to fit the screen, not preserving aspect and apparently this also breaks coordinates scaling in mouse events
|
||||
// SDL_RenderSetViewport(mainRenderer, nullptr);
|
||||
//#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef VCMI_ENDIAN_BIG
|
||||
int bmask = 0xff000000;
|
||||
int gmask = 0x00ff0000;
|
||||
int rmask = 0x0000ff00;
|
||||
int amask = 0x000000ff;
|
||||
#else
|
||||
int bmask = 0x000000ff;
|
||||
int gmask = 0x0000ff00;
|
||||
int rmask = 0x00ff0000;
|
||||
int amask = 0xFF000000;
|
||||
#endif
|
||||
|
||||
screen = SDL_CreateRGBSurface(0,renderWidth,renderHeight,bpp,rmask,gmask,bmask,amask);
|
||||
if(nullptr == screen)
|
||||
{
|
||||
logGlobal->error("Unable to create surface %dx%d with %d bpp: %s", renderWidth, renderHeight, bpp, SDL_GetError());
|
||||
throw std::runtime_error("Unable to create surface");
|
||||
}
|
||||
//No blending for screen itself. Required for proper cursor rendering.
|
||||
SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
|
||||
|
||||
screenTexture = SDL_CreateTexture(mainRenderer,
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
renderWidth, renderHeight);
|
||||
|
||||
if(nullptr == screenTexture)
|
||||
{
|
||||
logGlobal->error("Unable to create screen texture");
|
||||
logGlobal->error(SDL_GetError());
|
||||
throw std::runtime_error("Unable to create screen texture");
|
||||
}
|
||||
|
||||
screen2 = CSDL_Ext::copySurface(screen);
|
||||
|
||||
|
||||
if(nullptr == screen2)
|
||||
{
|
||||
throw std::runtime_error("Unable to copy surface\n");
|
||||
}
|
||||
|
||||
screenBuf = bufOnScreen ? screen : screen2;
|
||||
|
||||
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 0);
|
||||
SDL_RenderClear(mainRenderer);
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
|
||||
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::init(mainWindow);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//used only once during initialization
|
||||
static void setScreenRes(int w, int h, int bpp, bool fullscreen, int displayIndex, bool resetVideo)
|
||||
{
|
||||
if(!recreateWindow(w, h, bpp, fullscreen, displayIndex))
|
||||
{
|
||||
throw std::runtime_error("Requested screen resolution is not available\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void fullScreenChanged()
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
const bool toFullscreen = full->Bool();
|
||||
|
||||
auto bitsPerPixel = screen->format->BitsPerPixel;
|
||||
|
||||
auto w = screen->w;
|
||||
auto h = screen->h;
|
||||
|
||||
if(!recreateWindow(w, h, bitsPerPixel, toFullscreen, -1))
|
||||
{
|
||||
//will return false and report error if video mode is not supported
|
||||
return;
|
||||
}
|
||||
|
||||
GH.totalRedraw();
|
||||
}
|
||||
|
||||
static void handleEvent(SDL_Event & ev)
|
||||
{
|
||||
if((ev.type==SDL_QUIT) ||(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT)))
|
||||
@ -1001,8 +545,11 @@ static void handleEvent(SDL_Event & ev)
|
||||
CMM->menu->switchToTab("load");
|
||||
break;
|
||||
case EUserEvent::FULLSCREEN_TOGGLED:
|
||||
fullScreenChanged();
|
||||
break;
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.screenHandler().onScreenResize();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logGlobal->error("Unknown user event. Code %d", ev.user.code);
|
||||
break;
|
||||
@ -1015,7 +562,10 @@ static void handleEvent(SDL_Event & ev)
|
||||
switch (ev.window.event) {
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
#ifndef VCMI_IOS
|
||||
fullScreenChanged();
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
|
||||
GH.screenHandler().onScreenResize();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -1039,14 +589,14 @@ static void handleEvent(SDL_Event & ev)
|
||||
boost::unique_lock<boost::mutex> lock(eventsM);
|
||||
SDLEventsQueue.push(ev);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void mainLoop()
|
||||
{
|
||||
SettingsListener resChanged = settings.listen["video"]["fullscreen"];
|
||||
SettingsListener resChanged = settings.listen["video"]["resolution"];
|
||||
SettingsListener fsChanged = settings.listen["video"]["fullscreen"];
|
||||
resChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
fsChanged([](const JsonNode &newState){ CGuiHandler::pushUserEvent(EUserEvent::FULLSCREEN_TOGGLED); });
|
||||
|
||||
inGuiThread.reset(new bool(true));
|
||||
assert(GH.mainFPSmng);
|
||||
@ -1063,7 +613,6 @@ static void mainLoop()
|
||||
|
||||
CSH->applyPacksOnLobbyScreen();
|
||||
GH.renderFrame();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1100,29 +649,9 @@ static void quitApplication()
|
||||
vstd::clear_pointer(console);// should be removed after everything else since used by logging
|
||||
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(750));//???
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::destroy();
|
||||
}
|
||||
|
||||
cleanupRenderer();
|
||||
|
||||
if(nullptr != mainRenderer)
|
||||
{
|
||||
SDL_DestroyRenderer(mainRenderer);
|
||||
mainRenderer = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != mainWindow)
|
||||
{
|
||||
SDL_DestroyWindow(mainWindow);
|
||||
mainWindow = nullptr;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
GH.screenHandler().close();
|
||||
|
||||
if(logConfig != nullptr)
|
||||
{
|
||||
@ -1137,7 +666,6 @@ static void quitApplication()
|
||||
|
||||
void handleQuit(bool ask)
|
||||
{
|
||||
|
||||
if(CSH->client && LOCPLINT && ask)
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
|
@ -2,9 +2,10 @@ set(client_SRCS
|
||||
StdInc.cpp
|
||||
../CCallback.cpp
|
||||
|
||||
adventureMap/CAdvMapPanel.cpp
|
||||
adventureMap/CAdventureMapInterface.cpp
|
||||
adventureMap/CAdventureOptions.cpp
|
||||
adventureMap/AdventureMapInterface.cpp
|
||||
adventureMap/AdventureMapShortcuts.cpp
|
||||
adventureMap/AdventureMapWidget.cpp
|
||||
adventureMap/AdventureOptions.cpp
|
||||
adventureMap/CInGameConsole.cpp
|
||||
adventureMap/CInfoBar.cpp
|
||||
adventureMap/CList.cpp
|
||||
@ -75,6 +76,7 @@ set(client_SRCS
|
||||
renderSDL/SDLImage.cpp
|
||||
renderSDL/SDLImageLoader.cpp
|
||||
renderSDL/SDLRWwrapper.cpp
|
||||
renderSDL/ScreenHandler.cpp
|
||||
renderSDL/SDL_Extensions.cpp
|
||||
|
||||
widgets/Buttons.cpp
|
||||
@ -129,9 +131,11 @@ set(client_SRCS
|
||||
set(client_HEADERS
|
||||
StdInc.h
|
||||
|
||||
adventureMap/CAdvMapPanel.h
|
||||
adventureMap/CAdventureMapInterface.h
|
||||
adventureMap/CAdventureOptions.h
|
||||
adventureMap/AdventureMapInterface.h
|
||||
adventureMap/AdventureMapShortcuts.h
|
||||
adventureMap/AdventureMapWidget.h
|
||||
adventureMap/AdventureState.h
|
||||
adventureMap/AdventureOptions.h
|
||||
adventureMap/CInGameConsole.h
|
||||
adventureMap/CInfoBar.h
|
||||
adventureMap/CList.h
|
||||
@ -202,6 +206,7 @@ set(client_HEADERS
|
||||
render/IFont.h
|
||||
render/IImage.h
|
||||
render/IImageLoader.h
|
||||
render/IScreenHandler.h
|
||||
|
||||
renderSDL/CBitmapFont.h
|
||||
renderSDL/CBitmapHanFont.h
|
||||
@ -211,6 +216,7 @@ set(client_HEADERS
|
||||
renderSDL/SDLImage.h
|
||||
renderSDL/SDLImageLoader.h
|
||||
renderSDL/SDLRWwrapper.h
|
||||
renderSDL/ScreenHandler.h
|
||||
renderSDL/SDL_Extensions.h
|
||||
renderSDL/SDL_PixelAccess.h
|
||||
|
||||
|
@ -12,10 +12,9 @@
|
||||
|
||||
#include <vcmi/Artifact.h>
|
||||
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
#include "adventureMap/CList.h"
|
||||
#include "adventureMap/CInfoBar.h"
|
||||
#include "battle/BattleInterface.h"
|
||||
#include "battle/BattleEffectsController.h"
|
||||
#include "battle/BattleFieldController.h"
|
||||
@ -163,7 +162,7 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
|
||||
initializeHeroTownList();
|
||||
|
||||
// always recreate advmap interface to avoid possible memory-corruption bugs
|
||||
adventureInt.reset(new CAdventureMapInterface());
|
||||
adventureInt.reset(new AdventureMapInterface());
|
||||
}
|
||||
|
||||
void CPlayerInterface::playerStartsTurn(PlayerColor player)
|
||||
|
@ -31,7 +31,7 @@ struct CPathsInfo;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CButton;
|
||||
class CAdventureMapInterface;
|
||||
class AdventureMapInterface;
|
||||
class CCastleInterface;
|
||||
class BattleInterface;
|
||||
class CComponent;
|
||||
|
@ -123,7 +123,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
extern std::string NAME;
|
||||
static const std::string NAME_AFFIX = "client";
|
||||
static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
|
||||
CServerHandler::CServerHandler()
|
||||
: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "ClientNetPackVisitors.h"
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
#include "battle/BattleInterface.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:windowsSettings
|
||||
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
@ -15,7 +15,7 @@
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/CGTownInstance.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "adventureMap/CAdventureMapInterface.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
|
||||
PlayerLocalState::PlayerLocalState(CPlayerInterface & owner)
|
||||
: owner(owner)
|
||||
|
815
client/adventureMap/AdventureMapInterface.cpp
Normal file
@ -0,0 +1,815 @@
|
||||
/*
|
||||
* AdventureMapInterface.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 "AdventureMapInterface.h"
|
||||
|
||||
#include "AdventureOptions.h"
|
||||
#include "AdventureState.h"
|
||||
#include "CInGameConsole.h"
|
||||
#include "CMinimap.h"
|
||||
#include "CList.h"
|
||||
#include "CInfoBar.h"
|
||||
#include "MapAudioPlayer.h"
|
||||
#include "AdventureMapWidget.h"
|
||||
#include "AdventureMapShortcuts.h"
|
||||
|
||||
#include "../mapView/mapHandler.h"
|
||||
#include "../mapView/MapView.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../CMT.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
std::shared_ptr<AdventureMapInterface> adventureInt;
|
||||
|
||||
AdventureMapInterface::AdventureMapInterface():
|
||||
mapAudio(new MapAudioPlayer()),
|
||||
spellBeingCasted(nullptr),
|
||||
scrollingWasActive(false),
|
||||
scrollingWasBlocked(false)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
|
||||
|
||||
shortcuts = std::make_shared<AdventureMapShortcuts>(*this);
|
||||
|
||||
widget = std::make_shared<AdventureMapWidget>(shortcuts);
|
||||
shortcuts->setState(EAdventureState::MAKING_TURN);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
|
||||
addUsedEvents(KEYBOARD);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel)
|
||||
{
|
||||
shortcuts->onMapViewMoved(visibleArea, mapLevel);
|
||||
widget->getMinimap()->onMapViewMoved(visibleArea, mapLevel);
|
||||
widget->onMapViewMoved(visibleArea, mapLevel);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onAudioResumed()
|
||||
{
|
||||
mapAudio->onAudioResumed();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onAudioPaused()
|
||||
{
|
||||
mapAudio->onAudioPaused();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero)
|
||||
{
|
||||
widget->getInfoBar()->popAll();
|
||||
widget->getInfoBar()->showSelection();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
|
||||
{
|
||||
widget->getHeroList()->update(h);
|
||||
|
||||
if (h && h == LOCPLINT->localState->getCurrentHero() && !widget->getInfoBar()->showingComponents())
|
||||
widget->getInfoBar()->showSelection();
|
||||
|
||||
widget->updateActiveState();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTownChanged(const CGTownInstance * town)
|
||||
{
|
||||
widget->getTownList()->update(town);
|
||||
|
||||
if (town && town == LOCPLINT->localState->getCurrentTown() && !widget->getInfoBar()->showingComponents())
|
||||
widget->getInfoBar()->showSelection();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::showInfoBoxMessage(const std::vector<Component> & components, std::string message, int timer)
|
||||
{
|
||||
widget->getInfoBar()->pushComponents(components, message, timer);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::activate()
|
||||
{
|
||||
CIntObject::activate();
|
||||
|
||||
adjustActiveness();
|
||||
|
||||
screenBuf = screen;
|
||||
|
||||
if(LOCPLINT)
|
||||
{
|
||||
LOCPLINT->cingconsole->activate();
|
||||
LOCPLINT->cingconsole->pos = this->pos;
|
||||
}
|
||||
|
||||
GH.fakeMouseMove(); //to restore the cursor
|
||||
}
|
||||
|
||||
void AdventureMapInterface::deactivate()
|
||||
{
|
||||
CIntObject::deactivate();
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::showAll(SDL_Surface * to)
|
||||
{
|
||||
CIntObject::showAll(to);
|
||||
LOCPLINT->cingconsole->show(to);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::show(SDL_Surface * to)
|
||||
{
|
||||
handleMapScrollingUpdate();
|
||||
|
||||
CIntObject::show(to);
|
||||
LOCPLINT->cingconsole->show(to);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::handleMapScrollingUpdate()
|
||||
{
|
||||
/// Width of window border, in pixels, that triggers map scrolling
|
||||
static constexpr uint32_t borderScrollWidth = 15;
|
||||
|
||||
uint32_t timePassed = GH.mainFPSmng->getElapsedMilliseconds();
|
||||
uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float();
|
||||
uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000;
|
||||
|
||||
Point cursorPosition = GH.getCursorPosition();
|
||||
Point scrollDirection;
|
||||
|
||||
if (cursorPosition.x < borderScrollWidth)
|
||||
scrollDirection.x = -1;
|
||||
|
||||
if (cursorPosition.x > GH.screenDimensions().x - borderScrollWidth)
|
||||
scrollDirection.x = +1;
|
||||
|
||||
if (cursorPosition.y < borderScrollWidth)
|
||||
scrollDirection.y = -1;
|
||||
|
||||
if (cursorPosition.y > GH.screenDimensions().y - borderScrollWidth)
|
||||
scrollDirection.y = +1;
|
||||
|
||||
Point scrollDelta = scrollDirection * scrollDistance;
|
||||
|
||||
bool cursorInScrollArea = scrollDelta != Point(0,0);
|
||||
bool scrollingActive = cursorInScrollArea && active && shortcuts->optionSidePanelActive() && !scrollingWasBlocked;
|
||||
bool scrollingBlocked = GH.isKeyboardCtrlDown();
|
||||
|
||||
if (!scrollingWasActive && scrollingBlocked)
|
||||
{
|
||||
scrollingWasBlocked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cursorInScrollArea && scrollingWasBlocked)
|
||||
{
|
||||
scrollingWasBlocked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollingActive)
|
||||
widget->getMapView()->onMapScrolled(scrollDelta);
|
||||
|
||||
if (!scrollingActive && !scrollingWasActive)
|
||||
return;
|
||||
|
||||
if(scrollDelta.x > 0)
|
||||
{
|
||||
if(scrollDelta.y < 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST);
|
||||
if(scrollDelta.y > 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST);
|
||||
if(scrollDelta.y == 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_EAST);
|
||||
}
|
||||
if(scrollDelta.x < 0)
|
||||
{
|
||||
if(scrollDelta.y < 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST);
|
||||
if(scrollDelta.y > 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST);
|
||||
if(scrollDelta.y == 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_WEST);
|
||||
}
|
||||
|
||||
if (scrollDelta.x == 0)
|
||||
{
|
||||
if(scrollDelta.y < 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_NORTH);
|
||||
if(scrollDelta.y > 0)
|
||||
CCS->curh->set(Cursor::Map::SCROLL_SOUTH);
|
||||
if(scrollDelta.y == 0)
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
|
||||
scrollingWasActive = scrollingActive;
|
||||
}
|
||||
|
||||
void AdventureMapInterface::centerOnTile(int3 on)
|
||||
{
|
||||
widget->getMapView()->onCenteredTile(on);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::centerOnObject(const CGObjectInstance * obj)
|
||||
{
|
||||
widget->getMapView()->onCenteredObject(obj);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::keyPressed(EShortcut key)
|
||||
{
|
||||
if (key == EShortcut::GLOBAL_CANCEL && spellBeingCasted)
|
||||
hotkeyAbortCastingMode();
|
||||
|
||||
//fake mouse use to trigger onTileHovered()
|
||||
GH.fakeMouseMove();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onSelectionChanged(const CArmedInstance *sel)
|
||||
{
|
||||
assert(sel);
|
||||
|
||||
widget->getInfoBar()->popAll();
|
||||
mapAudio->onSelectionChanged(sel);
|
||||
bool centerView = !settings["session"]["autoSkip"].Bool();
|
||||
|
||||
if (centerView)
|
||||
centerOnObject(sel);
|
||||
|
||||
if(sel->ID==Obj::TOWN)
|
||||
{
|
||||
auto town = dynamic_cast<const CGTownInstance*>(sel);
|
||||
|
||||
widget->getInfoBar()->showTownSelection(town);
|
||||
widget->getTownList()->select(town);
|
||||
widget->getHeroList()->select(nullptr);
|
||||
onHeroChanged(nullptr);
|
||||
}
|
||||
else //hero selected
|
||||
{
|
||||
auto hero = dynamic_cast<const CGHeroInstance*>(sel);
|
||||
|
||||
widget->getInfoBar()->showHeroSelection(hero);
|
||||
widget->getHeroList()->select(hero);
|
||||
widget->getTownList()->select(nullptr);
|
||||
|
||||
LOCPLINT->localState->verifyPath(hero);
|
||||
onHeroChanged(hero);
|
||||
}
|
||||
|
||||
widget->updateActiveState();
|
||||
widget->getHeroList()->redraw();
|
||||
widget->getTownList()->redraw();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onMapTilesChanged(boost::optional<std::unordered_set<int3>> positions)
|
||||
{
|
||||
if (positions)
|
||||
widget->getMinimap()->updateTiles(*positions);
|
||||
else
|
||||
widget->getMinimap()->update();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onHotseatWaitStarted(PlayerColor playerID)
|
||||
{
|
||||
onCurrentPlayerChanged(playerID);
|
||||
setState(EAdventureState::HOTSEAT_WAIT);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID)
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return;
|
||||
|
||||
mapAudio->onEnemyTurnStarted();
|
||||
widget->getMinimap()->setAIRadar(true);
|
||||
widget->getInfoBar()->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer());
|
||||
setState(EAdventureState::ENEMY_TURN);
|
||||
|
||||
}
|
||||
|
||||
void AdventureMapInterface::setState(EAdventureState state)
|
||||
{
|
||||
shortcuts->setState(state);
|
||||
adjustActiveness();
|
||||
widget->updateActiveState();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::adjustActiveness()
|
||||
{
|
||||
bool widgetMustBeActive = active && shortcuts->optionSidePanelActive();
|
||||
bool mapViewMustBeActive = active && (shortcuts->optionMapViewActive());
|
||||
|
||||
if (widgetMustBeActive && !widget->active)
|
||||
widget->activate();
|
||||
|
||||
if (!widgetMustBeActive && widget->active)
|
||||
widget->deactivate();
|
||||
|
||||
if (mapViewMustBeActive && !widget->getMapView()->active)
|
||||
widget->getMapView()->activate();
|
||||
|
||||
if (!mapViewMustBeActive && widget->getMapView()->active)
|
||||
widget->getMapView()->deactivate();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(nullptr);
|
||||
|
||||
if (playerID == currentPlayerID)
|
||||
return;
|
||||
|
||||
currentPlayerID = playerID;
|
||||
widget->setPlayer(playerID);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
|
||||
{
|
||||
onCurrentPlayerChanged(playerID);
|
||||
|
||||
setState(EAdventureState::MAKING_TURN);
|
||||
if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID
|
||||
|| settings["session"]["spectate"].Bool())
|
||||
{
|
||||
widget->getMinimap()->setAIRadar(false);
|
||||
widget->getInfoBar()->showSelection();
|
||||
}
|
||||
|
||||
widget->getHeroList()->update();
|
||||
widget->getTownList()->update();
|
||||
|
||||
const CGHeroInstance * heroToSelect = nullptr;
|
||||
|
||||
// find first non-sleeping hero
|
||||
for (auto hero : LOCPLINT->localState->getWanderingHeroes())
|
||||
{
|
||||
if (!LOCPLINT->localState->isHeroSleeping(hero))
|
||||
{
|
||||
heroToSelect = hero;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//select first hero if available.
|
||||
if (heroToSelect != nullptr)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(heroToSelect);
|
||||
}
|
||||
else if (LOCPLINT->localState->getOwnedTowns().size())
|
||||
{
|
||||
LOCPLINT->localState->setSelection(LOCPLINT->localState->getOwnedTown(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCPLINT->localState->setSelection(LOCPLINT->localState->getWanderingHero(0));
|
||||
}
|
||||
|
||||
//show new day animation and sound on infobar
|
||||
widget->getInfoBar()->showDate();
|
||||
|
||||
onHeroChanged(nullptr);
|
||||
showAll(screen);
|
||||
mapAudio->onPlayerTurnStarted();
|
||||
|
||||
if(settings["session"]["autoSkip"].Bool() && !GH.isKeyboardShiftDown())
|
||||
{
|
||||
if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
|
||||
iw->close();
|
||||
|
||||
hotkeyEndingTurn();
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyEndingTurn()
|
||||
{
|
||||
if(settings["session"]["spectate"].Bool())
|
||||
return;
|
||||
|
||||
LOCPLINT->makingTurn = false;
|
||||
LOCPLINT->cb->endTurn();
|
||||
mapAudio->onPlayerTurnEnded();
|
||||
}
|
||||
|
||||
const CGObjectInstance* AdventureMapInterface::getActiveObject(const int3 &mapPos)
|
||||
{
|
||||
std::vector < const CGObjectInstance * > bobjs = LOCPLINT->cb->getBlockingObjs(mapPos); //blocking objects at tile
|
||||
|
||||
if (bobjs.empty())
|
||||
return nullptr;
|
||||
|
||||
return *boost::range::max_element(bobjs, &CMapHandler::compareObjectBlitOrder);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
|
||||
{
|
||||
if(!shortcuts->optionMapViewActive())
|
||||
return;
|
||||
|
||||
//FIXME: this line breaks H3 behavior for Dimension Door
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
return;
|
||||
if(!LOCPLINT->makingTurn)
|
||||
return;
|
||||
|
||||
const TerrainTile *tile = LOCPLINT->cb->getTile(mapPos);
|
||||
|
||||
const CGObjectInstance *topBlocking = getActiveObject(mapPos);
|
||||
|
||||
int3 selPos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
assert(shortcuts->optionSpellcasting());
|
||||
|
||||
if (!isInScreenRange(selPos, mapPos))
|
||||
return;
|
||||
|
||||
const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
|
||||
|
||||
switch(spellBeingCasted->id)
|
||||
{
|
||||
case SpellID::SCUTTLE_BOAT: //Scuttle Boat
|
||||
if(topBlocking && topBlocking->ID == Obj::BOAT)
|
||||
performSpellcasting(mapPos);
|
||||
break;
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
if(!tile || tile->isClear(heroTile))
|
||||
performSpellcasting(mapPos);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
//check if we can select this object
|
||||
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
|
||||
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
|
||||
|
||||
bool isHero = false;
|
||||
if(LOCPLINT->localState->getCurrentArmy()->ID != Obj::HERO) //hero is not selected (presumably town)
|
||||
{
|
||||
if(LOCPLINT->localState->getCurrentArmy() == topBlocking) //selected town clicked
|
||||
LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
|
||||
else if(canSelect)
|
||||
LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
|
||||
}
|
||||
else if(const CGHeroInstance * currentHero = LOCPLINT->localState->getCurrentHero()) //hero is selected
|
||||
{
|
||||
isHero = true;
|
||||
|
||||
const CGPathNode *pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(mapPos);
|
||||
if(currentHero == topBlocking) //clicked selected hero
|
||||
{
|
||||
LOCPLINT->openHeroWindow(currentHero);
|
||||
return;
|
||||
}
|
||||
else if(canSelect && pn->turns == 255 ) //selectable object at inaccessible tile
|
||||
{
|
||||
LOCPLINT->localState->setSelection(static_cast<const CArmedInstance*>(topBlocking));
|
||||
return;
|
||||
}
|
||||
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
|
||||
{
|
||||
if(LOCPLINT->localState->hasPath(currentHero) &&
|
||||
LOCPLINT->localState->getPath(currentHero).endPos() == mapPos)//we'll be moving
|
||||
{
|
||||
if(!CGI->mh->hasOngoingAnimations())
|
||||
LOCPLINT->moveHero(currentHero, LOCPLINT->localState->getPath(currentHero));
|
||||
return;
|
||||
}
|
||||
else //remove old path and find a new one if we clicked on accessible tile
|
||||
{
|
||||
LOCPLINT->localState->setPath(currentHero, mapPos);
|
||||
onHeroChanged(currentHero);
|
||||
}
|
||||
}
|
||||
} //end of hero is selected "case"
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Nothing is selected...");
|
||||
}
|
||||
|
||||
const auto shipyard = ourInaccessibleShipyard(topBlocking);
|
||||
if(isHero && shipyard != nullptr)
|
||||
{
|
||||
LOCPLINT->showShipyardDialogOrProblemPopup(shipyard);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTileHovered(const int3 &mapPos)
|
||||
{
|
||||
if(!shortcuts->optionMapViewActive())
|
||||
return;
|
||||
|
||||
//may occur just at the start of game (fake move before full intiialization)
|
||||
if(!LOCPLINT->localState->getCurrentArmy())
|
||||
return;
|
||||
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
GH.statusbar->clear();
|
||||
return;
|
||||
}
|
||||
auto objRelations = PlayerRelations::ALLIES;
|
||||
const CGObjectInstance *objAtTile = getActiveObject(mapPos);
|
||||
if(objAtTile)
|
||||
{
|
||||
objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
|
||||
std::string text = LOCPLINT->localState->getCurrentHero() ? objAtTile->getHoverText(LOCPLINT->localState->getCurrentHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
|
||||
boost::replace_all(text,"\n"," ");
|
||||
GH.statusbar->write(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string hlp = CGI->mh->getTerrainDescr(mapPos, false);
|
||||
GH.statusbar->write(hlp);
|
||||
}
|
||||
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
switch(spellBeingCasted->id)
|
||||
{
|
||||
case SpellID::SCUTTLE_BOAT:
|
||||
{
|
||||
int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||
|
||||
if(objAtTile && objAtTile->ID == Obj::BOAT && isInScreenRange(hpos, mapPos))
|
||||
CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
return;
|
||||
}
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
{
|
||||
const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
|
||||
int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
|
||||
if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
|
||||
CCS->curh->set(Cursor::Map::TELEPORT);
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(LOCPLINT->localState->getCurrentArmy()->ID == Obj::TOWN)
|
||||
{
|
||||
if(objAtTile)
|
||||
{
|
||||
if(objAtTile->ID == Obj::TOWN && objRelations != PlayerRelations::ENEMIES)
|
||||
CCS->curh->set(Cursor::Map::TOWN);
|
||||
else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
|
||||
CCS->curh->set(Cursor::Map::HERO);
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
else if(const CGHeroInstance * hero = LOCPLINT->localState->getCurrentHero())
|
||||
{
|
||||
std::array<Cursor::Map, 4> cursorMove = { Cursor::Map::T1_MOVE, Cursor::Map::T2_MOVE, Cursor::Map::T3_MOVE, Cursor::Map::T4_MOVE, };
|
||||
std::array<Cursor::Map, 4> cursorAttack = { Cursor::Map::T1_ATTACK, Cursor::Map::T2_ATTACK, Cursor::Map::T3_ATTACK, Cursor::Map::T4_ATTACK, };
|
||||
std::array<Cursor::Map, 4> cursorSail = { Cursor::Map::T1_SAIL, Cursor::Map::T2_SAIL, Cursor::Map::T3_SAIL, Cursor::Map::T4_SAIL, };
|
||||
std::array<Cursor::Map, 4> cursorDisembark = { Cursor::Map::T1_DISEMBARK, Cursor::Map::T2_DISEMBARK, Cursor::Map::T3_DISEMBARK, Cursor::Map::T4_DISEMBARK, };
|
||||
std::array<Cursor::Map, 4> cursorExchange = { Cursor::Map::T1_EXCHANGE, Cursor::Map::T2_EXCHANGE, Cursor::Map::T3_EXCHANGE, Cursor::Map::T4_EXCHANGE, };
|
||||
std::array<Cursor::Map, 4> cursorVisit = { Cursor::Map::T1_VISIT, Cursor::Map::T2_VISIT, Cursor::Map::T3_VISIT, Cursor::Map::T4_VISIT, };
|
||||
std::array<Cursor::Map, 4> cursorSailVisit = { Cursor::Map::T1_SAIL_VISIT, Cursor::Map::T2_SAIL_VISIT, Cursor::Map::T3_SAIL_VISIT, Cursor::Map::T4_SAIL_VISIT, };
|
||||
|
||||
const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(mapPos);
|
||||
assert(pathNode);
|
||||
|
||||
if((GH.isKeyboardAltDown() || settings["gameTweaks"]["forceMovementInfo"].Bool()) && pathNode->reachable()) //overwrite status bar text with movement info
|
||||
{
|
||||
showMoveDetailsInStatusbar(*hero, *pathNode);
|
||||
}
|
||||
|
||||
int turns = pathNode->turns;
|
||||
vstd::amin(turns, 3);
|
||||
switch(pathNode->action)
|
||||
{
|
||||
case CGPathNode::NORMAL:
|
||||
case CGPathNode::TELEPORT_NORMAL:
|
||||
if(pathNode->layer == EPathfindingLayer::LAND)
|
||||
CCS->curh->set(cursorMove[turns]);
|
||||
else
|
||||
CCS->curh->set(cursorSailVisit[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::VISIT:
|
||||
case CGPathNode::BLOCKING_VISIT:
|
||||
case CGPathNode::TELEPORT_BLOCKING_VISIT:
|
||||
if(objAtTile && objAtTile->ID == Obj::HERO)
|
||||
{
|
||||
if(LOCPLINT->localState->getCurrentArmy() == objAtTile)
|
||||
CCS->curh->set(Cursor::Map::HERO);
|
||||
else
|
||||
CCS->curh->set(cursorExchange[turns]);
|
||||
}
|
||||
else if(pathNode->layer == EPathfindingLayer::LAND)
|
||||
CCS->curh->set(cursorVisit[turns]);
|
||||
else
|
||||
CCS->curh->set(cursorSailVisit[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::BATTLE:
|
||||
case CGPathNode::TELEPORT_BATTLE:
|
||||
CCS->curh->set(cursorAttack[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::EMBARK:
|
||||
CCS->curh->set(cursorSail[turns]);
|
||||
break;
|
||||
|
||||
case CGPathNode::DISEMBARK:
|
||||
CCS->curh->set(cursorDisembark[turns]);
|
||||
break;
|
||||
|
||||
default:
|
||||
if(objAtTile && objRelations != PlayerRelations::ENEMIES)
|
||||
{
|
||||
if(objAtTile->ID == Obj::TOWN)
|
||||
CCS->curh->set(Cursor::Map::TOWN);
|
||||
else if(objAtTile->ID == Obj::HERO && objRelations == PlayerRelations::SAME_PLAYER)
|
||||
CCS->curh->set(Cursor::Map::HERO);
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
}
|
||||
else
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ourInaccessibleShipyard(objAtTile))
|
||||
{
|
||||
CCS->curh->set(Cursor::Map::T1_SAIL);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
|
||||
{
|
||||
const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement;
|
||||
const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
|
||||
const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;
|
||||
|
||||
std::string result = VLC->generaltexth->translate("vcmi.adventureMap", pathNode.turns > 0 ? "moveCostDetails" : "moveCostDetailsNoTurns");
|
||||
|
||||
boost::replace_first(result, "%TURNS", std::to_string(pathNode.turns));
|
||||
boost::replace_first(result, "%POINTS", std::to_string(movementPointsLastTurnCost));
|
||||
boost::replace_first(result, "%REMAINING", std::to_string(remainingPointsAfterMove));
|
||||
|
||||
GH.statusbar->write(result);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onTileRightClicked(const int3 &mapPos)
|
||||
{
|
||||
if(!shortcuts->optionMapViewActive())
|
||||
return;
|
||||
|
||||
if(spellBeingCasted)
|
||||
{
|
||||
hotkeyAbortCastingMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!LOCPLINT->cb->isVisible(mapPos))
|
||||
{
|
||||
CRClickPopup::createAndPush(VLC->generaltexth->allTexts[61]); //Uncharted Territory
|
||||
return;
|
||||
}
|
||||
|
||||
const CGObjectInstance * obj = getActiveObject(mapPos);
|
||||
if(!obj)
|
||||
{
|
||||
// Bare or undiscovered terrain
|
||||
const TerrainTile * tile = LOCPLINT->cb->getTile(mapPos);
|
||||
if(tile)
|
||||
{
|
||||
std::string hlp = CGI->mh->getTerrainDescr(mapPos, true);
|
||||
CRClickPopup::createAndPush(hlp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CRClickPopup::createAndPush(obj, GH.getCursorPosition(), ETextAlignment::CENTER);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::enterCastingMode(const CSpell * sp)
|
||||
{
|
||||
assert(sp->id == SpellID::SCUTTLE_BOAT || sp->id == SpellID::DIMENSION_DOOR);
|
||||
spellBeingCasted = sp;
|
||||
Settings config = settings.write["session"]["showSpellRange"];
|
||||
config->Bool() = true;
|
||||
|
||||
setState(EAdventureState::CASTING_SPELL);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::exitCastingMode()
|
||||
{
|
||||
assert(spellBeingCasted);
|
||||
spellBeingCasted = nullptr;
|
||||
setState(EAdventureState::MAKING_TURN);
|
||||
|
||||
Settings config = settings.write["session"]["showSpellRange"];
|
||||
config->Bool() = false;
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyAbortCastingMode()
|
||||
{
|
||||
exitCastingMode();
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled
|
||||
}
|
||||
|
||||
void AdventureMapInterface::performSpellcasting(const int3 & dest)
|
||||
{
|
||||
SpellID id = spellBeingCasted->id;
|
||||
exitCastingMode();
|
||||
LOCPLINT->cb->castSpell(LOCPLINT->localState->getCurrentHero(), id, dest);
|
||||
}
|
||||
|
||||
Rect AdventureMapInterface::terrainAreaPixels() const
|
||||
{
|
||||
return widget->getMapView()->pos;
|
||||
}
|
||||
|
||||
const IShipyard * AdventureMapInterface::ourInaccessibleShipyard(const CGObjectInstance *obj) const
|
||||
{
|
||||
const IShipyard *ret = IShipyard::castFrom(obj);
|
||||
|
||||
if(!ret ||
|
||||
obj->tempOwner != currentPlayerID ||
|
||||
(CCS->curh->get<Cursor::Map>() != Cursor::Map::T1_SAIL && CCS->curh->get<Cursor::Map>() != Cursor::Map::POINTER))
|
||||
return nullptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyExitWorldView()
|
||||
{
|
||||
setState(EAdventureState::MAKING_TURN);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::openWorldView(int tileSize)
|
||||
{
|
||||
setState(EAdventureState::WORLD_VIEW);
|
||||
widget->getMapView()->onViewWorldActivated(tileSize);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::openWorldView()
|
||||
{
|
||||
openWorldView(11);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain)
|
||||
{
|
||||
openWorldView(11);
|
||||
widget->getMapView()->onViewSpellActivated(11, objectPositions, showTerrain);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyNextTown()
|
||||
{
|
||||
widget->getTownList()->selectNext();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeySwitchMapLevel()
|
||||
{
|
||||
widget->getMapView()->onMapLevelSwitched();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onScreenResize()
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
widget.reset();
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
widget = std::make_shared<AdventureMapWidget>(shortcuts);
|
||||
widget->getMapView()->onViewMapActivated();
|
||||
widget->setPlayer(currentPlayerID);
|
||||
widget->updateActiveState();
|
||||
widget->getMinimap()->update();
|
||||
widget->getInfoBar()->showSelection();
|
||||
|
||||
adjustActiveness();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* CAdvMapInt.h, part of VCMI engine
|
||||
* AdventureMapInterface.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
@ -29,8 +29,8 @@ class CButton;
|
||||
class IImage;
|
||||
class CAnimImage;
|
||||
class CGStatusBar;
|
||||
class CAdvMapPanel;
|
||||
class CAdvMapWorldViewPanel;
|
||||
class AdventureMapWidget;
|
||||
class AdventureMapShortcuts;
|
||||
class CAnimation;
|
||||
class MapView;
|
||||
class CResDataBar;
|
||||
@ -39,111 +39,56 @@ class CTownList;
|
||||
class CInfoBar;
|
||||
class CMinimap;
|
||||
class MapAudioPlayer;
|
||||
enum class EAdventureState;
|
||||
|
||||
struct MapDrawingInfo;
|
||||
|
||||
/// That's a huge class which handles general adventure map actions and
|
||||
/// shows the right menu(questlog, spellbook, end turn,..) from where you
|
||||
/// can get to the towns and heroes.
|
||||
class CAdventureMapInterface : public CIntObject
|
||||
class AdventureMapInterface : public CIntObject
|
||||
{
|
||||
private:
|
||||
enum class EGameState
|
||||
{
|
||||
NOT_INITIALIZED,
|
||||
HOTSEAT_WAIT,
|
||||
MAKING_TURN,
|
||||
ENEMY_TURN,
|
||||
WORLD_VIEW
|
||||
};
|
||||
|
||||
EGameState state;
|
||||
|
||||
/// currently acting player
|
||||
PlayerColor currentPlayerID;
|
||||
|
||||
/// uses EDirections enum
|
||||
bool scrollingCursorSet;
|
||||
/// if true, cursor was changed to scrolling and must be reset back once scroll is over
|
||||
bool scrollingWasActive;
|
||||
|
||||
const CSpell *spellBeingCasted; //nullptr if none
|
||||
/// if true, then scrolling was blocked via ctrl and should not restart until player move cursor outside scrolling area
|
||||
bool scrollingWasBlocked;
|
||||
|
||||
std::vector<std::shared_ptr<CAnimImage>> gems;
|
||||
|
||||
std::shared_ptr<IImage> bg;
|
||||
std::shared_ptr<IImage> bgWorldView;
|
||||
std::shared_ptr<CButton> kingOverview;
|
||||
std::shared_ptr<CButton> sleepWake;
|
||||
std::shared_ptr<CButton> underground;
|
||||
std::shared_ptr<CButton> questlog;
|
||||
std::shared_ptr<CButton> moveHero;
|
||||
std::shared_ptr<CButton> spellbook;
|
||||
std::shared_ptr<CButton> advOptions;
|
||||
std::shared_ptr<CButton> sysOptions;
|
||||
std::shared_ptr<CButton> nextHero;
|
||||
std::shared_ptr<CButton> endTurn;
|
||||
std::shared_ptr<CButton> worldViewUnderground;
|
||||
|
||||
std::shared_ptr<MapView> terrain;
|
||||
std::shared_ptr<CMinimap> minimap;
|
||||
std::shared_ptr<CHeroList> heroList;
|
||||
std::shared_ptr<CTownList> townList;
|
||||
std::shared_ptr<CInfoBar> infoBar;
|
||||
std::shared_ptr<CGStatusBar> statusbar;
|
||||
std::shared_ptr<CResDataBar> resdatabar;
|
||||
|
||||
std::shared_ptr<CAdvMapPanel> panelMain; // panel that holds all right-side buttons in normal view
|
||||
std::shared_ptr<CAdvMapWorldViewPanel> panelWorldView; // panel that holds all buttons and other ui in world view
|
||||
std::shared_ptr<CAdvMapPanel> activeMapPanel; // currently active panel (either main or world view, depending on current mode)
|
||||
|
||||
std::shared_ptr<CAnimation> worldViewIcons;// images for world view overlay
|
||||
/// spell for which player is selecting target, or nullptr if none
|
||||
const CSpell *spellBeingCasted;
|
||||
|
||||
std::shared_ptr<MapAudioPlayer> mapAudio;
|
||||
std::shared_ptr<AdventureMapWidget> widget;
|
||||
std::shared_ptr<AdventureMapShortcuts> shortcuts;
|
||||
|
||||
private:
|
||||
//functions bound to buttons
|
||||
void fshowOverview();
|
||||
void fworldViewBack();
|
||||
void fworldViewScale1x();
|
||||
void fworldViewScale2x();
|
||||
void fworldViewScale4x();
|
||||
void fswitchLevel();
|
||||
void fshowQuestlog();
|
||||
void fsleepWake();
|
||||
void fmoveHero();
|
||||
void fshowSpellbok();
|
||||
void fadventureOPtions();
|
||||
void fsystemOptions();
|
||||
void fnextHero();
|
||||
void fendTurn();
|
||||
void setState(EAdventureState state);
|
||||
|
||||
void hotkeyMoveHeroDirectional(Point direction);
|
||||
/// updates active state of game window whenever game state changes
|
||||
void adjustActiveness();
|
||||
|
||||
bool isActive();
|
||||
void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
|
||||
|
||||
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const; //checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
|
||||
|
||||
// update locked state of buttons
|
||||
void updateButtons();
|
||||
/// checks if obj is our ashipyard and cursor is 0,0 -> returns shipyard or nullptr else
|
||||
const IShipyard * ourInaccessibleShipyard(const CGObjectInstance *obj) const;
|
||||
|
||||
/// check and if necessary reacts on scrolling by moving cursor to screen edge
|
||||
void handleMapScrollingUpdate();
|
||||
|
||||
void showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode);
|
||||
|
||||
const CGObjectInstance *getActiveObject(const int3 &tile);
|
||||
|
||||
std::optional<Point> keyToMoveDirection(EShortcut key);
|
||||
|
||||
void endingTurn();
|
||||
|
||||
/// exits currently opened world view mode and returns to normal map
|
||||
void exitWorldView();
|
||||
void exitCastingMode();
|
||||
void leaveCastingMode(const int3 & castTarget);
|
||||
void abortCastingMode();
|
||||
|
||||
/// casts current spell at specified location
|
||||
void performSpellcasting(const int3 & castTarget);
|
||||
|
||||
protected:
|
||||
// CIntObject interface implementation
|
||||
/// CIntObject interface implementation
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
@ -153,8 +98,16 @@ protected:
|
||||
|
||||
void keyPressed(EShortcut key) override;
|
||||
|
||||
void onScreenResize() override;
|
||||
|
||||
public:
|
||||
CAdventureMapInterface();
|
||||
AdventureMapInterface();
|
||||
|
||||
void hotkeyAbortCastingMode();
|
||||
void hotkeyExitWorldView();
|
||||
void hotkeyEndingTurn();
|
||||
void hotkeyNextTown();
|
||||
void hotkeySwitchMapLevel();
|
||||
|
||||
/// Called by PlayerInterface when specified player is ready to start his turn
|
||||
void onHotseatWaitStarted(PlayerColor playerID);
|
||||
@ -225,4 +178,4 @@ public:
|
||||
void openWorldView(const std::vector<ObjectPosInfo>& objectPositions, bool showTerrain);
|
||||
};
|
||||
|
||||
extern std::shared_ptr<CAdventureMapInterface> adventureInt;
|
||||
extern std::shared_ptr<AdventureMapInterface> adventureInt;
|
446
client/adventureMap/AdventureMapShortcuts.cpp
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* AdventureMapShortcuts.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 "AdventureMapShortcuts.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../lobby/CSavingScreen.h"
|
||||
#include "../mapView/mapHandler.h"
|
||||
#include "../windows/CKingdomInterface.h"
|
||||
#include "../windows/CSpellWindow.h"
|
||||
#include "../windows/CTradeWindow.h"
|
||||
#include "../windows/settings/SettingsMainWindow.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
#include "AdventureOptions.h"
|
||||
#include "AdventureState.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CPathfinder.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
AdventureMapShortcuts::AdventureMapShortcuts(AdventureMapInterface & owner)
|
||||
: owner(owner)
|
||||
, state(EAdventureState::NOT_INITIALIZED)
|
||||
, mapLevel(0)
|
||||
{}
|
||||
|
||||
void AdventureMapShortcuts::setState(EAdventureState newState)
|
||||
{
|
||||
state = newState;
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::onMapViewMoved(const Rect & visibleArea, int newMapLevel)
|
||||
{
|
||||
mapLevel = newMapLevel;
|
||||
}
|
||||
|
||||
std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
|
||||
{
|
||||
std::vector<AdventureMapShortcutState> result = {
|
||||
{ EShortcut::ADVENTURE_KINGDOM_OVERVIEW, optionInMapView(), [this]() { this->showOverview(); } },
|
||||
{ EShortcut::ADVENTURE_EXIT_WORLD_VIEW, optionInWorldView(), [this]() { this->worldViewBack(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD, optionInMapView(), [this]() { this->worldViewScale1x(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD_X1, optionInWorldView(), [this]() { this->worldViewScale1x(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD_X2, optionInWorldView(), [this]() { this->worldViewScale2x(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_WORLD_X4, optionInWorldView(), [this]() { this->worldViewScale4x(); } },
|
||||
{ EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL, optionCanToggleLevel(), [this]() { this->switchMapLevel(); } },
|
||||
{ EShortcut::ADVENTURE_QUEST_LOG, optionCanViewQuests(), [this]() { this->showQuestlog(); } },
|
||||
{ EShortcut::ADVENTURE_TOGGLE_SLEEP, optionHeroSelected(), [this]() { this->toggleSleepWake(); } },
|
||||
{ EShortcut::ADVENTURE_SET_HERO_ASLEEP, optionHeroAwake(), [this]() { this->setHeroSleeping(); } },
|
||||
{ EShortcut::ADVENTURE_SET_HERO_AWAKE, optionHeroSleeping(), [this]() { this->setHeroAwake(); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO, optionHeroCanMove(), [this]() { this->moveHeroAlongPath(); } },
|
||||
{ EShortcut::ADVENTURE_CAST_SPELL, optionHeroSelected(), [this]() { this->showSpellbook(); } },
|
||||
{ EShortcut::ADVENTURE_GAME_OPTIONS, optionInMapView(), [this]() { this->adventureOptions(); } },
|
||||
{ EShortcut::GLOBAL_OPTIONS, optionInMapView(), [this]() { this->systemOptions(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_HERO, optionHasNextHero(), [this]() { this->nextHero(); } },
|
||||
{ EShortcut::GAME_END_TURN, optionInMapView(), [this]() { this->endTurn(); } },
|
||||
{ EShortcut::ADVENTURE_THIEVES_GUILD, optionInMapView(), [this]() { this->showThievesGuild(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SCENARIO, optionInMapView(), [this]() { this->showScenarioInfo(); } },
|
||||
{ EShortcut::GAME_SAVE_GAME, optionInMapView(), [this]() { this->saveGame(); } },
|
||||
{ EShortcut::GAME_LOAD_GAME, optionInMapView(), [this]() { this->loadGame(); } },
|
||||
{ EShortcut::ADVENTURE_DIG_GRAIL, optionHeroSelected(), [this]() { this->digGrail(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_PUZZLE, optionSidePanelActive(),[this]() { this->viewPuzzleMap(); } },
|
||||
{ EShortcut::GAME_RESTART_GAME, optionInMapView(), [this]() { this->restartGame(); } },
|
||||
{ EShortcut::ADVENTURE_VISIT_OBJECT, optionHeroSelected(), [this]() { this->visitObject(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SELECTED, optionInMapView(), [this]() { this->openObject(); } },
|
||||
{ EShortcut::GAME_OPEN_MARKETPLACE, optionInMapView(), [this]() { this->showMarketplace(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_TOWN, optionInMapView(), [this]() { this->nextTown(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_OBJECT, optionInMapView(), [this]() { this->nextObject(); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, +1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SS, optionHeroSelected(), [this]() { this->moveHeroDirectional({ 0, +1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, +1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_WW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, 0}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_EE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, 0}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_NW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, -1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_NN, optionHeroSelected(), [this]() { this->moveHeroDirectional({ 0, -1}); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_NE, optionHeroSelected(), [this]() { this->moveHeroDirectional({+1, -1}); } }
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showOverview()
|
||||
{
|
||||
GH.pushIntT<CKingdomInterface>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewBack()
|
||||
{
|
||||
owner.hotkeyExitWorldView();
|
||||
|
||||
auto hero = LOCPLINT->localState->getCurrentHero();
|
||||
if (hero)
|
||||
owner.centerOnObject(hero);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewScale1x()
|
||||
{
|
||||
// TODO set corresponding scale button to "selected" mode
|
||||
owner.openWorldView(7);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewScale2x()
|
||||
{
|
||||
owner.openWorldView(11);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::worldViewScale4x()
|
||||
{
|
||||
owner.openWorldView(16);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::switchMapLevel()
|
||||
{
|
||||
int maxLevels = LOCPLINT->cb->getMapSize().z;
|
||||
if (maxLevels < 2)
|
||||
return;
|
||||
|
||||
owner.hotkeySwitchMapLevel();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showQuestlog()
|
||||
{
|
||||
LOCPLINT->showQuestLog();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::toggleSleepWake()
|
||||
{
|
||||
if (!optionHeroSelected())
|
||||
return;
|
||||
|
||||
if (optionHeroAwake())
|
||||
setHeroSleeping();
|
||||
else
|
||||
setHeroAwake();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::setHeroSleeping()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
if (h)
|
||||
{
|
||||
LOCPLINT->localState->setHeroAsleep(h);
|
||||
owner.onHeroChanged(h);
|
||||
nextHero();
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::setHeroAwake()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
if (h)
|
||||
{
|
||||
LOCPLINT->localState->setHeroAwaken(h);
|
||||
owner.onHeroChanged(h);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::moveHeroAlongPath()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
if (!h || !LOCPLINT->localState->hasPath(h))
|
||||
return;
|
||||
|
||||
LOCPLINT->moveHero(h, LOCPLINT->localState->getPath(h));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showSpellbook()
|
||||
{
|
||||
if (!LOCPLINT->localState->getCurrentHero())
|
||||
return;
|
||||
|
||||
owner.centerOnObject(LOCPLINT->localState->getCurrentHero());
|
||||
|
||||
GH.pushIntT<CSpellWindow>(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::adventureOptions()
|
||||
{
|
||||
GH.pushIntT<AdventureOptions>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::systemOptions()
|
||||
{
|
||||
GH.pushIntT<SettingsMainWindow>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextHero()
|
||||
{
|
||||
const auto * currHero = LOCPLINT->localState->getCurrentHero();
|
||||
const auto * nextHero = LOCPLINT->localState->getNextWanderingHero(currHero);
|
||||
|
||||
if (nextHero)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(nextHero);
|
||||
owner.centerOnObject(nextHero);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::endTurn()
|
||||
{
|
||||
if(!LOCPLINT->makingTurn)
|
||||
return;
|
||||
|
||||
if(settings["adventure"]["heroReminder"].Bool())
|
||||
{
|
||||
for(auto hero : LOCPLINT->localState->getWanderingHeroes())
|
||||
{
|
||||
if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0)
|
||||
{
|
||||
// Only show hero reminder if conditions met:
|
||||
// - There still movement points
|
||||
// - Hero don't have a path or there not points for first step on path
|
||||
LOCPLINT->localState->verifyPath(hero);
|
||||
|
||||
if(!LOCPLINT->localState->hasPath(hero))
|
||||
{
|
||||
LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], [this](){ owner.hotkeyEndingTurn(); }, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = LOCPLINT->localState->getPath(hero);
|
||||
if (path.nodes.size() < 2 || path.nodes[path.nodes.size() - 2].turns)
|
||||
{
|
||||
LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], [this](){ owner.hotkeyEndingTurn(); }, nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
owner.hotkeyEndingTurn();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showThievesGuild()
|
||||
{
|
||||
//find first town with tavern
|
||||
auto itr = range::find_if(LOCPLINT->localState->getOwnedTowns(), [](const CGTownInstance * town)
|
||||
{
|
||||
return town->hasBuilt(BuildingID::TAVERN);
|
||||
});
|
||||
|
||||
if(itr != LOCPLINT->localState->getOwnedTowns().end())
|
||||
LOCPLINT->showThievesGuildWindow(*itr);
|
||||
else
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showScenarioInfo()
|
||||
{
|
||||
AdventureOptions::showScenarioInfo();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::saveGame()
|
||||
{
|
||||
GH.pushIntT<CSavingScreen>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::loadGame()
|
||||
{
|
||||
LOCPLINT->proposeLoadingGame();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::digGrail()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
|
||||
if(h && LOCPLINT->makingTurn)
|
||||
LOCPLINT->tryDiggging(h);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::viewPuzzleMap()
|
||||
{
|
||||
LOCPLINT->showPuzzleMap();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::restartGame()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"),
|
||||
[](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::visitObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
|
||||
if(h)
|
||||
LOCPLINT->cb->moveHero(h,h->pos);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::openObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
const CGTownInstance *t = LOCPLINT->localState->getCurrentTown();
|
||||
if(h)
|
||||
LOCPLINT->openHeroWindow(h);
|
||||
|
||||
if(t)
|
||||
LOCPLINT->openTownWindow(t);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::showMarketplace()
|
||||
{
|
||||
//check if we have any marketplace
|
||||
const CGTownInstance *townWithMarket = nullptr;
|
||||
for(const CGTownInstance *t : LOCPLINT->cb->getTownsInfo())
|
||||
{
|
||||
if(t->hasBuilt(BuildingID::MARKETPLACE))
|
||||
{
|
||||
townWithMarket = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(townWithMarket) //if any town has marketplace, open window
|
||||
GH.pushIntT<CMarketplaceWindow>(townWithMarket);
|
||||
else //if not - complain
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextTown()
|
||||
{
|
||||
owner.hotkeyNextTown();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextObject()
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
|
||||
const CGTownInstance *t = LOCPLINT->localState->getCurrentTown();
|
||||
if(h)
|
||||
nextHero();
|
||||
|
||||
if(t)
|
||||
nextTown();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::moveHeroDirectional(const Point & direction)
|
||||
{
|
||||
const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero
|
||||
|
||||
if(!h)
|
||||
return;
|
||||
|
||||
if (CGI->mh->hasOngoingAnimations())
|
||||
return;
|
||||
|
||||
int3 dst = h->visitablePos() + int3(direction.x, direction.y, 0);
|
||||
|
||||
if (!CGI->mh->isInMap((dst)))
|
||||
return;
|
||||
|
||||
if ( !LOCPLINT->localState->setPath(h, dst))
|
||||
return;
|
||||
|
||||
const CGPath & path = LOCPLINT->localState->getPath(h);
|
||||
|
||||
if (path.nodes.size() > 2)
|
||||
owner.onHeroChanged(h);
|
||||
else
|
||||
if(path.nodes[0].turns == 0)
|
||||
LOCPLINT->moveHero(h, path);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionCanViewQuests()
|
||||
{
|
||||
return optionInMapView() && CGI->mh->getMap()->quests.empty();
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionCanToggleLevel()
|
||||
{
|
||||
return optionInMapView() && LOCPLINT->cb->getMapSize().z > 0;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionMapLevelSurface()
|
||||
{
|
||||
return mapLevel == 0;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroSleeping()
|
||||
{
|
||||
const CGHeroInstance *hero = LOCPLINT->localState->getCurrentHero();
|
||||
return optionInMapView() && hero && LOCPLINT->localState->isHeroSleeping(hero);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroAwake()
|
||||
{
|
||||
const CGHeroInstance *hero = LOCPLINT->localState->getCurrentHero();
|
||||
return optionInMapView() && hero && !LOCPLINT->localState->isHeroSleeping(hero);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroSelected()
|
||||
{
|
||||
return optionInMapView() && LOCPLINT->localState->getCurrentHero() != nullptr;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHeroCanMove()
|
||||
{
|
||||
const auto * hero = LOCPLINT->localState->getCurrentHero();
|
||||
return optionInMapView() && hero && hero->movement != 0 && LOCPLINT->localState->hasPath(hero);
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionHasNextHero()
|
||||
{
|
||||
const auto * hero = LOCPLINT->localState->getCurrentHero();
|
||||
const auto * nextSuitableHero = LOCPLINT->localState->getNextWanderingHero(hero);
|
||||
|
||||
return optionInMapView() && nextSuitableHero != nullptr;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionSpellcasting()
|
||||
{
|
||||
return state == EAdventureState::CASTING_SPELL;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionInMapView()
|
||||
{
|
||||
return state == EAdventureState::MAKING_TURN;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionInWorldView()
|
||||
{
|
||||
return state == EAdventureState::WORLD_VIEW;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionSidePanelActive()
|
||||
{
|
||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionMapViewActive()
|
||||
{
|
||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW || state == EAdventureState::CASTING_SPELL;
|
||||
}
|
87
client/adventureMap/AdventureMapShortcuts.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* AdventureMapShortcuts.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 Point;
|
||||
class Rect;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
enum class EShortcut;
|
||||
class AdventureMapInterface;
|
||||
enum class EAdventureState;
|
||||
|
||||
struct AdventureMapShortcutState
|
||||
{
|
||||
EShortcut shortcut;
|
||||
bool isEnabled;
|
||||
std::function<void()> callback;
|
||||
};
|
||||
|
||||
/// Class that contains list of functions for shortcuts available from adventure map
|
||||
class AdventureMapShortcuts
|
||||
{
|
||||
AdventureMapInterface & owner;
|
||||
EAdventureState state;
|
||||
int mapLevel;
|
||||
|
||||
void showOverview();
|
||||
void worldViewBack();
|
||||
void worldViewScale1x();
|
||||
void worldViewScale2x();
|
||||
void worldViewScale4x();
|
||||
void switchMapLevel();
|
||||
void showQuestlog();
|
||||
void toggleSleepWake();
|
||||
void setHeroSleeping();
|
||||
void setHeroAwake();
|
||||
void moveHeroAlongPath();
|
||||
void showSpellbook();
|
||||
void adventureOptions();
|
||||
void systemOptions();
|
||||
void nextHero();
|
||||
void endTurn();
|
||||
void showThievesGuild();
|
||||
void showScenarioInfo();
|
||||
void saveGame();
|
||||
void loadGame();
|
||||
void digGrail();
|
||||
void viewPuzzleMap();
|
||||
void restartGame();
|
||||
void visitObject();
|
||||
void openObject();
|
||||
void showMarketplace();
|
||||
void nextTown();
|
||||
void nextObject();
|
||||
void moveHeroDirectional(const Point & direction);
|
||||
|
||||
public:
|
||||
explicit AdventureMapShortcuts(AdventureMapInterface & owner);
|
||||
|
||||
std::vector<AdventureMapShortcutState> getShortcuts();
|
||||
|
||||
bool optionCanViewQuests();
|
||||
bool optionCanToggleLevel();
|
||||
bool optionMapLevelSurface();
|
||||
bool optionHeroSleeping();
|
||||
bool optionHeroAwake();
|
||||
bool optionHeroSelected();
|
||||
bool optionHeroCanMove();
|
||||
bool optionHasNextHero();
|
||||
bool optionSpellcasting();
|
||||
bool optionInMapView();
|
||||
bool optionInWorldView();
|
||||
bool optionSidePanelActive();
|
||||
bool optionMapViewActive();
|
||||
|
||||
void setState(EAdventureState newState);
|
||||
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
|
||||
};
|
455
client/adventureMap/AdventureMapWidget.cpp
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* CAdventureMapWidget.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 "AdventureMapWidget.h"
|
||||
|
||||
#include "AdventureMapShortcuts.h"
|
||||
#include "CInfoBar.h"
|
||||
#include "CList.h"
|
||||
#include "CMinimap.h"
|
||||
#include "CResDataBar.h"
|
||||
#include "AdventureState.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../mapView/MapView.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
|
||||
#include "../../lib/StringConstants.h"
|
||||
#include "../../lib/filesystem/ResourceID.h"
|
||||
|
||||
AdventureMapWidget::AdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts )
|
||||
: shortcuts(shortcuts)
|
||||
, mapLevel(0)
|
||||
{
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
REGISTER_BUILDER("adventureInfobar", &AdventureMapWidget::buildInfobox );
|
||||
REGISTER_BUILDER("adventureMapImage", &AdventureMapWidget::buildMapImage );
|
||||
REGISTER_BUILDER("adventureMapButton", &AdventureMapWidget::buildMapButton );
|
||||
REGISTER_BUILDER("adventureMapContainer", &AdventureMapWidget::buildMapContainer );
|
||||
REGISTER_BUILDER("adventureMapGameArea", &AdventureMapWidget::buildMapGameArea );
|
||||
REGISTER_BUILDER("adventureMapHeroList", &AdventureMapWidget::buildMapHeroList );
|
||||
REGISTER_BUILDER("adventureMapIcon", &AdventureMapWidget::buildMapIcon );
|
||||
REGISTER_BUILDER("adventureMapTownList", &AdventureMapWidget::buildMapTownList );
|
||||
REGISTER_BUILDER("adventureMinimap", &AdventureMapWidget::buildMinimap );
|
||||
REGISTER_BUILDER("adventureResourceDateBar", &AdventureMapWidget::buildResourceDateBar );
|
||||
REGISTER_BUILDER("adventureStatusBar", &AdventureMapWidget::buildStatusBar );
|
||||
REGISTER_BUILDER("adventurePlayerTexture", &AdventureMapWidget::buildTexturePlayerColored);
|
||||
|
||||
for (const auto & entry : shortcuts->getShortcuts())
|
||||
addShortcut(entry.shortcut, entry.callback);
|
||||
|
||||
const JsonNode config(ResourceID("config/widgets/adventureMap.json"));
|
||||
|
||||
for(const auto & entry : config["options"]["imagesPlayerColored"].Vector())
|
||||
{
|
||||
ResourceID resourceName(entry.String(), EResType::IMAGE);
|
||||
playerColorerImages.push_back(resourceName.getName());
|
||||
}
|
||||
|
||||
build(config);
|
||||
addUsedEvents(KEYBOARD);
|
||||
}
|
||||
|
||||
void AdventureMapWidget::onMapViewMoved(const Rect & visibleArea, int newMapLevel)
|
||||
{
|
||||
if(mapLevel == newMapLevel)
|
||||
return;
|
||||
|
||||
mapLevel = newMapLevel;
|
||||
updateActiveState();
|
||||
}
|
||||
|
||||
Rect AdventureMapWidget::readSourceArea(const JsonNode & source, const JsonNode & sourceCommon)
|
||||
{
|
||||
const auto & input = source.isNull() ? sourceCommon : source;
|
||||
|
||||
return readArea(input, Rect(Point(0, 0), Point(800, 600)));
|
||||
}
|
||||
|
||||
Rect AdventureMapWidget::readTargetArea(const JsonNode & source)
|
||||
{
|
||||
if(subwidgetSizes.empty())
|
||||
return readArea(source, pos);
|
||||
return readArea(source, subwidgetSizes.back());
|
||||
}
|
||||
|
||||
Rect AdventureMapWidget::readArea(const JsonNode & source, const Rect & boundingBox)
|
||||
{
|
||||
const auto & object = source.Struct();
|
||||
|
||||
if(object.count("left") + object.count("width") + object.count("right") != 2)
|
||||
logGlobal->error("Invalid area definition in widget! Unable to load width!");
|
||||
|
||||
if(object.count("top") + object.count("height") + object.count("bottom") != 2)
|
||||
logGlobal->error("Invalid area definition in widget! Unable to load height!");
|
||||
|
||||
int left = source["left"].Integer();
|
||||
int width = source["width"].Integer();
|
||||
int right = source["right"].Integer();
|
||||
|
||||
int top = source["top"].Integer();
|
||||
int height = source["height"].Integer();
|
||||
int bottom = source["bottom"].Integer();
|
||||
|
||||
Point topLeft(left, top);
|
||||
Point dimensions(width, height);
|
||||
|
||||
if(source["left"].isNull())
|
||||
topLeft.x = boundingBox.w - right - width;
|
||||
|
||||
if(source["width"].isNull())
|
||||
dimensions.x = boundingBox.w - right - left;
|
||||
|
||||
if(source["top"].isNull())
|
||||
topLeft.y = boundingBox.h - bottom - height;
|
||||
|
||||
if(source["height"].isNull())
|
||||
dimensions.y = boundingBox.h - bottom - top;
|
||||
|
||||
return Rect(topLeft + boundingBox.topLeft(), dimensions);
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> AdventureMapWidget::loadImage(const std::string & name)
|
||||
{
|
||||
ResourceID resource(name, EResType::IMAGE);
|
||||
|
||||
if(images.count(resource.getName()) == 0)
|
||||
images[resource.getName()] = IImage::createFromFile(resource.getName());
|
||||
|
||||
return images[resource.getName()];
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> AdventureMapWidget::loadAnimation(const std::string & name)
|
||||
{
|
||||
ResourceID resource(name, EResType::ANIMATION);
|
||||
|
||||
if(animations.count(resource.getName()) == 0)
|
||||
animations[resource.getName()] = std::make_shared<CAnimation>(resource.getName());
|
||||
|
||||
return animations[resource.getName()];
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildInfobox(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
infoBar = std::make_shared<CInfoBar>(area);
|
||||
return infoBar;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapImage(const JsonNode & input)
|
||||
{
|
||||
Rect targetArea = readTargetArea(input["area"]);
|
||||
Rect sourceArea = readSourceArea(input["sourceArea"], input["area"]);
|
||||
std::string image = input["image"].String();
|
||||
|
||||
return std::make_shared<CFilledTexture>(loadImage(image), targetArea, sourceArea);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapButton(const JsonNode & input)
|
||||
{
|
||||
auto position = readTargetArea(input["area"]);
|
||||
auto image = input["image"].String();
|
||||
auto help = readHintText(input["help"]);
|
||||
bool playerColored = input["playerColored"].Bool();
|
||||
|
||||
auto button = std::make_shared<CButton>(position.topLeft(), image, help, 0, EShortcut::NONE, playerColored);
|
||||
|
||||
loadButtonBorderColor(button, input["borderColor"]);
|
||||
loadButtonHotkey(button, input["hotkey"]);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapContainer(const JsonNode & input)
|
||||
{
|
||||
auto position = readTargetArea(input["area"]);
|
||||
std::shared_ptr<CAdventureMapContainerWidget> result;
|
||||
|
||||
if (!input["exists"].isNull())
|
||||
{
|
||||
if (!input["exists"]["heightMin"].isNull() && input["exists"]["heightMin"].Integer() >= pos.h)
|
||||
return nullptr;
|
||||
if (!input["exists"]["heightMax"].isNull() && input["exists"]["heightMax"].Integer() < pos.h)
|
||||
return nullptr;
|
||||
if (!input["exists"]["widthMin"].isNull() && input["exists"]["widthMin"].Integer() >= pos.w)
|
||||
return nullptr;
|
||||
if (!input["exists"]["widthMax"].isNull() && input["exists"]["widthMax"].Integer() < pos.w)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (input["overlay"].Bool())
|
||||
result = std::make_shared<CAdventureMapOverlayWidget>();
|
||||
else
|
||||
result = std::make_shared<CAdventureMapContainerWidget>();
|
||||
|
||||
result->disableCondition = input["hideWhen"].String();
|
||||
|
||||
result->moveBy(position.topLeft());
|
||||
subwidgetSizes.push_back(position);
|
||||
for(const auto & entry : input["items"].Vector())
|
||||
{
|
||||
auto widget = buildWidget(entry);
|
||||
|
||||
addWidget(entry["name"].String(), widget);
|
||||
result->ownedChildren.push_back(widget);
|
||||
|
||||
// FIXME: remove cast and replace it with better check
|
||||
if (std::dynamic_pointer_cast<CLabel>(widget) || std::dynamic_pointer_cast<CLabelGroup>(widget))
|
||||
result->addChild(widget.get(), true);
|
||||
else
|
||||
result->addChild(widget.get(), false);
|
||||
}
|
||||
subwidgetSizes.pop_back();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapGameArea(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
mapView = std::make_shared<MapView>(area.topLeft(), area.dimensions());
|
||||
return mapView;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapHeroList(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
subwidgetSizes.push_back(area);
|
||||
|
||||
Rect item = readTargetArea(input["item"]);
|
||||
|
||||
Point itemOffset(input["itemsOffset"]["x"].Integer(), input["itemsOffset"]["y"].Integer());
|
||||
int itemsCount = input["itemsCount"].Integer();
|
||||
|
||||
auto result = std::make_shared<CHeroList>(itemsCount, area, item.topLeft() - area.topLeft(), itemOffset, LOCPLINT->localState->getWanderingHeroes().size());
|
||||
|
||||
|
||||
if(!input["scrollUp"].isNull())
|
||||
result->setScrollUpButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollUp"])));
|
||||
|
||||
if(!input["scrollDown"].isNull())
|
||||
result->setScrollDownButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollDown"])));
|
||||
|
||||
subwidgetSizes.pop_back();
|
||||
|
||||
heroList = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapIcon(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
size_t index = input["index"].Integer();
|
||||
size_t perPlayer = input["perPlayer"].Integer();
|
||||
std::string image = input["image"].String();
|
||||
|
||||
return std::make_shared<CAdventureMapIcon>(area.topLeft(), loadAnimation(image), index, perPlayer);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMapTownList(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
subwidgetSizes.push_back(area);
|
||||
|
||||
Rect item = readTargetArea(input["item"]);
|
||||
Point itemOffset(input["itemsOffset"]["x"].Integer(), input["itemsOffset"]["y"].Integer());
|
||||
int itemsCount = input["itemsCount"].Integer();
|
||||
|
||||
auto result = std::make_shared<CTownList>(itemsCount, area, item.topLeft() - area.topLeft(), itemOffset, LOCPLINT->localState->getOwnedTowns().size());
|
||||
|
||||
|
||||
if(!input["scrollUp"].isNull())
|
||||
result->setScrollUpButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollUp"])));
|
||||
|
||||
if(!input["scrollDown"].isNull())
|
||||
result->setScrollDownButton(std::dynamic_pointer_cast<CButton>(buildMapButton(input["scrollDown"])));
|
||||
|
||||
subwidgetSizes.pop_back();
|
||||
|
||||
townList = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildMinimap(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
minimap = std::make_shared<CMinimap>(area);
|
||||
return minimap;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildResourceDateBar(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
std::string image = input["image"].String();
|
||||
|
||||
auto result = std::make_shared<CResDataBar>(image, area.topLeft());
|
||||
|
||||
for(auto i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
|
||||
{
|
||||
const auto & node = input[GameConstants::RESOURCE_NAMES[i]];
|
||||
|
||||
if(node.isNull())
|
||||
continue;
|
||||
|
||||
result->setResourcePosition(GameResID(i), Point(node["x"].Integer(), node["y"].Integer()));
|
||||
}
|
||||
|
||||
result->setDatePosition(Point(input["date"]["x"].Integer(), input["date"]["y"].Integer()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildStatusBar(const JsonNode & input)
|
||||
{
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
std::string image = input["image"].String();
|
||||
|
||||
auto background = std::make_shared<CFilledTexture>(image, area);
|
||||
|
||||
return CGStatusBar::create(background);
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> AdventureMapWidget::buildTexturePlayerColored(const JsonNode & input)
|
||||
{
|
||||
logGlobal->debug("Building widget CFilledTexture");
|
||||
auto image = input["image"].String();
|
||||
Rect area = readTargetArea(input["area"]);
|
||||
return std::make_shared<FilledTexturePlayerColored>(image, area);
|
||||
}
|
||||
|
||||
std::shared_ptr<CHeroList> AdventureMapWidget::getHeroList()
|
||||
{
|
||||
return heroList;
|
||||
}
|
||||
|
||||
std::shared_ptr<CTownList> AdventureMapWidget::getTownList()
|
||||
{
|
||||
return townList;
|
||||
}
|
||||
|
||||
std::shared_ptr<CMinimap> AdventureMapWidget::getMinimap()
|
||||
{
|
||||
return minimap;
|
||||
}
|
||||
|
||||
std::shared_ptr<MapView> AdventureMapWidget::getMapView()
|
||||
{
|
||||
return mapView;
|
||||
}
|
||||
|
||||
std::shared_ptr<CInfoBar> AdventureMapWidget::getInfoBar()
|
||||
{
|
||||
return infoBar;
|
||||
}
|
||||
|
||||
void AdventureMapWidget::setPlayer(const PlayerColor & player)
|
||||
{
|
||||
setPlayerChildren(this, player);
|
||||
}
|
||||
|
||||
void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColor & player)
|
||||
{
|
||||
for(auto & entry : widget->children)
|
||||
{
|
||||
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
|
||||
auto icon = dynamic_cast<CAdventureMapIcon *>(entry);
|
||||
auto button = dynamic_cast<CButton *>(entry);
|
||||
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
|
||||
|
||||
if(button)
|
||||
button->setPlayerColor(player);
|
||||
|
||||
if(icon)
|
||||
icon->setPlayer(player);
|
||||
|
||||
if(container)
|
||||
setPlayerChildren(container, player);
|
||||
|
||||
if (texture)
|
||||
texture->playerColored(player);
|
||||
}
|
||||
|
||||
for(const auto & entry : playerColorerImages)
|
||||
{
|
||||
if(images.count(entry))
|
||||
images[entry]->playerColored(player);
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
CAdventureMapIcon::CAdventureMapIcon(const Point & position, std::shared_ptr<CAnimation> animation, size_t index, size_t iconsPerPlayer)
|
||||
: index(index)
|
||||
, iconsPerPlayer(iconsPerPlayer)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos += position;
|
||||
image = std::make_shared<CAnimImage>(animation, index);
|
||||
}
|
||||
|
||||
void CAdventureMapIcon::setPlayer(const PlayerColor & player)
|
||||
{
|
||||
image->setFrame(index + player.getNum() * iconsPerPlayer);
|
||||
}
|
||||
|
||||
void CAdventureMapOverlayWidget::show(SDL_Surface * to)
|
||||
{
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
void AdventureMapWidget::updateActiveStateChildden(CIntObject * widget)
|
||||
{
|
||||
for(auto & entry : widget->children)
|
||||
{
|
||||
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
|
||||
|
||||
if (container)
|
||||
{
|
||||
if (container->disableCondition == "heroAwake")
|
||||
container->setEnabled(!shortcuts->optionHeroSleeping());
|
||||
|
||||
if (container->disableCondition == "heroSleeping")
|
||||
container->setEnabled(shortcuts->optionHeroSleeping());
|
||||
|
||||
if (container->disableCondition == "mapLayerSurface")
|
||||
container->setEnabled(shortcuts->optionMapLevelSurface());
|
||||
|
||||
if (container->disableCondition == "mapLayerUnderground")
|
||||
container->setEnabled(!shortcuts->optionMapLevelSurface());
|
||||
|
||||
if (container->disableCondition == "mapViewMode")
|
||||
container->setEnabled(shortcuts->optionInWorldView());
|
||||
|
||||
if (container->disableCondition == "worldViewMode")
|
||||
container->setEnabled(!shortcuts->optionInWorldView());
|
||||
|
||||
updateActiveStateChildden(container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapWidget::updateActiveState()
|
||||
{
|
||||
updateActiveStateChildden(this);
|
||||
|
||||
for (auto entry: shortcuts->getShortcuts())
|
||||
setShortcutBlocked(entry.shortcut, !entry.isEnabled);
|
||||
}
|
109
client/adventureMap/AdventureMapWidget.h
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* CAdventureMapWidget.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
|
||||
|
||||
#include "../gui/InterfaceObjectConfigurable.h"
|
||||
|
||||
class CHeroList;
|
||||
class CTownList;
|
||||
class CMinimap;
|
||||
class MapView;
|
||||
class CInfoBar;
|
||||
class IImage;
|
||||
class AdventureMapShortcuts;
|
||||
enum class EAdventureState;
|
||||
|
||||
/// Internal class of AdventureMapInterface that contains actual UI elements
|
||||
class AdventureMapWidget : public InterfaceObjectConfigurable
|
||||
{
|
||||
int mapLevel;
|
||||
/// temporary stack of sizes of currently building widgets
|
||||
std::vector<Rect> subwidgetSizes;
|
||||
|
||||
/// list of images on which player-colored palette will be applied
|
||||
std::vector<std::string> playerColorerImages;
|
||||
|
||||
/// list of named images shared between widgets
|
||||
std::map<std::string, std::shared_ptr<IImage>> images;
|
||||
std::map<std::string, std::shared_ptr<CAnimation>> animations;
|
||||
|
||||
/// Widgets that require access from adventure map
|
||||
std::shared_ptr<CHeroList> heroList;
|
||||
std::shared_ptr<CTownList> townList;
|
||||
std::shared_ptr<CMinimap> minimap;
|
||||
std::shared_ptr<MapView> mapView;
|
||||
std::shared_ptr<CInfoBar> infoBar;
|
||||
|
||||
std::shared_ptr<AdventureMapShortcuts> shortcuts;
|
||||
|
||||
Rect readTargetArea(const JsonNode & source);
|
||||
Rect readSourceArea(const JsonNode & source, const JsonNode & sourceCommon);
|
||||
Rect readArea(const JsonNode & source, const Rect & boundingBox);
|
||||
|
||||
std::shared_ptr<IImage> loadImage(const std::string & name);
|
||||
std::shared_ptr<CAnimation> loadAnimation(const std::string & name);
|
||||
|
||||
std::shared_ptr<CIntObject> buildInfobox(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapImage(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapButton(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapContainer(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapGameArea(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapHeroList(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapIcon(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMapTownList(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildMinimap(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildResourceDateBar(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildStatusBar(const JsonNode & input);
|
||||
std::shared_ptr<CIntObject> buildTexturePlayerColored(const JsonNode &);
|
||||
|
||||
|
||||
void setPlayerChildren(CIntObject * widget, const PlayerColor & player);
|
||||
void updateActiveStateChildden(CIntObject * widget);
|
||||
public:
|
||||
explicit AdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts );
|
||||
|
||||
std::shared_ptr<CHeroList> getHeroList();
|
||||
std::shared_ptr<CTownList> getTownList();
|
||||
std::shared_ptr<CMinimap> getMinimap();
|
||||
std::shared_ptr<MapView> getMapView();
|
||||
std::shared_ptr<CInfoBar> getInfoBar();
|
||||
|
||||
void setPlayer(const PlayerColor & player);
|
||||
|
||||
void onMapViewMoved(const Rect & visibleArea, int mapLevel);
|
||||
void updateActiveState();
|
||||
};
|
||||
|
||||
/// Small helper class that provides ownership for shared_ptr's of child elements
|
||||
class CAdventureMapContainerWidget : public CIntObject
|
||||
{
|
||||
friend class AdventureMapWidget;
|
||||
std::vector<std::shared_ptr<CIntObject>> ownedChildren;
|
||||
std::string disableCondition;
|
||||
};
|
||||
|
||||
class CAdventureMapOverlayWidget : public CAdventureMapContainerWidget
|
||||
{
|
||||
public:
|
||||
void show(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// Small helper class that provides player-colorable icon using animation file
|
||||
class CAdventureMapIcon : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
|
||||
size_t index;
|
||||
size_t iconsPerPlayer;
|
||||
public:
|
||||
CAdventureMapIcon(const Point & position, std::shared_ptr<CAnimation> image, size_t index, size_t iconsPerPlayer);
|
||||
|
||||
void setPlayer(const PlayerColor & player);
|
||||
};
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "CAdventureOptions.h"
|
||||
#include "AdventureOptions.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
@ -23,7 +23,7 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
|
||||
CAdventureOptions::CAdventureOptions()
|
||||
AdventureOptions::AdventureOptions()
|
||||
: CWindowObject(PLAYER_COLORED, "ADVOPTS")
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
@ -31,10 +31,10 @@ CAdventureOptions::CAdventureOptions()
|
||||
viewWorld = std::make_shared<CButton>(Point(24, 23), "ADVVIEW.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_WORLD);
|
||||
viewWorld->addCallback( [] { LOCPLINT->viewWorldMap(); });
|
||||
|
||||
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&CAdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
|
||||
exit = std::make_shared<CButton>(Point(204, 313), "IOK6432.DEF", CButton::tooltip(), std::bind(&AdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
|
||||
|
||||
scenInfo = std::make_shared<CButton>(Point(24, 198), "ADVINFO.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO);
|
||||
scenInfo->addCallback(CAdventureOptions::showScenarioInfo);
|
||||
scenInfo->addCallback(AdventureOptions::showScenarioInfo);
|
||||
|
||||
puzzle = std::make_shared<CButton>(Point(24, 81), "ADVPUZ.DEF", CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_PUZZLE);
|
||||
puzzle->addCallback(std::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT));
|
||||
@ -46,7 +46,7 @@ CAdventureOptions::CAdventureOptions()
|
||||
dig->block(true);
|
||||
}
|
||||
|
||||
void CAdventureOptions::showScenarioInfo()
|
||||
void AdventureOptions::showScenarioInfo()
|
||||
{
|
||||
if(LOCPLINT->cb->getStartInfo()->campState)
|
||||
{
|
@ -14,7 +14,7 @@
|
||||
class CButton;
|
||||
|
||||
/// Adventure options dialog where you can view the world, dig, play the replay of the last turn,...
|
||||
class CAdventureOptions : public CWindowObject
|
||||
class AdventureOptions : public CWindowObject
|
||||
{
|
||||
std::shared_ptr<CButton> exit;
|
||||
std::shared_ptr<CButton> viewWorld;
|
||||
@ -24,7 +24,7 @@ class CAdventureOptions : public CWindowObject
|
||||
/*std::shared_ptr<CButton> replay*/
|
||||
|
||||
public:
|
||||
CAdventureOptions();
|
||||
AdventureOptions();
|
||||
|
||||
static void showScenarioInfo();
|
||||
};
|
20
client/adventureMap/AdventureState.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* AdventureState.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
|
||||
|
||||
enum class EAdventureState
|
||||
{
|
||||
NOT_INITIALIZED,
|
||||
HOTSEAT_WAIT,
|
||||
MAKING_TURN,
|
||||
ENEMY_TURN,
|
||||
CASTING_SPELL,
|
||||
WORLD_VIEW
|
||||
};
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* CAdvMapPanel.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 "CAdvMapPanel.h"
|
||||
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
CAdvMapPanel::CAdvMapPanel(std::shared_ptr<IImage> bg, Point position)
|
||||
: CIntObject()
|
||||
, background(bg)
|
||||
{
|
||||
defActions = 255;
|
||||
recActions = 255;
|
||||
pos.x += position.x;
|
||||
pos.y += position.y;
|
||||
if (bg)
|
||||
{
|
||||
pos.w = bg->width();
|
||||
pos.h = bg->height();
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapPanel::addChildColorableButton(std::shared_ptr<CButton> button)
|
||||
{
|
||||
colorableButtons.push_back(button);
|
||||
addChildToPanel(button, ACTIVATE | DEACTIVATE);
|
||||
}
|
||||
|
||||
void CAdvMapPanel::setPlayerColor(const PlayerColor & clr)
|
||||
{
|
||||
for(auto & button : colorableButtons)
|
||||
{
|
||||
button->setPlayerColor(clr);
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapPanel::showAll(SDL_Surface * to)
|
||||
{
|
||||
if(background)
|
||||
background->draw(to, pos.x, pos.y);
|
||||
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
void CAdvMapPanel::addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions)
|
||||
{
|
||||
otherObjects.push_back(obj);
|
||||
obj->recActions |= actions | SHOWALL;
|
||||
obj->recActions &= ~DISPOSE;
|
||||
addChild(obj.get(), false);
|
||||
}
|
||||
|
||||
CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color)
|
||||
: CAdvMapPanel(bg, position), icons(_icons)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
int fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0;
|
||||
|
||||
if(fillerHeight > 0)
|
||||
{
|
||||
backgroundFiller = std::make_shared<CFilledTexture>("DIBOXBCK", Rect(0, pos.h, pos.w, fillerHeight));
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOffset)
|
||||
{
|
||||
assert(iconsData.size() == currentIcons.size());
|
||||
|
||||
for(size_t idx = 0; idx < iconsData.size(); idx++)
|
||||
{
|
||||
const auto & data = iconsData.at(idx);
|
||||
currentIcons[idx]->setFrame(data.first + indexOffset);
|
||||
}
|
||||
}
|
||||
|
||||
void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, int indexOffset)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
iconsData.push_back(data);
|
||||
currentIcons.push_back(std::make_shared<CAnimImage>(icons, data.first + indexOffset, 0, data.second.x, data.second.y));
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* CAdvMapPanel.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
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class PlayerColor;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CAnimation;
|
||||
class CAnimImage;
|
||||
class CFilledTexture;
|
||||
class CButton;
|
||||
class IImage;
|
||||
|
||||
/// simple panel that contains other displayable elements; used to separate groups of controls
|
||||
class CAdvMapPanel : public CIntObject
|
||||
{
|
||||
std::vector<std::shared_ptr<CButton>> colorableButtons;
|
||||
std::vector<std::shared_ptr<CIntObject>> otherObjects;
|
||||
/// the surface passed to this obj will be freed in dtor
|
||||
std::shared_ptr<IImage> background;
|
||||
public:
|
||||
CAdvMapPanel(std::shared_ptr<IImage> bg, Point position);
|
||||
|
||||
void addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions = 0);
|
||||
void addChildColorableButton(std::shared_ptr<CButton> button);
|
||||
/// recolors all buttons to given player color
|
||||
void setPlayerColor(const PlayerColor & clr);
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// specialized version of CAdvMapPanel that handles recolorable def-based pictures for world view info panel
|
||||
class CAdvMapWorldViewPanel : public CAdvMapPanel
|
||||
{
|
||||
/// data that allows reconstruction of panel info icons
|
||||
std::vector<std::pair<int, Point>> iconsData;
|
||||
/// ptrs to child-pictures constructed from iconsData
|
||||
std::vector<std::shared_ptr<CAnimImage>> currentIcons;
|
||||
/// surface drawn below world view panel on higher resolutions (won't be needed when world view panel is configured for extraResolutions mod)
|
||||
std::shared_ptr<CFilledTexture> backgroundFiller;
|
||||
std::shared_ptr<CAnimation> icons;
|
||||
public:
|
||||
CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, std::shared_ptr<IImage> bg, Point position, int spaceBottom, const PlayerColor &color);
|
||||
|
||||
void addChildIcon(std::pair<int, Point> data, int indexOffset);
|
||||
|
||||
/// recreates all pictures from given def to recolor them according to current player color
|
||||
void recolorIcons(const PlayerColor & color, int indexOffset);
|
||||
};
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../windows/CMessage.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
@ -83,24 +85,17 @@ void CInGameConsole::print(const std::string & txt)
|
||||
// boost::unique_lock scope
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(texts_mx);
|
||||
int lineLen = conf.go()->ac.outputLineLength;
|
||||
|
||||
if(txt.size() < lineLen)
|
||||
{
|
||||
texts.push_back({txt, 0});
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(lineLen);
|
||||
for(int g = 0; g < txt.size() / lineLen + 1; ++g)
|
||||
{
|
||||
std::string part = txt.substr(g * lineLen, lineLen);
|
||||
if(part.empty())
|
||||
break;
|
||||
// Maximum width for a text line is limited by:
|
||||
// 1) width of adventure map terrain area, for when in-game console is on top of advmap
|
||||
// 2) width of castle/battle window (fixed to 800) when this window is open
|
||||
// 3) arbitrary selected left and right margins
|
||||
int maxWidth = std::min( 800, adventureInt->terrainAreaPixels().w) - 100;
|
||||
|
||||
texts.push_back({part, 0});
|
||||
}
|
||||
}
|
||||
auto splitText = CMessage::breakText(txt, maxWidth, FONT_MEDIUM);
|
||||
|
||||
for (auto const & entry : splitText)
|
||||
texts.push_back({entry, 0});
|
||||
|
||||
while(texts.size() > maxDisplayedTexts)
|
||||
texts.erase(texts.begin());
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CInfoBar.h"
|
||||
|
||||
#include "CAdventureMapInterface.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/Images.h"
|
||||
|
@ -11,15 +11,17 @@
|
||||
#include "StdInc.h"
|
||||
#include "CList.h"
|
||||
|
||||
#include "CAdventureMapInterface.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
@ -81,24 +83,44 @@ void CList::CListItem::onSelect(bool on)
|
||||
redraw();
|
||||
}
|
||||
|
||||
CList::CList(int Size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create)
|
||||
: CIntObject(0, position),
|
||||
CList::CList(int Size, Rect widgetDimensions)
|
||||
: CIntObject(0, widgetDimensions.topLeft()),
|
||||
size(Size),
|
||||
selected(nullptr)
|
||||
{
|
||||
pos.w = widgetDimensions.w;
|
||||
pos.h = widgetDimensions.h;
|
||||
}
|
||||
|
||||
void CList::showAll(SDL_Surface * to)
|
||||
{
|
||||
CSDL_Ext::fillRect(to, pos, Colors::BLACK);
|
||||
CIntObject::showAll(to);
|
||||
}
|
||||
|
||||
void CList::createList(Point firstItemPosition, Point itemPositionDelta, size_t listAmount)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
scrollUp = std::make_shared<CButton>(Point(0, 0), btnUp, CGI->generaltexth->zelp[helpUp]);
|
||||
scrollDown = std::make_shared<CButton>(Point(0, scrollUp->pos.h + 32*(int)size), btnDown, CGI->generaltexth->zelp[helpDown]);
|
||||
listBox = std::make_shared<CListBox>(std::bind(&CList::createItem, this, _1), firstItemPosition, itemPositionDelta, size, listAmount);
|
||||
}
|
||||
|
||||
listBox = std::make_shared<CListBox>(create, Point(1,scrollUp->pos.h), Point(0, 32), size, listAmount);
|
||||
void CList::setScrollUpButton(std::shared_ptr<CButton> button)
|
||||
{
|
||||
addChild(button.get());
|
||||
|
||||
//assign callback only after list was created
|
||||
scrollUp = button;
|
||||
scrollUp->addCallback(std::bind(&CListBox::moveToPrev, listBox));
|
||||
scrollDown->addCallback(std::bind(&CListBox::moveToNext, listBox));
|
||||
|
||||
scrollUp->addCallback(std::bind(&CList::update, this));
|
||||
scrollDown->addCallback(std::bind(&CList::update, this));
|
||||
update();
|
||||
}
|
||||
|
||||
void CList::setScrollDownButton(std::shared_ptr<CButton> button)
|
||||
{
|
||||
addChild(button.get());
|
||||
|
||||
scrollDown = button;
|
||||
scrollDown->addCallback(std::bind(&CList::update, this));
|
||||
scrollDown->addCallback(std::bind(&CListBox::moveToNext, listBox));
|
||||
update();
|
||||
}
|
||||
|
||||
@ -107,8 +129,11 @@ void CList::update()
|
||||
bool onTop = listBox->getPos() == 0;
|
||||
bool onBottom = listBox->getPos() + size >= listBox->size();
|
||||
|
||||
scrollUp->block(onTop);
|
||||
scrollDown->block(onBottom);
|
||||
if (scrollUp)
|
||||
scrollUp->block(onTop);
|
||||
|
||||
if (scrollDown)
|
||||
scrollDown->block(onBottom);
|
||||
}
|
||||
|
||||
void CList::select(std::shared_ptr<CListItem> which)
|
||||
@ -223,16 +248,17 @@ std::string CHeroList::CHeroItem::getHoverText()
|
||||
return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->type->heroClass->getNameTranslated());
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
|
||||
std::shared_ptr<CIntObject> CHeroList::createItem(size_t index)
|
||||
{
|
||||
if (LOCPLINT->localState->getWanderingHeroes().size() > index)
|
||||
return std::make_shared<CHeroItem>(this, LOCPLINT->localState->getWanderingHero(index));
|
||||
return std::make_shared<CEmptyHeroItem>();
|
||||
}
|
||||
|
||||
CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->localState->getWanderingHeroes().size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
|
||||
CHeroList::CHeroList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount)
|
||||
: CList(visibleItemsCount, widgetPosition)
|
||||
{
|
||||
createList(firstItemOffset, itemOffsetDelta, initialItemsCount);
|
||||
}
|
||||
|
||||
void CHeroList::select(const CGHeroInstance * hero)
|
||||
@ -261,7 +287,7 @@ void CHeroList::update(const CGHeroInstance * hero)
|
||||
CList::update();
|
||||
}
|
||||
|
||||
std::shared_ptr<CIntObject> CTownList::createTownItem(size_t index)
|
||||
std::shared_ptr<CIntObject> CTownList::createItem(size_t index)
|
||||
{
|
||||
if (LOCPLINT->localState->getOwnedTowns().size() > index)
|
||||
return std::make_shared<CTownItem>(this, LOCPLINT->localState->getOwnedTown(index));
|
||||
@ -312,9 +338,10 @@ std::string CTownList::CTownItem::getHoverText()
|
||||
return town->getObjectName();
|
||||
}
|
||||
|
||||
CTownList::CTownList(int size, Point position, std::string btnUp, std::string btnDown):
|
||||
CList(size, position, btnUp, btnDown, LOCPLINT->localState->getOwnedTowns().size(), 306, 307, std::bind(&CTownList::createTownItem, this, _1))
|
||||
CTownList::CTownList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount)
|
||||
: CList(visibleItemsCount, widgetPosition)
|
||||
{
|
||||
createList(firstItemOffset, itemOffsetDelta, initialItemsCount);
|
||||
}
|
||||
|
||||
void CTownList::select(const CGTownInstance * town)
|
||||
|
@ -10,8 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../../lib/FunctionList.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -21,7 +19,9 @@ class CGTownInstance;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CListBox;
|
||||
class CButton;
|
||||
class CAnimImage;
|
||||
|
||||
/// Base UI Element for hero\town lists
|
||||
class CList : public CIntObject
|
||||
@ -53,23 +53,9 @@ protected:
|
||||
virtual std::string getHoverText()=0;
|
||||
};
|
||||
|
||||
std::shared_ptr<CListBox> listBox;
|
||||
private:
|
||||
const size_t size;
|
||||
|
||||
/**
|
||||
* @brief CList - protected constructor
|
||||
* @param size - maximal amount of visible at once items
|
||||
* @param position - cordinates
|
||||
* @param btnUp - path to image to use as top button
|
||||
* @param btnDown - path to image to use as bottom button
|
||||
* @param listAmount - amount of items in the list
|
||||
* @param helpUp - index in zelp.txt for button help tooltip
|
||||
* @param helpDown - index in zelp.txt for button help tooltip
|
||||
* @param create - function for creating items in listbox
|
||||
* @param destroy - function for deleting items in listbox
|
||||
*/
|
||||
CList(int size, Point position, std::string btnUp, std::string btnDown, size_t listAmount, int helpUp, int helpDown, CListBox::CreateFunc create);
|
||||
|
||||
//for selection\deselection
|
||||
std::shared_ptr<CListItem> selected;
|
||||
void select(std::shared_ptr<CListItem> which);
|
||||
@ -78,8 +64,14 @@ protected:
|
||||
std::shared_ptr<CButton> scrollUp;
|
||||
std::shared_ptr<CButton> scrollDown;
|
||||
|
||||
/// should be called when list is invalidated
|
||||
void update();
|
||||
protected:
|
||||
std::shared_ptr<CListBox> listBox;
|
||||
|
||||
CList(int size, Rect widgetDimensions);
|
||||
|
||||
void createList(Point firstItemPosition, Point itemPositionDelta, size_t listAmount);
|
||||
|
||||
virtual std::shared_ptr<CIntObject> createItem(size_t index) = 0;
|
||||
|
||||
public:
|
||||
/// functions that will be called when selection changes
|
||||
@ -88,10 +80,18 @@ public:
|
||||
/// return index of currently selected element
|
||||
int getSelectedIndex();
|
||||
|
||||
void setScrollUpButton(std::shared_ptr<CButton> button);
|
||||
void setScrollDownButton(std::shared_ptr<CButton> button);
|
||||
|
||||
/// should be called when list is invalidated
|
||||
void update();
|
||||
|
||||
/// set of methods to switch selection
|
||||
void selectIndex(int which);
|
||||
void selectNext();
|
||||
void selectPrev();
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
/// List of heroes which is shown at the right of the adventure map screen
|
||||
@ -125,13 +125,9 @@ class CHeroList : public CList
|
||||
std::string getHoverText() override;
|
||||
};
|
||||
|
||||
std::shared_ptr<CIntObject> createHeroItem(size_t index);
|
||||
std::shared_ptr<CIntObject> createItem(size_t index);
|
||||
public:
|
||||
/**
|
||||
* @brief CHeroList
|
||||
* @param size, position, btnUp, btnDown @see CList::CList
|
||||
*/
|
||||
CHeroList(int size, Point position, std::string btnUp, std::string btnDown);
|
||||
CHeroList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount);
|
||||
|
||||
/// Select specific hero and scroll if needed
|
||||
void select(const CGHeroInstance * hero = nullptr);
|
||||
@ -159,13 +155,9 @@ class CTownList : public CList
|
||||
std::string getHoverText() override;
|
||||
};
|
||||
|
||||
std::shared_ptr<CIntObject> createTownItem(size_t index);
|
||||
std::shared_ptr<CIntObject> createItem(size_t index) override;
|
||||
public:
|
||||
/**
|
||||
* @brief CTownList
|
||||
* @param size, position, btnUp, btnDown @see CList::CList
|
||||
*/
|
||||
CTownList(int size, Point position, std::string btnUp, std::string btnDown);
|
||||
CTownList(int visibleItemsCount, Rect widgetPosition, Point firstItemOffset, Point itemOffsetDelta, size_t initialItemsCount);
|
||||
|
||||
/// Select specific town and scroll if needed
|
||||
void select(const CGTownInstance * town = nullptr);
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "CMinimap.h"
|
||||
|
||||
#include "CAdventureMapInterface.h"
|
||||
#include "AdventureMapInterface.h"
|
||||
|
||||
#include "../widgets/Images.h"
|
||||
#include "../CGameInfo.h"
|
||||
|
@ -19,50 +19,40 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/ResourceSet.h"
|
||||
|
||||
#define ADVOPT (conf.go()->ac)
|
||||
|
||||
CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist)
|
||||
CResDataBar::CResDataBar(const std::string & imageName, const Point & position)
|
||||
{
|
||||
pos.x += x;
|
||||
pos.y += y;
|
||||
pos.x += position.x;
|
||||
pos.y += position.y;
|
||||
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
background = std::make_shared<CPicture>(defname, 0, 0);
|
||||
background = std::make_shared<CPicture>(imageName, 0, 0);
|
||||
background->colorize(LOCPLINT->playerID);
|
||||
|
||||
pos.w = background->pos.w;
|
||||
pos.h = background->pos.h;
|
||||
|
||||
txtpos.resize(8);
|
||||
for (int i = 0; i < 8 ; i++)
|
||||
{
|
||||
txtpos[i].first = pos.x + offx + resdist*i;
|
||||
txtpos[i].second = pos.y + offy;
|
||||
}
|
||||
txtpos[7].first = txtpos[6].first + datedist;
|
||||
addUsedEvents(RCLICK);
|
||||
}
|
||||
|
||||
CResDataBar::CResDataBar()
|
||||
CResDataBar::CResDataBar(const std::string & defname, int x, int y, int offx, int offy, int resdist, int datedist):
|
||||
CResDataBar(defname, Point(x,y))
|
||||
{
|
||||
pos.x += ADVOPT.resdatabarX;
|
||||
pos.y += ADVOPT.resdatabarY;
|
||||
for (int i = 0; i < 7 ; i++)
|
||||
resourcePositions[GameResID(i)] = Point( offx + resdist*i, offy );
|
||||
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
background = std::make_shared<CPicture>(ADVOPT.resdatabarG, 0, 0);
|
||||
background->colorize(LOCPLINT->playerID);
|
||||
datePosition = resourcePositions[EGameResID::GOLD] + Point(datedist, 0);
|
||||
}
|
||||
|
||||
pos.w = background->pos.w;
|
||||
pos.h = background->pos.h;
|
||||
|
||||
txtpos.resize(8);
|
||||
for (int i = 0; i < 8 ; i++)
|
||||
{
|
||||
txtpos[i].first = pos.x + ADVOPT.resOffsetX + ADVOPT.resDist*i;
|
||||
txtpos[i].second = pos.y + ADVOPT.resOffsetY;
|
||||
}
|
||||
txtpos[7].first = txtpos[6].first + ADVOPT.resDateDist;
|
||||
void CResDataBar::setDatePosition(const Point & position)
|
||||
{
|
||||
datePosition = position;
|
||||
}
|
||||
|
||||
void CResDataBar::setResourcePosition(const GameResID & resource, const Point & position)
|
||||
{
|
||||
resourcePositions[resource] = position;
|
||||
}
|
||||
|
||||
std::string CResDataBar::buildDateString()
|
||||
@ -80,13 +70,15 @@ std::string CResDataBar::buildDateString()
|
||||
void CResDataBar::draw(SDL_Surface * to)
|
||||
{
|
||||
//TODO: all this should be labels, but they require proper text update on change
|
||||
for (GameResID i=EGameResID::WOOD; i <= GameResID(EGameResID::GOLD); ++i)
|
||||
for (auto & entry : resourcePositions)
|
||||
{
|
||||
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(i));
|
||||
std::string text = std::to_string(LOCPLINT->cb->getResourceAmount(entry.first));
|
||||
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, Point(txtpos[i].first, txtpos[i].second));
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, text, Colors::WHITE, pos.topLeft() + entry.second);
|
||||
}
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, buildDateString(), Colors::WHITE, Point(txtpos[7].first, txtpos[7].second));
|
||||
|
||||
if (datePosition)
|
||||
graphics->fonts[FONT_SMALL]->renderTextLeft(to, buildDateString(), Colors::WHITE, pos.topLeft() + *datePosition);
|
||||
}
|
||||
|
||||
void CResDataBar::showAll(SDL_Surface * to)
|
||||
|
@ -11,6 +11,11 @@
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
enum class EGameResID : int8_t;
|
||||
using GameResID = Identifier<EGameResID>;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
/// Resources bar which shows information about how many gold, crystals,... you have
|
||||
/// Current date is displayed too
|
||||
class CResDataBar : public CIntObject
|
||||
@ -19,14 +24,21 @@ class CResDataBar : public CIntObject
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
|
||||
std::vector<std::pair<int,int> > txtpos;
|
||||
|
||||
std::map<GameResID, Point> resourcePositions;
|
||||
std::optional<Point> datePosition;
|
||||
|
||||
void draw(SDL_Surface * to);
|
||||
public:
|
||||
CResDataBar();
|
||||
|
||||
/// For dynamically-sized UI windows, e.g. adventure map interface
|
||||
CResDataBar(const std::string & imageName, const Point & position);
|
||||
|
||||
/// For fixed-size UI windows, e.g. CastleInterface
|
||||
CResDataBar(const std::string &defname, int x, int y, int offx, int offy, int resdist, int datedist);
|
||||
|
||||
void setDatePosition(const Point & position);
|
||||
void setResourcePosition(const GameResID & resource, const Point & position);
|
||||
|
||||
void colorize(PlayerColor player);
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CStack.h"
|
||||
|
@ -72,7 +72,6 @@ BattleWindow::BattleWindow(BattleInterface & owner):
|
||||
|
||||
console = widget<BattleConsole>("console");
|
||||
|
||||
GH.statusbar = console;
|
||||
owner.console = console;
|
||||
|
||||
owner.fieldController.reset( new BattleFieldController(owner));
|
||||
@ -187,6 +186,11 @@ void BattleWindow::deactivate()
|
||||
LOCPLINT->cingconsole->deactivate();
|
||||
}
|
||||
|
||||
bool BattleWindow::captureThisKey(EShortcut key)
|
||||
{
|
||||
return owner.openingPlaying();
|
||||
}
|
||||
|
||||
void BattleWindow::keyPressed(EShortcut key)
|
||||
{
|
||||
if (owner.openingPlaying())
|
||||
@ -359,7 +363,7 @@ void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action)
|
||||
}
|
||||
|
||||
auto anim = std::make_shared<CAnimation>(iconName);
|
||||
w->setImage(anim, false);
|
||||
w->setImage(anim);
|
||||
w->redraw();
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ public:
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void keyPressed(EShortcut key) override;
|
||||
bool captureThisKey(EShortcut key) override;
|
||||
void clickRight(tribool down, bool previousState) override;
|
||||
void show(SDL_Surface *to) override;
|
||||
void showAll(SDL_Surface *to) override;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../renderSDL/ScreenHandler.h"
|
||||
#include "../CMT.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../battle/BattleInterface.h"
|
||||
@ -38,6 +39,8 @@
|
||||
#include "ios/utils.h"
|
||||
#endif
|
||||
|
||||
CGuiHandler GH;
|
||||
|
||||
extern std::queue<SDL_Event> SDLEventsQueue;
|
||||
extern boost::mutex eventsM;
|
||||
|
||||
@ -95,9 +98,10 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
|
||||
|
||||
void CGuiHandler::init()
|
||||
{
|
||||
screenHandlerInstance = std::make_unique<ScreenHandler>();
|
||||
shortcutsHandlerInstance = std::make_unique<ShortcutHandler>();
|
||||
mainFPSmng = new CFramerateManager();
|
||||
mainFPSmng->init(settings["video"]["targetfps"].Integer());
|
||||
mainFPSmng = new CFramerateManager(settings["video"]["targetfps"].Integer());
|
||||
|
||||
isPointerRelativeMode = settings["general"]["userRelativePointer"].Bool();
|
||||
pointerSpeedMultiplier = settings["general"]["relativePointerSpeedMultiplier"].Float();
|
||||
}
|
||||
@ -180,9 +184,8 @@ std::shared_ptr<IShowActivatable> CGuiHandler::topInt()
|
||||
|
||||
void CGuiHandler::totalRedraw()
|
||||
{
|
||||
#ifdef VCMI_ANDROID
|
||||
SDL_FillRect(screen2, NULL, SDL_MapRGB(screen2->format, 0, 0, 0));
|
||||
#endif
|
||||
CSDL_Ext::fillSurface( screen2, Colors::BLACK);
|
||||
|
||||
for(auto & elem : objsToBlit)
|
||||
elem->showAll(screen2);
|
||||
CSDL_Ext::blitAt(screen2,0,0,screen);
|
||||
@ -803,7 +806,26 @@ void CGuiHandler::pushUserEvent(EUserEvent usercode, void * userdata)
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
CFramerateManager::CFramerateManager()
|
||||
IScreenHandler & CGuiHandler::screenHandler()
|
||||
{
|
||||
return *screenHandlerInstance;
|
||||
}
|
||||
|
||||
void CGuiHandler::onScreenResize()
|
||||
{
|
||||
for (auto const & entry : listInt)
|
||||
{
|
||||
auto intObject = std::dynamic_pointer_cast<CIntObject>(entry);
|
||||
|
||||
if (intObject)
|
||||
intObject->onScreenResize();
|
||||
}
|
||||
|
||||
totalRedraw();
|
||||
}
|
||||
|
||||
|
||||
CFramerateManager::CFramerateManager(int newRate)
|
||||
: rate(0)
|
||||
, rateticks(0)
|
||||
, fps(0)
|
||||
@ -811,7 +833,9 @@ CFramerateManager::CFramerateManager()
|
||||
, accumulatedTime(0)
|
||||
, lastticks(0)
|
||||
, timeElapsed(0)
|
||||
{}
|
||||
{
|
||||
init(newRate);
|
||||
}
|
||||
|
||||
void CFramerateManager::init(int newRate)
|
||||
{
|
||||
|
@ -29,6 +29,7 @@ class CIntObject;
|
||||
class IUpdateable;
|
||||
class IShowActivatable;
|
||||
class IShowable;
|
||||
class IScreenHandler;
|
||||
|
||||
// TODO: event handling need refactoring
|
||||
enum class EUserEvent
|
||||
@ -56,7 +57,7 @@ private:
|
||||
ui32 accumulatedFrames;
|
||||
|
||||
public:
|
||||
CFramerateManager(); // initializes the manager with a given fps rate
|
||||
CFramerateManager(int newRate); // initializes the manager with a given fps rate
|
||||
void init(int newRate); // needs to be called directly before the main game loop to reset the internal timer
|
||||
void framerateDelay(); // needs to be called every game update cycle
|
||||
ui32 getElapsedMilliseconds() const {return this->timeElapsed;}
|
||||
@ -95,6 +96,7 @@ private:
|
||||
CIntObjectList doubleClickInterested;
|
||||
CIntObjectList textInterested;
|
||||
|
||||
std::unique_ptr<IScreenHandler> screenHandlerInstance;
|
||||
|
||||
void handleMouseButtonClick(CIntObjectList & interestedObjs, MouseButton btn, bool isPressed);
|
||||
void processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb);
|
||||
@ -135,6 +137,8 @@ public:
|
||||
/// moves mouse pointer into specified position inside vcmi window
|
||||
void moveCursorToPosition(const Point & position);
|
||||
|
||||
IScreenHandler & screenHandler();
|
||||
|
||||
IUpdateable *curInt;
|
||||
|
||||
Point lastClick;
|
||||
@ -156,6 +160,9 @@ public:
|
||||
void totalRedraw(); //forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering
|
||||
void simpleRedraw(); //update only top interface and draw background from buffer, sets a flag, method gets called at the end of the rendering
|
||||
|
||||
/// called whenever user selects different resolution, requiring to center/resize all windows
|
||||
void onScreenResize();
|
||||
|
||||
void pushInt(std::shared_ptr<IShowActivatable> newInt); //deactivate old top interface, activates this one and pushes to the top
|
||||
template <typename T, typename ... Args>
|
||||
void pushIntT(Args && ... args)
|
||||
|
@ -159,16 +159,6 @@ void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFon
|
||||
graphics->fonts[font]->renderTextCenter(dst, text, kolor, pos.topLeft() + p);
|
||||
}
|
||||
|
||||
void CIntObject::blitAtLoc( SDL_Surface * src, int x, int y, SDL_Surface * dst )
|
||||
{
|
||||
CSDL_Ext::blitAt(src, pos.x + x, pos.y + y, dst);
|
||||
}
|
||||
|
||||
void CIntObject::blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst)
|
||||
{
|
||||
blitAtLoc(src, p.x, p.y, dst);
|
||||
}
|
||||
|
||||
void CIntObject::printAtMiddleWBLoc( const std::string & text, int x, int y, EFonts font, int charpr, SDL_Color kolor, SDL_Surface * dst)
|
||||
{
|
||||
graphics->fonts[font]->renderTextLinesCenter(dst, CMessage::breakText(text, charpr, font), kolor, Point(pos.x + x, pos.y + y));
|
||||
@ -199,11 +189,22 @@ void CIntObject::disable()
|
||||
void CIntObject::enable()
|
||||
{
|
||||
if(!active_m && (!parent_m || parent_m->active))
|
||||
{
|
||||
activate();
|
||||
redraw();
|
||||
}
|
||||
|
||||
recActions = 255;
|
||||
}
|
||||
|
||||
void CIntObject::setEnabled(bool on)
|
||||
{
|
||||
if (on)
|
||||
enable();
|
||||
else
|
||||
disable();
|
||||
}
|
||||
|
||||
void CIntObject::fitToScreen(int borderWidth, bool propagate)
|
||||
{
|
||||
Point newPos = pos.topLeft();
|
||||
@ -242,7 +243,7 @@ void CIntObject::addChild(CIntObject * child, bool adjustPosition)
|
||||
children.push_back(child);
|
||||
child->parent_m = this;
|
||||
if(adjustPosition)
|
||||
child->pos += pos.topLeft();
|
||||
child->moveBy(pos.topLeft(), adjustPosition);
|
||||
|
||||
if (!active && child->active)
|
||||
child->deactivate();
|
||||
@ -286,6 +287,11 @@ void CIntObject::redraw()
|
||||
}
|
||||
}
|
||||
|
||||
void CIntObject::onScreenResize()
|
||||
{
|
||||
center(pos, true);
|
||||
}
|
||||
|
||||
const Rect & CIntObject::center( const Rect &r, bool propagate )
|
||||
{
|
||||
pos.w = r.w;
|
||||
|
@ -53,7 +53,7 @@ class IShowActivatable : public IShowable, public IActivatable
|
||||
{
|
||||
public:
|
||||
//redraw parent flag - this int may be semi-transparent and require redraw of parent window
|
||||
enum {BLOCK_ADV_HOTKEYS = 2, REDRAW_PARENT=8};
|
||||
enum {REDRAW_PARENT=8};
|
||||
int type; //bin flags using etype
|
||||
IShowActivatable();
|
||||
virtual ~IShowActivatable(){};
|
||||
@ -140,8 +140,13 @@ public:
|
||||
ui8 defActions; //which calls will be tried to be redirected to children
|
||||
ui8 recActions; //which calls we allow to receive from parent
|
||||
|
||||
void disable(); //deactivates if needed, blocks all automatic activity, allows only disposal
|
||||
void enable(); //activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
|
||||
/// deactivates if needed, blocks all automatic activity, allows only disposal
|
||||
void disable();
|
||||
/// activates if needed, all activity enabled (Warning: may not be symetric with disable if recActions was limited!)
|
||||
void enable();
|
||||
/// deactivates or activates UI element based on flag
|
||||
void setEnabled(bool on);
|
||||
|
||||
|
||||
// activate or deactivate object. Inactive object won't receive any input events (keyboard\mouse)
|
||||
// usually used automatically by parent
|
||||
@ -155,6 +160,10 @@ public:
|
||||
//request complete redraw of this object
|
||||
void redraw() override;
|
||||
|
||||
/// called only for windows whenever screen size changes
|
||||
/// default behavior is to re-center, can be overriden
|
||||
virtual void onScreenResize();
|
||||
|
||||
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
|
||||
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
|
||||
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position
|
||||
@ -177,10 +186,6 @@ public:
|
||||
void printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color color, SDL_Surface * dst);
|
||||
void printAtMiddleWBLoc(const std::string & text, int x, int y, EFonts font, int charsPerLine, SDL_Color color, SDL_Surface * dst);
|
||||
|
||||
//image blitting. If possible use CPicture or CAnimImage instead
|
||||
void blitAtLoc(SDL_Surface * src, int x, int y, SDL_Surface * dst);
|
||||
void blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst);
|
||||
|
||||
friend class CGuiHandler;
|
||||
};
|
||||
|
||||
|
@ -82,15 +82,26 @@ void InterfaceObjectConfigurable::build(const JsonNode &config)
|
||||
items = &config["items"];
|
||||
}
|
||||
|
||||
const std::string unnamedObjectPrefix = "__widget_";
|
||||
for(const auto & item : items->Vector())
|
||||
{
|
||||
std::string name = item["name"].isNull()
|
||||
? unnamedObjectPrefix + std::to_string(unnamedObjectId++)
|
||||
: item["name"].String();
|
||||
logGlobal->debug("Building widget with name %s", name);
|
||||
widgets[name] = buildWidget(item);
|
||||
}
|
||||
addWidget(item["name"].String(), buildWidget(item));
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::addWidget(const std::string & namePreferred, std::shared_ptr<CIntObject> widget)
|
||||
{
|
||||
static const std::string unnamedObjectPrefix = "__widget_";
|
||||
|
||||
std::string nameActual;
|
||||
|
||||
if (widgets.count(namePreferred) == 0)
|
||||
nameActual = namePreferred;
|
||||
else
|
||||
logGlobal->error("Duplicated widget name: '%s'", namePreferred);
|
||||
|
||||
if (nameActual.empty())
|
||||
nameActual = unnamedObjectPrefix + std::to_string(unnamedObjectId++);
|
||||
|
||||
logGlobal->debug("Building widget with name %s", nameActual);
|
||||
widgets[nameActual] = widget;
|
||||
}
|
||||
|
||||
std::string InterfaceObjectConfigurable::readText(const JsonNode & config) const
|
||||
@ -174,6 +185,8 @@ EFonts InterfaceObjectConfigurable::readFont(const JsonNode & config) const
|
||||
return EFonts::FONT_SMALL;
|
||||
if(config.String() == "tiny")
|
||||
return EFonts::FONT_TINY;
|
||||
if(config.String() == "calisto")
|
||||
return EFonts::FONT_CALLI;
|
||||
}
|
||||
logGlobal->debug("Uknown font attribute");
|
||||
return EFonts::FONT_TIMES;
|
||||
@ -287,15 +300,7 @@ std::shared_ptr<CToggleButton> InterfaceObjectConfigurable::buildToggleButton(co
|
||||
assert(imgOrder.size() >= 4);
|
||||
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
|
||||
}
|
||||
if(!config["callback"].isNull())
|
||||
{
|
||||
std::string callbackName = config["callback"].String();
|
||||
|
||||
if (callbacks.count(callbackName))
|
||||
button->addCallback(callbacks.at(callbackName));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
loadButtonCallback(button, config["callback"]);
|
||||
return button;
|
||||
}
|
||||
|
||||
@ -319,32 +324,56 @@ std::shared_ptr<CButton> InterfaceObjectConfigurable::buildButton(const JsonNode
|
||||
assert(imgOrder.size() >= 4);
|
||||
button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer());
|
||||
}
|
||||
if(!config["callback"].isNull())
|
||||
{
|
||||
std::string callbackName = config["callback"].String();
|
||||
|
||||
if (callbacks.count(callbackName) > 0)
|
||||
button->addCallback(std::bind(callbacks.at(callbackName), 0));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
if(!config["hotkey"].isNull())
|
||||
{
|
||||
if(config["hotkey"].getType() == JsonNode::JsonType::DATA_STRING)
|
||||
{
|
||||
button->assignedKey = readHotkey(config["hotkey"]);
|
||||
|
||||
auto target = shortcuts.find(button->assignedKey);
|
||||
if (target != shortcuts.end())
|
||||
{
|
||||
button->addCallback(target->second.callback);
|
||||
target->second.assignedToButton = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
loadButtonBorderColor(button, config["borderColor"]);
|
||||
loadButtonCallback(button, config["callback"]);
|
||||
loadButtonHotkey(button, config["hotkey"]);
|
||||
return button;
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadButtonBorderColor(std::shared_ptr<CButton> button, const JsonNode & config) const
|
||||
{
|
||||
if (config.isNull())
|
||||
return;
|
||||
|
||||
auto color = readColor(config);
|
||||
button->setBorderColor(color);
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadButtonCallback(std::shared_ptr<CButton> button, const JsonNode & config) const
|
||||
{
|
||||
if(config.isNull())
|
||||
return;
|
||||
|
||||
std::string callbackName = config.String();
|
||||
|
||||
if (callbacks.count(callbackName) > 0)
|
||||
button->addCallback(std::bind(callbacks.at(callbackName), 0));
|
||||
else
|
||||
logGlobal->error("Invalid callback '%s' in widget", callbackName );
|
||||
}
|
||||
|
||||
void InterfaceObjectConfigurable::loadButtonHotkey(std::shared_ptr<CButton> button, const JsonNode & config) const
|
||||
{
|
||||
if(config.isNull())
|
||||
return;
|
||||
|
||||
if(config.getType() != JsonNode::JsonType::DATA_STRING)
|
||||
{
|
||||
logGlobal->error("Invalid shortcut format - string expected!");
|
||||
return;
|
||||
}
|
||||
|
||||
button->assignedKey = readHotkey(config);
|
||||
|
||||
auto target = shortcuts.find(button->assignedKey);
|
||||
if (target == shortcuts.end())
|
||||
return;
|
||||
|
||||
button->addCallback(target->second.callback);
|
||||
target->second.assignedToButton = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const
|
||||
{
|
||||
logGlobal->debug("Building widget CLabelGroup");
|
||||
@ -374,7 +403,8 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode
|
||||
auto itemsTotal = config["itemsTotal"].Integer();
|
||||
auto value = config["selected"].Integer();
|
||||
bool horizontal = config["orientation"].String() == "horizontal";
|
||||
auto const & result = std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
|
||||
const auto & result =
|
||||
std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
|
||||
|
||||
if (!config["scrollBounds"].isNull())
|
||||
{
|
||||
|
@ -48,6 +48,8 @@ protected:
|
||||
|
||||
//must be called after adding callbacks
|
||||
void build(const JsonNode & config);
|
||||
|
||||
void addWidget(const std::string & name, std::shared_ptr<CIntObject> widget);
|
||||
|
||||
void addCallback(const std::string & callbackName, std::function<void(int)> callback);
|
||||
JsonNode variables;
|
||||
@ -73,6 +75,10 @@ protected:
|
||||
std::pair<std::string, std::string> readHintText(const JsonNode &) const;
|
||||
EShortcut readHotkey(const JsonNode &) const;
|
||||
|
||||
void loadButtonCallback(std::shared_ptr<CButton> button, const JsonNode & config) const;
|
||||
void loadButtonHotkey(std::shared_ptr<CButton> button, const JsonNode & config) const;
|
||||
void loadButtonBorderColor(std::shared_ptr<CButton> button, const JsonNode & config) const;
|
||||
|
||||
//basic widgets
|
||||
std::shared_ptr<CPicture> buildPicture(const JsonNode &) const;
|
||||
std::shared_ptr<CLabel> buildLabel(const JsonNode &) const;
|
||||
|
@ -81,7 +81,9 @@ enum class EShortcut
|
||||
// Adventure map screen
|
||||
ADVENTURE_GAME_OPTIONS, // 'o', Open CAdventureOptions window
|
||||
ADVENTURE_TOGGLE_GRID, // F6, Toggles map grid
|
||||
ADVENTURE_TOGGLE_SLEEP, // z,w, Toggles hero sleep status
|
||||
ADVENTURE_TOGGLE_SLEEP, // Toggles hero sleep status
|
||||
ADVENTURE_SET_HERO_ASLEEP, // Moves hero to sleep state
|
||||
ADVENTURE_SET_HERO_AWAKE, // Move hero to awake state
|
||||
ADVENTURE_MOVE_HERO, // Moves hero alongside set path
|
||||
ADVENTURE_VISIT_OBJECT, // Revisits object hero is standing on
|
||||
ADVENTURE_VIEW_SELECTED,// Open window with currently selected hero/town
|
||||
@ -94,12 +96,15 @@ enum class EShortcut
|
||||
ADVENTURE_DIG_GRAIL,
|
||||
ADVENTURE_VIEW_PUZZLE,
|
||||
ADVENTURE_VIEW_WORLD,
|
||||
ADVENTURE_VIEW_WORLD_X1,
|
||||
ADVENTURE_VIEW_WORLD_X2,
|
||||
ADVENTURE_VIEW_WORLD_X4,
|
||||
ADVENTURE_TOGGLE_MAP_LEVEL,
|
||||
ADVENTURE_KINGDOM_OVERVIEW,
|
||||
ADVENTURE_QUEST_LOG,
|
||||
ADVENTURE_CAST_SPELL,
|
||||
ADVENTURE_END_TURN,
|
||||
ADVENTURE_THIEVES_GUILD,
|
||||
ADVENTURE_EXIT_WORLD_VIEW,
|
||||
|
||||
// Move hero one tile in specified direction. Bound to cursors & numpad buttons
|
||||
ADVENTURE_MOVE_HERO_SW,
|
||||
@ -153,4 +158,3 @@ enum class EShortcut
|
||||
|
||||
AFTER_LAST
|
||||
};
|
||||
|
||||
|
@ -64,6 +64,8 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_RETURN, EShortcut::LOBBY_LOAD_GAME },
|
||||
{SDLK_KP_ENTER, EShortcut::LOBBY_LOAD_GAME },
|
||||
{SDLK_s, EShortcut::LOBBY_SAVE_GAME },
|
||||
{SDLK_RETURN, EShortcut::LOBBY_SAVE_GAME },
|
||||
{SDLK_KP_ENTER, EShortcut::LOBBY_SAVE_GAME },
|
||||
{SDLK_r, EShortcut::LOBBY_RANDOM_MAP },
|
||||
{SDLK_h, EShortcut::LOBBY_HIDE_CHAT },
|
||||
{SDLK_a, EShortcut::LOBBY_ADDITIONAL_OPTIONS },
|
||||
@ -79,8 +81,8 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_TAB, EShortcut::GAME_ACTIVATE_CONSOLE },
|
||||
{SDLK_o, EShortcut::ADVENTURE_GAME_OPTIONS },
|
||||
{SDLK_F6, EShortcut::ADVENTURE_TOGGLE_GRID },
|
||||
{SDLK_z, EShortcut::ADVENTURE_TOGGLE_SLEEP },
|
||||
{SDLK_w, EShortcut::ADVENTURE_TOGGLE_SLEEP },
|
||||
{SDLK_z, EShortcut::ADVENTURE_SET_HERO_ASLEEP },
|
||||
{SDLK_w, EShortcut::ADVENTURE_SET_HERO_AWAKE },
|
||||
{SDLK_m, EShortcut::ADVENTURE_MOVE_HERO },
|
||||
{SDLK_SPACE, EShortcut::ADVENTURE_VISIT_OBJECT },
|
||||
{SDLK_KP_1, EShortcut::ADVENTURE_MOVE_HERO_SW },
|
||||
@ -106,11 +108,13 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
|
||||
{SDLK_d, EShortcut::ADVENTURE_DIG_GRAIL },
|
||||
{SDLK_p, EShortcut::ADVENTURE_VIEW_PUZZLE },
|
||||
{SDLK_v, EShortcut::ADVENTURE_VIEW_WORLD },
|
||||
{SDLK_1, EShortcut::ADVENTURE_VIEW_WORLD_X1 },
|
||||
{SDLK_2, EShortcut::ADVENTURE_VIEW_WORLD_X2 },
|
||||
{SDLK_4, EShortcut::ADVENTURE_VIEW_WORLD_X4 },
|
||||
{SDLK_u, EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL},
|
||||
{SDLK_k, EShortcut::ADVENTURE_KINGDOM_OVERVIEW},
|
||||
{SDLK_q, EShortcut::ADVENTURE_QUEST_LOG },
|
||||
{SDLK_c, EShortcut::ADVENTURE_CAST_SPELL },
|
||||
{SDLK_e, EShortcut::ADVENTURE_END_TURN },
|
||||
{SDLK_g, EShortcut::ADVENTURE_THIEVES_GUILD },
|
||||
{SDLK_q, EShortcut::BATTLE_TOGGLE_QUEUE },
|
||||
{SDLK_c, EShortcut::BATTLE_USE_CREATURE_SPELL },
|
||||
@ -218,6 +222,8 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"adventureGameOptions", EShortcut::ADVENTURE_GAME_OPTIONS },
|
||||
{"adventureToggleGrid", EShortcut::ADVENTURE_TOGGLE_GRID },
|
||||
{"adventureToggleSleep", EShortcut::ADVENTURE_TOGGLE_SLEEP },
|
||||
{"adventureSetHeroAsleep", EShortcut::ADVENTURE_SET_HERO_ASLEEP },
|
||||
{"adventureSetHeroAwake", EShortcut::ADVENTURE_SET_HERO_AWAKE },
|
||||
{"adventureMoveHero", EShortcut::ADVENTURE_MOVE_HERO },
|
||||
{"adventureVisitObject", EShortcut::ADVENTURE_VISIT_OBJECT },
|
||||
{"adventureMoveHeroSW", EShortcut::ADVENTURE_MOVE_HERO_SW },
|
||||
@ -238,12 +244,15 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"adventureDigGrail", EShortcut::ADVENTURE_DIG_GRAIL },
|
||||
{"adventureViewPuzzle", EShortcut::ADVENTURE_VIEW_PUZZLE },
|
||||
{"adventureViewWorld", EShortcut::ADVENTURE_VIEW_WORLD },
|
||||
{"adventureViewWorld1", EShortcut::ADVENTURE_VIEW_WORLD_X1 },
|
||||
{"adventureViewWorld2", EShortcut::ADVENTURE_VIEW_WORLD_X2 },
|
||||
{"adventureViewWorld4", EShortcut::ADVENTURE_VIEW_WORLD_X4 },
|
||||
{"adventureToggleMapLevel", EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL},
|
||||
{"adventureKingdomOverview", EShortcut::ADVENTURE_KINGDOM_OVERVIEW},
|
||||
{"adventureQuestLog", EShortcut::ADVENTURE_QUEST_LOG },
|
||||
{"adventureCastSpell", EShortcut::ADVENTURE_CAST_SPELL },
|
||||
{"adventureEndTurn", EShortcut::ADVENTURE_END_TURN },
|
||||
{"adventureThievesGuild", EShortcut::ADVENTURE_THIEVES_GUILD },
|
||||
{"adventureExitWorldView", EShortcut::ADVENTURE_EXIT_WORLD_VIEW },
|
||||
{"battleToggleQueue", EShortcut::BATTLE_TOGGLE_QUEUE },
|
||||
{"battleUseCreatureSpell", EShortcut::BATTLE_USE_CREATURE_SPELL },
|
||||
{"battleSurrender", EShortcut::BATTLE_SURRENDER },
|
||||
@ -278,85 +287,3 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
return shortcutNames.at(identifier);
|
||||
return EShortcut::NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -51,7 +51,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
|
||||
buttonOptions = std::make_shared<CButton>(Point(411, 510), "GSPBUTT.DEF", CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), EShortcut::LOBBY_ADDITIONAL_OPTIONS);
|
||||
};
|
||||
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 83), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 80), "GSPBUT2.DEF", CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
|
||||
buttonChat->addTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL);
|
||||
|
||||
switch(screenType)
|
||||
|
@ -71,7 +71,6 @@ CSelectionBase::CSelectionBase(ESelectionScreen type)
|
||||
: CWindowObject(BORDERED | SHADOW_DISABLED), ISelectionScreenInfo(type)
|
||||
{
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
IShowActivatable::type = BLOCK_ADV_HOTKEYS;
|
||||
pos.w = 762;
|
||||
pos.h = 584;
|
||||
if(screenType == ESelectionScreen::campaignList)
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "../../lib/NetPacksLobby.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/GameSettings.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
|
@ -299,6 +299,26 @@ CMainMenu::~CMainMenu()
|
||||
GH.curInt = nullptr;
|
||||
}
|
||||
|
||||
void CMainMenu::activate()
|
||||
{
|
||||
// check if screen was resized while main menu was inactive - e.g. in gameplay mode
|
||||
if (pos.dimensions() != GH.screenDimensions())
|
||||
onScreenResize();
|
||||
|
||||
CIntObject::activate();
|
||||
}
|
||||
|
||||
void CMainMenu::onScreenResize()
|
||||
{
|
||||
pos.w = GH.screenDimensions().x;
|
||||
pos.h = GH.screenDimensions().y;
|
||||
|
||||
menu = nullptr;
|
||||
menu = std::make_shared<CMenuScreen>(CMainMenuConfig::get().getConfig()["window"]);
|
||||
|
||||
backgroundAroundMenu->pos = pos;
|
||||
}
|
||||
|
||||
void CMainMenu::update()
|
||||
{
|
||||
if(CMM != this->shared_from_this()) //don't update if you are not a main interface
|
||||
|
@ -141,6 +141,8 @@ public:
|
||||
std::shared_ptr<CMenuScreen> menu;
|
||||
|
||||
~CMainMenu();
|
||||
void activate() override;
|
||||
void onScreenResize() override;
|
||||
void update() override;
|
||||
static void openLobby(ESelectionScreen screenType, bool host, const std::vector<std::string> * names, ELoadMode loadMode);
|
||||
static void openCampaignLobby(const std::string & campaignFileName);
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "../../CCallback.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "MapViewModel.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "MapViewModel.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
36
client/render/IScreenHandler.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* IScreenHandler.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 Point;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class IScreenHandler
|
||||
{
|
||||
public:
|
||||
virtual ~IScreenHandler() = default;
|
||||
|
||||
/// Updates window state after fullscreen state has been changed in settings
|
||||
virtual void onScreenResize() = 0;
|
||||
|
||||
/// De-initializes window state
|
||||
virtual void close() = 0;
|
||||
|
||||
/// Fills screen with black color, erasing any existing content
|
||||
virtual void clearScreen() = 0;
|
||||
|
||||
/// Returns list of resolutions supported by current screen
|
||||
virtual std::vector<Point> getSupportedResolutions() const = 0;
|
||||
|
||||
/// Returns <min, max> range of possible values for screen scaling percentage
|
||||
virtual std::tuple<int, int> getSupportedScalingRange() const = 0;
|
||||
};
|
@ -861,43 +861,6 @@ void CSDL_Ext::getClipRect(SDL_Surface * src, Rect & other)
|
||||
other = CSDL_Ext::fromSDL(rect);
|
||||
}
|
||||
|
||||
bool CSDL_Ext::isResolutionSupported(const std::vector<Point> & resolutions, const Point toTest )
|
||||
{
|
||||
#if defined(VCMI_MOBILE)
|
||||
// ios can use any resolution
|
||||
// presumably, same goes for Android
|
||||
return true;
|
||||
#else
|
||||
// in fullscreen only resolutions supported by monitor can be used
|
||||
return vstd::contains(resolutions, toTest);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<Point> CSDL_Ext::getSupportedResolutions()
|
||||
{
|
||||
int displayID = SDL_GetWindowDisplayIndex(mainWindow);
|
||||
return getSupportedResolutions(displayID);
|
||||
}
|
||||
|
||||
std::vector<Point> CSDL_Ext::getSupportedResolutions( int displayIndex)
|
||||
{
|
||||
std::vector<Point> result;
|
||||
|
||||
int modesCount = SDL_GetNumDisplayModes(displayIndex);
|
||||
|
||||
for (int i =0; i < modesCount; ++i)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0)
|
||||
continue;
|
||||
|
||||
Point resolution(mode.w, mode.h);
|
||||
|
||||
result.push_back(resolution);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int);
|
||||
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);
|
||||
template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int);
|
||||
|
@ -105,11 +105,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
|
||||
void convertToGrayscaleBpp(SDL_Surface * surf, const Rect & rect);
|
||||
void convertToGrayscale(SDL_Surface * surf, const Rect & rect);
|
||||
|
||||
bool isResolutionSupported(const std::vector<Point> & resolutions, const Point toTest);
|
||||
|
||||
std::vector<Point> getSupportedResolutions();
|
||||
std::vector<Point> getSupportedResolutions(int displayIndex);
|
||||
|
||||
void setColorKey(SDL_Surface * surface, SDL_Color color);
|
||||
|
||||
///set key-color to 0,255,255
|
||||
|
510
client/renderSDL/ScreenHandler.cpp
Normal file
@ -0,0 +1,510 @@
|
||||
/*
|
||||
* ScreenHandler.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 "ScreenHandler.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/NotificationHandler.h"
|
||||
#include "CMT.h"
|
||||
#include "SDL_Extensions.h"
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "../lib/CAndroidVMHelper.h"
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
// TODO: should be made into a private members of ScreenHandler
|
||||
SDL_Window * mainWindow = nullptr;
|
||||
SDL_Renderer * mainRenderer = nullptr;
|
||||
SDL_Texture * screenTexture = nullptr;
|
||||
SDL_Surface * screen = nullptr; //main screen surface
|
||||
SDL_Surface * screen2 = nullptr; //and hlp surface (used to store not-active interfaces layer)
|
||||
SDL_Surface * screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
|
||||
|
||||
static const std::string NAME_AFFIX = "client";
|
||||
static const std::string NAME = GameConstants::VCMI_VERSION + std::string(" (") + NAME_AFFIX + ')'; //application name
|
||||
|
||||
std::tuple<int, int> ScreenHandler::getSupportedScalingRange() const
|
||||
{
|
||||
// H3 resolution, any resolution smaller than that is not correctly supported
|
||||
static const Point minResolution = {800, 600};
|
||||
// arbitrary limit on *downscaling*. Allow some downscaling, if requested by user. Should be generally limited to 100+ for all but few devices
|
||||
static const double minimalScaling = 50;
|
||||
|
||||
Point renderResolution = getPreferredRenderingResolution();
|
||||
double maximalScalingWidth = 100.0 * renderResolution.x / minResolution.x;
|
||||
double maximalScalingHeight = 100.0 * renderResolution.y / minResolution.y;
|
||||
double maximalScaling = std::min(maximalScalingWidth, maximalScalingHeight);
|
||||
|
||||
return { minimalScaling, maximalScaling };
|
||||
}
|
||||
|
||||
Point ScreenHandler::getPreferredLogicalResolution() const
|
||||
{
|
||||
Point renderResolution = getPreferredRenderingResolution();
|
||||
auto [minimalScaling, maximalScaling] = getSupportedScalingRange();
|
||||
|
||||
int userScaling = settings["video"]["resolution"]["scaling"].Integer();
|
||||
int scaling = std::clamp(userScaling, minimalScaling, maximalScaling);
|
||||
|
||||
Point logicalResolution = renderResolution * 100.0 / scaling;
|
||||
|
||||
return logicalResolution;
|
||||
}
|
||||
|
||||
Point ScreenHandler::getPreferredRenderingResolution() const
|
||||
{
|
||||
if (getPreferredWindowMode() == EWindowMode::FULLSCREEN_BORDERLESS_WINDOWED)
|
||||
{
|
||||
SDL_Rect bounds;
|
||||
if (SDL_GetDisplayBounds(getPreferredDisplayIndex(), &bounds) == 0)
|
||||
return Point(bounds.w, bounds.h);
|
||||
}
|
||||
|
||||
const JsonNode & video = settings["video"];
|
||||
int width = video["resolution"]["width"].Integer();
|
||||
int height = video["resolution"]["height"].Integer();
|
||||
|
||||
return Point(width, height);
|
||||
}
|
||||
|
||||
int ScreenHandler::getPreferredDisplayIndex() const
|
||||
{
|
||||
#ifdef VCMI_MOBILE
|
||||
// Assuming no multiple screens on Android / ios?
|
||||
return 0;
|
||||
#else
|
||||
if (mainWindow != nullptr)
|
||||
{
|
||||
int result = SDL_GetWindowDisplayIndex(mainWindow);
|
||||
if (result >= 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return settings["video"]["displayIndex"].Integer();
|
||||
#endif
|
||||
}
|
||||
|
||||
EWindowMode ScreenHandler::getPreferredWindowMode() const
|
||||
{
|
||||
#ifdef VCMI_MOBILE
|
||||
// On Android / ios game will always render to screen size
|
||||
return EWindowMode::FULLSCREEN_BORDERLESS_WINDOWED;
|
||||
#else
|
||||
const JsonNode & video = settings["video"];
|
||||
bool fullscreen = video["fullscreen"].Bool();
|
||||
bool realFullscreen = settings["video"]["realFullscreen"].Bool();
|
||||
|
||||
if (!fullscreen)
|
||||
return EWindowMode::WINDOWED;
|
||||
|
||||
if (realFullscreen)
|
||||
return EWindowMode::FULLSCREEN_EXCLUSIVE;
|
||||
else
|
||||
return EWindowMode::FULLSCREEN_BORDERLESS_WINDOWED;
|
||||
#endif
|
||||
}
|
||||
|
||||
ScreenHandler::ScreenHandler()
|
||||
{
|
||||
#ifdef VCMI_WINDOWS
|
||||
// set VCMI as "per-monitor DPI awareness". This completely disables any DPI-scaling by system.
|
||||
// Might not be the best solution since VCMI can't automatically adjust to DPI changes (including moving to monitors with different DPI scaling)
|
||||
// However this fixed unintuitive bug where player selects specific resolution for windowed mode, but ends up with completely different one due to scaling
|
||||
// NOTE: requires SDL 2.24.
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_DPI_AWARENESS, "permonitor");
|
||||
#endif
|
||||
|
||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO))
|
||||
{
|
||||
logGlobal->error("Something was wrong: %s", SDL_GetError());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
const auto & logCallback = [](void * userdata, int category, SDL_LogPriority priority, const char * message)
|
||||
{
|
||||
logGlobal->debug("SDL(category %d; priority %d) %s", category, priority, message);
|
||||
};
|
||||
|
||||
SDL_LogSetOutputFunction(logCallback, nullptr);
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
// manually setting egl pixel format, as a possible solution for sdl2<->android problem
|
||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=2291
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
|
||||
#endif // VCMI_ANDROID
|
||||
|
||||
validateSettings();
|
||||
recreateWindowAndScreenBuffers();
|
||||
}
|
||||
|
||||
void ScreenHandler::recreateWindowAndScreenBuffers()
|
||||
{
|
||||
destroyScreenBuffers();
|
||||
|
||||
if(mainWindow == nullptr)
|
||||
initializeWindow();
|
||||
else
|
||||
updateWindowState();
|
||||
|
||||
initializeScreenBuffers();
|
||||
|
||||
if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool())
|
||||
{
|
||||
NotificationHandler::init(mainWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenHandler::updateWindowState()
|
||||
{
|
||||
#ifndef VCMI_MOBILE
|
||||
int displayIndex = getPreferredDisplayIndex();
|
||||
|
||||
switch(getPreferredWindowMode())
|
||||
{
|
||||
case EWindowMode::FULLSCREEN_EXCLUSIVE:
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN);
|
||||
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetDesktopDisplayMode(displayIndex, &mode);
|
||||
Point resolution = getPreferredRenderingResolution();
|
||||
|
||||
mode.w = resolution.x;
|
||||
mode.h = resolution.y;
|
||||
|
||||
SDL_SetWindowDisplayMode(mainWindow, &mode);
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex));
|
||||
|
||||
return;
|
||||
}
|
||||
case EWindowMode::FULLSCREEN_BORDERLESS_WINDOWED:
|
||||
{
|
||||
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex));
|
||||
return;
|
||||
}
|
||||
case EWindowMode::WINDOWED:
|
||||
{
|
||||
Point resolution = getPreferredRenderingResolution();
|
||||
SDL_SetWindowFullscreen(mainWindow, 0);
|
||||
SDL_SetWindowSize(mainWindow, resolution.x, resolution.y);
|
||||
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScreenHandler::initializeWindow()
|
||||
{
|
||||
mainWindow = createWindow();
|
||||
|
||||
if(mainWindow == nullptr)
|
||||
throw std::runtime_error("Unable to create window\n");
|
||||
|
||||
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
|
||||
mainRenderer = SDL_CreateRenderer(mainWindow, getPreferredRenderingDriver(), 0);
|
||||
|
||||
if(mainRenderer == nullptr)
|
||||
throw std::runtime_error("Unable to create renderer\n");
|
||||
|
||||
SDL_RendererInfo info;
|
||||
SDL_GetRendererInfo(mainRenderer, &info);
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
logGlobal->info("Created renderer %s", info.name);
|
||||
}
|
||||
|
||||
void ScreenHandler::initializeScreenBuffers()
|
||||
{
|
||||
#ifdef VCMI_ENDIAN_BIG
|
||||
int bmask = 0xff000000;
|
||||
int gmask = 0x00ff0000;
|
||||
int rmask = 0x0000ff00;
|
||||
int amask = 0x000000ff;
|
||||
#else
|
||||
int bmask = 0x000000ff;
|
||||
int gmask = 0x0000ff00;
|
||||
int rmask = 0x00ff0000;
|
||||
int amask = 0xFF000000;
|
||||
#endif
|
||||
|
||||
auto logicalSize = getPreferredLogicalResolution();
|
||||
SDL_RenderSetLogicalSize(mainRenderer, logicalSize.x, logicalSize.y);
|
||||
|
||||
screen = SDL_CreateRGBSurface(0, logicalSize.x, logicalSize.y, 32, rmask, gmask, bmask, amask);
|
||||
if(nullptr == screen)
|
||||
{
|
||||
logGlobal->error("Unable to create surface %dx%d with %d bpp: %s", logicalSize.x, logicalSize.y, 32, SDL_GetError());
|
||||
throw std::runtime_error("Unable to create surface");
|
||||
}
|
||||
//No blending for screen itself. Required for proper cursor rendering.
|
||||
SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
|
||||
|
||||
screenTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, logicalSize.x, logicalSize.y);
|
||||
|
||||
if(nullptr == screenTexture)
|
||||
{
|
||||
logGlobal->error("Unable to create screen texture");
|
||||
logGlobal->error(SDL_GetError());
|
||||
throw std::runtime_error("Unable to create screen texture");
|
||||
}
|
||||
|
||||
screen2 = CSDL_Ext::copySurface(screen);
|
||||
|
||||
if(nullptr == screen2)
|
||||
{
|
||||
throw std::runtime_error("Unable to copy surface\n");
|
||||
}
|
||||
|
||||
if (GH.listInt.size() > 1)
|
||||
screenBuf = screen2;
|
||||
else
|
||||
screenBuf = screen;
|
||||
|
||||
clearScreen();
|
||||
}
|
||||
|
||||
SDL_Window * ScreenHandler::createWindowImpl(Point dimensions, int flags, bool center)
|
||||
{
|
||||
int displayIndex = getPreferredDisplayIndex();
|
||||
int positionFlags = center ? SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex) : SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex);
|
||||
|
||||
return SDL_CreateWindow(NAME.c_str(), positionFlags, positionFlags, dimensions.x, dimensions.y, flags);
|
||||
}
|
||||
|
||||
SDL_Window * ScreenHandler::createWindow()
|
||||
{
|
||||
#ifndef VCMI_MOBILE
|
||||
Point dimensions = getPreferredRenderingResolution();
|
||||
|
||||
switch(getPreferredWindowMode())
|
||||
{
|
||||
case EWindowMode::FULLSCREEN_EXCLUSIVE:
|
||||
return createWindowImpl(dimensions, SDL_WINDOW_FULLSCREEN, false);
|
||||
|
||||
case EWindowMode::FULLSCREEN_BORDERLESS_WINDOWED:
|
||||
return createWindowImpl(Point(), SDL_WINDOW_FULLSCREEN_DESKTOP, false);
|
||||
|
||||
case EWindowMode::WINDOWED:
|
||||
return createWindowImpl(dimensions, SDL_WINDOW_RESIZABLE, true);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
SDL_SetHint(SDL_HINT_IOS_HIDE_HOME_INDICATOR, "1");
|
||||
SDL_SetHint(SDL_HINT_RETURN_KEY_HIDES_IME, "1");
|
||||
|
||||
uint32_t windowFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALLOW_HIGHDPI;
|
||||
SDL_Window * result = createWindowImpl(Point(), windowFlags | SDL_WINDOW_METAL, false);
|
||||
|
||||
if(result != nullptr)
|
||||
return result;
|
||||
|
||||
logGlobal->warn("Metal unavailable, using OpenGLES");
|
||||
return createWindowImpl(Point(), windowFlags, false);
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
return createWindowImpl(Point(), SDL_WINDOW_FULLSCREEN, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ScreenHandler::onScreenResize()
|
||||
{
|
||||
recreateWindowAndScreenBuffers();
|
||||
GH.onScreenResize();
|
||||
}
|
||||
|
||||
void ScreenHandler::validateSettings()
|
||||
{
|
||||
#ifndef VCMI_MOBILE
|
||||
{
|
||||
int displayIndex = settings["video"]["displayIndex"].Integer();
|
||||
int displaysCount = SDL_GetNumVideoDisplays();
|
||||
|
||||
if (displayIndex >= displaysCount)
|
||||
{
|
||||
Settings writer = settings.write["video"]["displayIndex"];
|
||||
writer->Float() = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (getPreferredWindowMode() == EWindowMode::WINDOWED)
|
||||
{
|
||||
//we only check that our desired window size fits on screen
|
||||
int displayIndex = getPreferredDisplayIndex();
|
||||
Point resolution = getPreferredRenderingResolution();
|
||||
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
if (SDL_GetDesktopDisplayMode(displayIndex, &mode) == 0)
|
||||
{
|
||||
if(resolution.x > mode.w || resolution.y > mode.h)
|
||||
{
|
||||
Settings writer = settings.write["video"]["resolution"];
|
||||
writer["width"].Float() = mode.w;
|
||||
writer["height"].Float() = mode.h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getPreferredWindowMode() == EWindowMode::FULLSCREEN_EXCLUSIVE)
|
||||
{
|
||||
auto legalOptions = getSupportedResolutions();
|
||||
Point selectedResolution = getPreferredRenderingResolution();
|
||||
|
||||
if(!vstd::contains(legalOptions, selectedResolution))
|
||||
{
|
||||
// resolution selected for fullscreen mode is not supported by display
|
||||
// try to find current display resolution and use it instead as "reasonable default"
|
||||
SDL_DisplayMode mode;
|
||||
|
||||
if (SDL_GetDesktopDisplayMode(getPreferredDisplayIndex(), &mode) == 0)
|
||||
{
|
||||
Settings writer = settings.write["video"]["resolution"];
|
||||
writer["width"].Float() = mode.w;
|
||||
writer["height"].Float() = mode.h;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ScreenHandler::getPreferredRenderingDriver() const
|
||||
{
|
||||
int result = -1;
|
||||
const JsonNode & video = settings["video"];
|
||||
|
||||
int driversCount = SDL_GetNumRenderDrivers();
|
||||
std::string preferredDriverName = video["driver"].String();
|
||||
|
||||
logGlobal->info("Found %d render drivers", driversCount);
|
||||
|
||||
for(int it = 0; it < driversCount; it++)
|
||||
{
|
||||
SDL_RendererInfo info;
|
||||
if (SDL_GetRenderDriverInfo(it, &info) == 0)
|
||||
{
|
||||
std::string driverName(info.name);
|
||||
|
||||
if(!preferredDriverName.empty() && driverName == preferredDriverName)
|
||||
{
|
||||
result = it;
|
||||
logGlobal->info("\t%s (active)", driverName);
|
||||
}
|
||||
else
|
||||
logGlobal->info("\t%s", driverName);
|
||||
}
|
||||
else
|
||||
logGlobal->info("\t(error)");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScreenHandler::destroyScreenBuffers()
|
||||
{
|
||||
// screenBuf is not a separate surface, but points to either screen or screen2 - just set to null
|
||||
screenBuf = nullptr;
|
||||
|
||||
if(nullptr != screen2)
|
||||
{
|
||||
SDL_FreeSurface(screen2);
|
||||
screen2 = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != screen)
|
||||
{
|
||||
SDL_FreeSurface(screen);
|
||||
screen = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != screenTexture)
|
||||
{
|
||||
SDL_DestroyTexture(screenTexture);
|
||||
screenTexture = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenHandler::destroyWindow()
|
||||
{
|
||||
if(nullptr != mainRenderer)
|
||||
{
|
||||
SDL_DestroyRenderer(mainRenderer);
|
||||
mainRenderer = nullptr;
|
||||
}
|
||||
|
||||
if(nullptr != mainWindow)
|
||||
{
|
||||
SDL_DestroyWindow(mainWindow);
|
||||
mainWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenHandler::close()
|
||||
{
|
||||
if(settings["general"]["notifications"].Bool())
|
||||
NotificationHandler::destroy();
|
||||
|
||||
destroyScreenBuffers();
|
||||
destroyWindow();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void ScreenHandler::clearScreen()
|
||||
{
|
||||
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(mainRenderer);
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
}
|
||||
|
||||
std::vector<Point> ScreenHandler::getSupportedResolutions() const
|
||||
{
|
||||
int displayID = SDL_GetWindowDisplayIndex(mainWindow);
|
||||
return getSupportedResolutions(displayID);
|
||||
}
|
||||
|
||||
std::vector<Point> ScreenHandler::getSupportedResolutions( int displayIndex) const
|
||||
{
|
||||
//NOTE: this method is never called on Android/iOS, only on desktop systems
|
||||
|
||||
std::vector<Point> result;
|
||||
|
||||
int modesCount = SDL_GetNumDisplayModes(displayIndex);
|
||||
|
||||
for (int i =0; i < modesCount; ++i)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDisplayMode(displayIndex, i, &mode) == 0)
|
||||
{
|
||||
Point resolution(mode.w, mode.h);
|
||||
result.push_back(resolution);
|
||||
}
|
||||
}
|
||||
|
||||
boost::range::sort(result, [](const auto & left, const auto & right)
|
||||
{
|
||||
return left.x * left.y < right.x * right.y;
|
||||
});
|
||||
|
||||
// erase potential duplicates, e.g. resolutions with different framerate / bits per pixel
|
||||
result.erase(boost::unique(result).end(), result.end());
|
||||
|
||||
return result;
|
||||
}
|
89
client/renderSDL/ScreenHandler.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* ScreenHandler.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
|
||||
|
||||
struct SDL_Texture;
|
||||
struct SDL_Window;
|
||||
struct SDL_Renderer;
|
||||
struct SDL_Surface;
|
||||
|
||||
#include "../../lib/Point.h"
|
||||
#include "../render/IScreenHandler.h"
|
||||
|
||||
enum class EWindowMode
|
||||
{
|
||||
// game runs in a window that covers part of the screen
|
||||
WINDOWED,
|
||||
// game runs in a 'window' that always covers entire screen and uses unmodified desktop resolution
|
||||
// The only mode that is available on mobile devices
|
||||
FULLSCREEN_BORDERLESS_WINDOWED,
|
||||
// game runs in a fullscreen mode with resolution selected by player
|
||||
FULLSCREEN_EXCLUSIVE
|
||||
};
|
||||
|
||||
/// This class is responsible for management of game window and its main rendering surface
|
||||
class ScreenHandler : public IScreenHandler
|
||||
{
|
||||
/// Dimensions of target surfaces/textures, this value is what game logic views as screen size
|
||||
Point getPreferredLogicalResolution() const;
|
||||
|
||||
/// Dimensions of output window, if different from logicalResolution SDL will perform scaling
|
||||
/// This value is what player views as window size
|
||||
Point getPreferredRenderingResolution() const;
|
||||
|
||||
EWindowMode getPreferredWindowMode() const;
|
||||
|
||||
/// Returns index of display on which window should be created
|
||||
int getPreferredDisplayIndex() const;
|
||||
|
||||
/// Returns index of rendering driver preferred by player or -1 if no preference
|
||||
int getPreferredRenderingDriver() const;
|
||||
|
||||
/// Creates SDL window with specified parameters
|
||||
SDL_Window * createWindowImpl(Point dimensions, int flags, bool center);
|
||||
|
||||
/// Creates SDL window using OS-specific settings & user-specific config
|
||||
SDL_Window * createWindow();
|
||||
|
||||
/// Manages window and SDL renderer
|
||||
void initializeWindow();
|
||||
void destroyWindow();
|
||||
|
||||
/// Manages surfaces & textures used for
|
||||
void initializeScreenBuffers();
|
||||
void destroyScreenBuffers();
|
||||
|
||||
/// Updates state (e.g. position) of game window after resolution/fullscreen change
|
||||
void updateWindowState();
|
||||
|
||||
/// Initializes or reiniitalizes all screen state
|
||||
void recreateWindowAndScreenBuffers();
|
||||
|
||||
/// Performs validation of settings and updates them to valid values if necessary
|
||||
void validateSettings();
|
||||
public:
|
||||
|
||||
/// Creates and initializes screen, window and SDL state
|
||||
ScreenHandler();
|
||||
|
||||
/// Updates and potentially recreates target screen to match selected fullscreen status
|
||||
void onScreenResize() final;
|
||||
|
||||
/// De-initializes and destroys screen, window and SDL state
|
||||
void close() final;
|
||||
|
||||
/// Fills screen with black color, erasing any existing content
|
||||
void clearScreen() final;
|
||||
|
||||
std::vector<Point> getSupportedResolutions() const final;
|
||||
std::vector<Point> getSupportedResolutions(int displayIndex) const;
|
||||
std::tuple<int, int> getSupportedScalingRange() const final;
|
||||
};
|
@ -249,32 +249,32 @@ CButton::CButton(Point position, const std::string &defName, const std::pair<std
|
||||
if (!defName.empty())
|
||||
{
|
||||
imageNames.push_back(defName);
|
||||
setIndex(0, playerColoredButton);
|
||||
setIndex(0);
|
||||
if (playerColoredButton)
|
||||
image->playerColored(LOCPLINT->playerID);
|
||||
}
|
||||
}
|
||||
|
||||
void CButton::setIndex(size_t index, bool playerColoredButton)
|
||||
void CButton::setIndex(size_t index)
|
||||
{
|
||||
if (index == currentImage || index>=imageNames.size())
|
||||
return;
|
||||
currentImage = index;
|
||||
auto anim = std::make_shared<CAnimation>(imageNames[index]);
|
||||
setImage(anim, playerColoredButton);
|
||||
setImage(anim);
|
||||
}
|
||||
|
||||
void CButton::setImage(std::shared_ptr<CAnimation> anim, bool playerColoredButton, int animFlags)
|
||||
void CButton::setImage(std::shared_ptr<CAnimation> anim, int animFlags)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
|
||||
image = std::make_shared<CAnimImage>(anim, getState(), 0, 0, 0, animFlags);
|
||||
if (playerColoredButton)
|
||||
image->playerColored(LOCPLINT->playerID);
|
||||
pos = image->pos;
|
||||
}
|
||||
|
||||
void CButton::setPlayerColor(PlayerColor player)
|
||||
{
|
||||
if (image)
|
||||
if (image && image->isPlayerColored())
|
||||
image->playerColored(player);
|
||||
}
|
||||
|
||||
|
@ -16,13 +16,7 @@
|
||||
#include <SDL_pixels.h>
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace config
|
||||
{
|
||||
struct ButtonInfo;
|
||||
}
|
||||
class Rect;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
struct SDL_Surface;
|
||||
@ -104,8 +98,8 @@ public:
|
||||
CFunctionList<void()> Callback = 0, EShortcut key = {}, bool playerColoredButton = false );
|
||||
|
||||
/// Appearance modifiers
|
||||
void setIndex(size_t index, bool playerColoredButton=false);
|
||||
void setImage(std::shared_ptr<CAnimation> anim, bool playerColoredButton=false, int animFlags=0);
|
||||
void setIndex(size_t index);
|
||||
void setImage(std::shared_ptr<CAnimation> anim, int animFlags=0);
|
||||
void setPlayerColor(PlayerColor player);
|
||||
|
||||
/// CIntObject overrides
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/ColorFilter.h"
|
||||
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
@ -109,6 +110,16 @@ void CPicture::colorize(PlayerColor player)
|
||||
CFilledTexture::CFilledTexture(std::string imageName, Rect position):
|
||||
CIntObject(0, position.topLeft()),
|
||||
texture(IImage::createFromFile(imageName))
|
||||
{
|
||||
pos.w = position.w;
|
||||
pos.h = position.h;
|
||||
imageArea = Rect(Point(), texture->dimensions());
|
||||
}
|
||||
|
||||
CFilledTexture::CFilledTexture(std::shared_ptr<IImage> image, Rect position, Rect imageArea)
|
||||
: CIntObject(0, position.topLeft())
|
||||
, texture(image)
|
||||
, imageArea(imageArea)
|
||||
{
|
||||
pos.w = position.w;
|
||||
pos.h = position.h;
|
||||
@ -118,17 +129,45 @@ void CFilledTexture::showAll(SDL_Surface *to)
|
||||
{
|
||||
CSDL_Ext::CClipRectGuard guard(to, pos);
|
||||
|
||||
for (int y=pos.top(); y < pos.bottom(); y+= texture->height())
|
||||
for (int y=pos.top(); y < pos.bottom(); y+= imageArea.h)
|
||||
{
|
||||
for (int x=pos.left(); x < pos.right(); x+=texture->width())
|
||||
texture->draw(to, x, y);
|
||||
for (int x=pos.left(); x < pos.right(); x+= imageArea.w)
|
||||
texture->draw(to, x, y, &imageArea);
|
||||
}
|
||||
}
|
||||
|
||||
FilledTexturePlayerColored::FilledTexturePlayerColored(std::string imageName, Rect position)
|
||||
: CFilledTexture(imageName, position)
|
||||
{
|
||||
}
|
||||
|
||||
void FilledTexturePlayerColored::playerColored(PlayerColor player)
|
||||
{
|
||||
// Color transform to make color of brown DIBOX.PCX texture match color of specified player
|
||||
std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = {
|
||||
ColorFilter::genRangeShifter( 0.25, 0, 0, 1.25, 0.00, 0.00 ), // red
|
||||
ColorFilter::genRangeShifter( 0, 0, 0, 0.45, 1.20, 4.50 ), // blue
|
||||
ColorFilter::genRangeShifter( 0.40, 0.27, 0.23, 1.10, 1.20, 1.15 ), // tan
|
||||
ColorFilter::genRangeShifter( -0.27, 0.10, -0.27, 0.70, 1.70, 0.70 ), // green
|
||||
ColorFilter::genRangeShifter( 0.47, 0.17, -0.27, 1.60, 1.20, 0.70 ), // orange
|
||||
ColorFilter::genRangeShifter( 0.12, -0.1, 0.25, 1.15, 1.20, 2.20 ), // purple
|
||||
ColorFilter::genRangeShifter( -0.13, 0.23, 0.23, 0.90, 1.20, 2.20 ), // teal
|
||||
ColorFilter::genRangeShifter( 0.44, 0.15, 0.25, 1.00, 1.00, 1.75 ) // pink
|
||||
};
|
||||
|
||||
assert(player.isValidPlayer());
|
||||
if (!player.isValidPlayer())
|
||||
{
|
||||
logGlobal->error("Unable to colorize to invalid player color %d!", static_cast<int>(player.getNum()));
|
||||
return;
|
||||
}
|
||||
|
||||
texture->adjustPalette(filters[player.getNum()], 0);
|
||||
}
|
||||
|
||||
CAnimImage::CAnimImage(const std::string & name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
|
||||
frame(Frame),
|
||||
group(Group),
|
||||
player(-1),
|
||||
flags(Flags)
|
||||
{
|
||||
pos.x += x;
|
||||
@ -141,7 +180,6 @@ CAnimImage::CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, size_t Gr
|
||||
anim(Anim),
|
||||
frame(Frame),
|
||||
group(Group),
|
||||
player(-1),
|
||||
flags(Flags)
|
||||
{
|
||||
pos.x += x;
|
||||
@ -153,7 +191,6 @@ CAnimImage::CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, Rect targ
|
||||
anim(Anim),
|
||||
frame(Frame),
|
||||
group(Group),
|
||||
player(-1),
|
||||
flags(Flags),
|
||||
scaledSize(targetPos.w, targetPos.h)
|
||||
{
|
||||
@ -242,8 +279,8 @@ void CAnimImage::setFrame(size_t Frame, size_t Group)
|
||||
group = Group;
|
||||
if(auto img = anim->getImage(frame, group))
|
||||
{
|
||||
if (flags & CShowableAnim::PLAYER_COLORED)
|
||||
img->playerColored(player);
|
||||
if (player.has_value())
|
||||
img->playerColored(*player);
|
||||
setSizeFromImage(*img);
|
||||
}
|
||||
}
|
||||
@ -254,10 +291,14 @@ void CAnimImage::setFrame(size_t Frame, size_t Group)
|
||||
void CAnimImage::playerColored(PlayerColor currPlayer)
|
||||
{
|
||||
player = currPlayer;
|
||||
flags |= CShowableAnim::PLAYER_COLORED;
|
||||
anim->getImage(frame, group)->playerColored(player);
|
||||
anim->getImage(frame, group)->playerColored(*player);
|
||||
if (flags & CShowableAnim::BASE)
|
||||
anim->getImage(0, group)->playerColored(player);
|
||||
anim->getImage(0, group)->playerColored(*player);
|
||||
}
|
||||
|
||||
bool CAnimImage::isPlayerColored() const
|
||||
{
|
||||
return player.has_value();
|
||||
}
|
||||
|
||||
CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
|
||||
|
@ -68,14 +68,25 @@ public:
|
||||
/// area filled with specific texture
|
||||
class CFilledTexture : public CIntObject
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<IImage> texture;
|
||||
Rect imageArea;
|
||||
|
||||
public:
|
||||
CFilledTexture(std::string imageName, Rect position);
|
||||
CFilledTexture(std::shared_ptr<IImage> image, Rect position, Rect imageArea);
|
||||
|
||||
void showAll(SDL_Surface *to) override;
|
||||
};
|
||||
|
||||
class FilledTexturePlayerColored : public CFilledTexture
|
||||
{
|
||||
public:
|
||||
FilledTexturePlayerColored(std::string imageName, Rect position);
|
||||
|
||||
void playerColored(PlayerColor player);
|
||||
};
|
||||
|
||||
/// Class for displaying one image from animation
|
||||
class CAnimImage: public CIntObject
|
||||
{
|
||||
@ -84,10 +95,12 @@ private:
|
||||
//displayed frame/group
|
||||
size_t frame;
|
||||
size_t group;
|
||||
PlayerColor player;
|
||||
ui8 flags;
|
||||
const Point scaledSize;
|
||||
|
||||
/// If set, then image is colored using player-specific palette
|
||||
std::optional<PlayerColor> player;
|
||||
|
||||
bool isScaled() const;
|
||||
void setSizeFromImage(const IImage &img);
|
||||
void init();
|
||||
@ -99,15 +112,18 @@ public:
|
||||
CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, Rect targetPos, size_t Group=0, ui8 Flags=0);
|
||||
~CAnimImage();
|
||||
|
||||
//size of animation
|
||||
/// size of animation
|
||||
size_t size();
|
||||
|
||||
//change displayed frame on this one
|
||||
/// change displayed frame on this one
|
||||
void setFrame(size_t Frame, size_t Group=0);
|
||||
|
||||
//makes image player-colored
|
||||
/// makes image player-colored to specific player
|
||||
void playerColored(PlayerColor player);
|
||||
|
||||
/// returns true if image has player-colored effect applied
|
||||
bool isPlayerColored() const;
|
||||
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
||||
|
||||
@ -120,7 +136,6 @@ public:
|
||||
BASE=1, //base frame will be blitted before current one
|
||||
HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
|
||||
VERTICAL_FLIP=4, //TODO: will be displayed rotated
|
||||
PLAYER_COLORED=16, //TODO: all loaded images will be player-colored
|
||||
PLAY_ONCE=32 //play animation only once and stop at last frame
|
||||
};
|
||||
protected:
|
||||
|
@ -399,7 +399,7 @@ void CGStatusBar::clear()
|
||||
write({});
|
||||
}
|
||||
|
||||
CGStatusBar::CGStatusBar(std::shared_ptr<CPicture> background_, EFonts Font, ETextAlignment Align, const SDL_Color & Color)
|
||||
CGStatusBar::CGStatusBar(std::shared_ptr<CIntObject> background_, EFonts Font, ETextAlignment Align, const SDL_Color & Color)
|
||||
: CLabel(background_->pos.x, background_->pos.y, Font, Align, Color, "")
|
||||
, enteringText(false)
|
||||
{
|
||||
@ -419,28 +419,31 @@ CGStatusBar::CGStatusBar(int x, int y, std::string name, int maxw)
|
||||
addUsedEvents(LCLICK);
|
||||
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
|
||||
background = std::make_shared<CPicture>(name);
|
||||
auto backgroundImage = std::make_shared<CPicture>(name);
|
||||
background = backgroundImage;
|
||||
pos = background->pos;
|
||||
|
||||
if((unsigned)maxw < (unsigned)pos.w) //(insigned)-1 > than any correct value of pos.w
|
||||
{
|
||||
//execution of this block when maxw is incorrect breaks text centralization (issue #3151)
|
||||
vstd::amin(pos.w, maxw);
|
||||
background->srcRect = Rect(0, 0, maxw, pos.h);
|
||||
backgroundImage->srcRect = Rect(0, 0, maxw, pos.h);
|
||||
}
|
||||
autoRedraw = false;
|
||||
}
|
||||
|
||||
CGStatusBar::~CGStatusBar()
|
||||
{
|
||||
assert(GH.statusbar.get() != this || GH.statusbar == nullptr);
|
||||
if (GH.statusbar.get() == this)
|
||||
GH.statusbar = nullptr;
|
||||
}
|
||||
|
||||
void CGStatusBar::show(SDL_Surface * to)
|
||||
{
|
||||
showAll(to);
|
||||
}
|
||||
|
||||
void CGStatusBar::init()
|
||||
{
|
||||
GH.statusbar = shared_from_this();
|
||||
}
|
||||
|
||||
void CGStatusBar::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(!down)
|
||||
@ -450,8 +453,16 @@ void CGStatusBar::clickLeft(tribool down, bool previousState)
|
||||
}
|
||||
}
|
||||
|
||||
void CGStatusBar::activate()
|
||||
{
|
||||
GH.statusbar = shared_from_this();
|
||||
CIntObject::deactivate();
|
||||
}
|
||||
|
||||
void CGStatusBar::deactivate()
|
||||
{
|
||||
assert(GH.statusbar.get() == this);
|
||||
|
||||
if (enteringText)
|
||||
LOCPLINT->cingconsole->endEnteringText(false);
|
||||
|
||||
|
@ -45,7 +45,7 @@ protected:
|
||||
Point getBorderSize() override;
|
||||
virtual std::string visibleText();
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CIntObject> background;
|
||||
std::string text;
|
||||
bool autoRedraw; //whether control will redraw itself on setTxt
|
||||
|
||||
@ -125,9 +125,7 @@ class CGStatusBar : public CLabel, public std::enable_shared_from_this<CGStatusB
|
||||
std::string consoleText;
|
||||
bool enteringText;
|
||||
|
||||
void init();
|
||||
|
||||
CGStatusBar(std::shared_ptr<CPicture> background_, EFonts Font = FONT_SMALL, ETextAlignment Align = ETextAlignment::CENTER, const SDL_Color & Color = Colors::WHITE);
|
||||
CGStatusBar(std::shared_ptr<CIntObject> background_, EFonts Font = FONT_SMALL, ETextAlignment Align = ETextAlignment::CENTER, const SDL_Color & Color = Colors::WHITE);
|
||||
CGStatusBar(int x, int y, std::string name, int maxw = -1);
|
||||
|
||||
//make CLabel API private
|
||||
@ -143,15 +141,17 @@ protected:
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
|
||||
public:
|
||||
~CGStatusBar();
|
||||
|
||||
template<typename ...Args>
|
||||
static std::shared_ptr<CGStatusBar> create(Args... args)
|
||||
{
|
||||
std::shared_ptr<CGStatusBar> ret{new CGStatusBar{args...}};
|
||||
ret->init();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void show(SDL_Surface * to) override;
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
// IStatusBar interface
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/ColorFilter.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../adventureMap/CList.h"
|
||||
#include "../adventureMap/CResDataBar.h"
|
||||
|
||||
@ -1198,9 +1198,12 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
|
||||
Rect barRect(9, 182, 732, 18);
|
||||
auto statusbarBackground = std::make_shared<CPicture>(panel->getSurface(), barRect, 9, 555);
|
||||
statusbar = CGStatusBar::create(statusbarBackground);
|
||||
resdatabar = std::make_shared<CResDataBar>("ARESBAR", 3, 575, 32, 2, 85, 85);
|
||||
resdatabar = std::make_shared<CResDataBar>("ARESBAR", 3, 575, 37, 3, 84, 78);
|
||||
|
||||
townlist = std::make_shared<CTownList>(3, Rect(Point(743, 414), Point(48, 128)), Point(1,16), Point(0, 32), LOCPLINT->localState->getOwnedTowns().size() );
|
||||
townlist->setScrollUpButton( std::make_shared<CButton>( Point(744, 414), "IAM014", CButton::tooltipLocalized("core.help.306")));
|
||||
townlist->setScrollDownButton( std::make_shared<CButton>( Point(744, 526), "IAM015", CButton::tooltipLocalized("core.help.307")));
|
||||
|
||||
townlist = std::make_shared<CTownList>(3, Point(744, 414), "IAM014", "IAM015");
|
||||
if(from)
|
||||
townlist->select(from);
|
||||
|
||||
|
@ -39,6 +39,9 @@
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
|
||||
static const std::string OVERVIEW_BACKGROUND = "OvCast.pcx";
|
||||
static const size_t OVERVIEW_SIZE = 4;
|
||||
|
||||
InfoBox::InfoBox(Point position, InfoPos Pos, InfoSize Size, std::shared_ptr<IInfoBoxData> Data):
|
||||
size(Size),
|
||||
infoPos(Pos),
|
||||
@ -468,10 +471,10 @@ void InfoBoxCustom::prepareMessage(std::string & text, std::shared_ptr<CComponen
|
||||
}
|
||||
|
||||
CKingdomInterface::CKingdomInterface()
|
||||
: CWindowObject(PLAYER_COLORED | BORDERED, conf.go()->ac.overviewBg)
|
||||
: CWindowObject(PLAYER_COLORED | BORDERED, OVERVIEW_BACKGROUND)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
ui32 footerPos = conf.go()->ac.overviewSize * 116;
|
||||
ui32 footerPos = OVERVIEW_SIZE * 116;
|
||||
|
||||
tabArea = std::make_shared<CTabbedInt>(std::bind(&CKingdomInterface::createMainTab, this, _1), Point(4,4));
|
||||
|
||||
@ -481,12 +484,12 @@ CKingdomInterface::CKingdomInterface()
|
||||
generateButtons();
|
||||
|
||||
statusbar = CGStatusBar::create(std::make_shared<CPicture>("KSTATBAR", 10,pos.h - 45));
|
||||
resdatabar = std::make_shared<CResDataBar>("KRESBAR", 3, 111+footerPos, 32, 2, 76, 76);
|
||||
resdatabar = std::make_shared<CResDataBar>("KRESBAR", 7, 111+footerPos, 29, 5, 76, 81);
|
||||
}
|
||||
|
||||
void CKingdomInterface::generateObjectsList(const std::vector<const CGObjectInstance * > &ownedObjects)
|
||||
{
|
||||
ui32 footerPos = conf.go()->ac.overviewSize * 116;
|
||||
ui32 footerPos = OVERVIEW_SIZE * 116;
|
||||
size_t dwellSize = (footerPos - 64)/57;
|
||||
|
||||
//Map used to determine image number for several objects
|
||||
@ -550,7 +553,7 @@ std::shared_ptr<CIntObject> CKingdomInterface::createOwnedObject(size_t index)
|
||||
|
||||
std::shared_ptr<CIntObject> CKingdomInterface::createMainTab(size_t index)
|
||||
{
|
||||
size_t size = conf.go()->ac.overviewSize;
|
||||
size_t size = OVERVIEW_SIZE;
|
||||
switch(index)
|
||||
{
|
||||
case 0:
|
||||
@ -564,7 +567,7 @@ std::shared_ptr<CIntObject> CKingdomInterface::createMainTab(size_t index)
|
||||
|
||||
void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstance *> & ownedObjects)
|
||||
{
|
||||
ui32 footerPos = conf.go()->ac.overviewSize * 116;
|
||||
ui32 footerPos = OVERVIEW_SIZE * 116;
|
||||
TResources minesCount(GameConstants::RESOURCE_QUANTITY, 0);
|
||||
int totalIncome=0;
|
||||
|
||||
@ -610,7 +613,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
|
||||
|
||||
void CKingdomInterface::generateButtons()
|
||||
{
|
||||
ui32 footerPos = conf.go()->ac.overviewSize * 116;
|
||||
ui32 footerPos = OVERVIEW_SIZE * 116;
|
||||
|
||||
//Main control buttons
|
||||
btnHeroes = std::make_shared<CButton>(Point(748, 28+footerPos), "OVBUTN1.DEF", CButton::tooltip(CGI->generaltexth->overview[11], CGI->generaltexth->overview[6]),
|
||||
@ -689,7 +692,7 @@ CKingdHeroList::CKingdHeroList(size_t maxSize)
|
||||
skillsLabel = std::make_shared<CLabel>(500, 10, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[1]);
|
||||
|
||||
ui32 townCount = LOCPLINT->cb->howManyHeroes(false);
|
||||
ui32 size = conf.go()->ac.overviewSize*116 + 19;
|
||||
ui32 size = OVERVIEW_SIZE*116 + 19;
|
||||
heroes = std::make_shared<CListBox>(std::bind(&CKingdHeroList::createHeroItem, this, _1),
|
||||
Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size));
|
||||
}
|
||||
@ -705,7 +708,7 @@ void CKingdHeroList::updateGarrisons()
|
||||
|
||||
std::shared_ptr<CIntObject> CKingdHeroList::createHeroItem(size_t index)
|
||||
{
|
||||
ui32 picCount = conf.go()->ac.overviewPics;
|
||||
ui32 picCount = 4; // OVSLOT contains 4 images
|
||||
size_t heroesCount = LOCPLINT->cb->howManyHeroes(false);
|
||||
|
||||
if(index < heroesCount)
|
||||
@ -730,7 +733,7 @@ CKingdTownList::CKingdTownList(size_t maxSize)
|
||||
visitHeroLabel = std::make_shared<CLabel>(608, 10, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[5]);
|
||||
|
||||
ui32 townCount = LOCPLINT->cb->howManyTowns();
|
||||
ui32 size = conf.go()->ac.overviewSize*116 + 19;
|
||||
ui32 size = OVERVIEW_SIZE*116 + 19;
|
||||
towns = std::make_shared<CListBox>(std::bind(&CKingdTownList::createTownItem, this, _1),
|
||||
Point(19,21), Point(0,116), maxSize, townCount, 0, 1, Rect(-19, -21, size, size));
|
||||
}
|
||||
@ -756,7 +759,7 @@ void CKingdTownList::updateGarrisons()
|
||||
|
||||
std::shared_ptr<CIntObject> CKingdTownList::createTownItem(size_t index)
|
||||
{
|
||||
ui32 picCount = conf.go()->ac.overviewPics;
|
||||
ui32 picCount = 4; // OVSLOT contains 4 images
|
||||
size_t townsCount = LOCPLINT->cb->howManyTowns();
|
||||
|
||||
if(index < townsCount)
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../adventureMap/CMinimap.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
|
||||
|
@ -332,7 +332,6 @@ CTradeWindow::CTradeWindow(std::string bgName, const IMarket *Market, const CGHe
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
|
||||
type |= BLOCK_ADV_HOTKEYS;
|
||||
mode = Mode;
|
||||
initTypes();
|
||||
}
|
||||
|
@ -248,9 +248,3 @@ CStatusbarWindow::CStatusbarWindow(int options, std::string imageName, Point cen
|
||||
CStatusbarWindow::CStatusbarWindow(int options, std::string imageName) : CWindowObject(options, imageName)
|
||||
{
|
||||
}
|
||||
|
||||
void CStatusbarWindow::activate()
|
||||
{
|
||||
CIntObject::activate();
|
||||
GH.statusbar = statusbar;
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ class CStatusbarWindow : public CWindowObject
|
||||
public:
|
||||
CStatusbarWindow(int options, std::string imageName, Point centerAt);
|
||||
CStatusbarWindow(int options, std::string imageName = "");
|
||||
void activate() override;
|
||||
protected:
|
||||
std::shared_ptr<CGStatusBar> statusbar;
|
||||
};
|
||||
|
@ -1619,7 +1619,6 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner):
|
||||
owner(_owner)
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
type |= BLOCK_ADV_HOTKEYS;
|
||||
|
||||
SThievesGuildInfo tgi; //info to be displayed
|
||||
LOCPLINT->cb->getThievesGuildInfo(tgi, owner);
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "CWindowObject.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/ResourceSet.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/int3.h"
|
||||
#include "../widgets/CWindowWithArtifacts.h"
|
||||
#include "../widgets/CGarrisonInt.h"
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
#include "../adventureMap/CAdventureMapInterface.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../windows/CMessage.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
@ -119,7 +119,6 @@ CInfoWindow::CInfoWindow(std::string Text, PlayerColor player, const TCompsInfo
|
||||
{
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
|
||||
type |= BLOCK_ADV_HOTKEYS;
|
||||
ID = QueryID(-1);
|
||||
for(auto & Button : Buttons)
|
||||
{
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "CPlayerInterface.h"
|
||||
#include "windows/GUIClasses.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "renderSDL/SDL_Extensions.h"
|
||||
#include "render/IScreenHandler.h"
|
||||
|
||||
|
||||
static void setIntSetting(std::string group, std::string field, int value)
|
||||
@ -37,6 +37,19 @@ static void setBoolSetting(std::string group, std::string field, bool value)
|
||||
entry->Bool() = value;
|
||||
}
|
||||
|
||||
static std::string scalingToEntryString( int scaling)
|
||||
{
|
||||
return std::to_string(scaling) + '%';
|
||||
}
|
||||
|
||||
static std::string scalingToLabelString( int scaling)
|
||||
{
|
||||
std::string string = CGI->generaltexth->translate("vcmi.systemOptions.scalingButton.hover");
|
||||
boost::replace_all(string, "%p", std::to_string(scaling));
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
static std::string resolutionToEntryString( int w, int h)
|
||||
{
|
||||
std::string string = "%wx%h";
|
||||
@ -96,6 +109,10 @@ GeneralOptionsTab::GeneralOptionsTab()
|
||||
{
|
||||
selectGameResolution();
|
||||
});
|
||||
addCallback("setGameScaling", [this](int dummyValue)
|
||||
{
|
||||
selectGameScaling();
|
||||
});
|
||||
addCallback("framerateChanged", [](bool value)
|
||||
{
|
||||
setBoolSetting("video", "showfps", value);
|
||||
@ -114,10 +131,14 @@ GeneralOptionsTab::GeneralOptionsTab()
|
||||
|
||||
build(config);
|
||||
|
||||
const auto & currentResolution = settings["video"]["resolution"];
|
||||
|
||||
std::shared_ptr<CLabel> resolutionLabel = widget<CLabel>("resolutionLabel");
|
||||
const auto & currentResolution = settings["video"]["screenRes"];
|
||||
resolutionLabel->setText(resolutionToLabelString(currentResolution["width"].Integer(), currentResolution["height"].Integer()));
|
||||
|
||||
std::shared_ptr<CLabel> scalingLabel = widget<CLabel>("scalingLabel");
|
||||
scalingLabel->setText(scalingToLabelString(currentResolution["scaling"].Integer()));
|
||||
|
||||
std::shared_ptr<CToggleButton> spellbookAnimationCheckbox = widget<CToggleButton>("spellbookAnimationCheckbox");
|
||||
spellbookAnimationCheckbox->setSelected(settings["video"]["spellbookAnimation"].Bool());
|
||||
|
||||
@ -149,32 +170,25 @@ GeneralOptionsTab::GeneralOptionsTab()
|
||||
std::shared_ptr<CLabel> soundVolumeLabel = widget<CLabel>("soundValueLabel");
|
||||
soundVolumeLabel->setText(std::to_string(CCS->soundh->getVolume()) + "%");
|
||||
|
||||
}
|
||||
#ifdef VCMI_MOBILE
|
||||
// On mobile platforms, VCMI always uses OS screen resolutions
|
||||
// Players can control UI size via "Interface Scaling" option instead
|
||||
std::shared_ptr<CButton> resolutionButton = widget<CButton>("resolutionButton");
|
||||
|
||||
|
||||
bool GeneralOptionsTab::isResolutionSupported(const Point & resolution)
|
||||
{
|
||||
return isResolutionSupported( resolution, settings["video"]["fullscreen"].Bool());
|
||||
}
|
||||
|
||||
bool GeneralOptionsTab::isResolutionSupported(const Point & resolution, bool fullscreen)
|
||||
{
|
||||
if (!fullscreen)
|
||||
return true;
|
||||
|
||||
auto supportedList = CSDL_Ext::getSupportedResolutions();
|
||||
|
||||
return CSDL_Ext::isResolutionSupported(supportedList, resolution);
|
||||
resolutionButton->disable();
|
||||
resolutionLabel->disable();
|
||||
fullscreenCheckbox->block(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GeneralOptionsTab::selectGameResolution()
|
||||
{
|
||||
fillSelectableResolutions();
|
||||
supportedResolutions = GH.screenHandler().getSupportedResolutions();
|
||||
|
||||
std::vector<std::string> items;
|
||||
size_t currentResolutionIndex = 0;
|
||||
size_t i = 0;
|
||||
for(const auto & it : selectableResolutions)
|
||||
for(const auto & it : supportedResolutions)
|
||||
{
|
||||
auto resolutionStr = resolutionToEntryString(it.x, it.y);
|
||||
if(widget<CLabel>("resolutionLabel")->getText() == resolutionToLabelString(it.x, it.y))
|
||||
@ -195,14 +209,14 @@ void GeneralOptionsTab::selectGameResolution()
|
||||
|
||||
void GeneralOptionsTab::setGameResolution(int index)
|
||||
{
|
||||
assert(index >= 0 && index < selectableResolutions.size());
|
||||
assert(index >= 0 && index < supportedResolutions.size());
|
||||
|
||||
if ( index < 0 || index >= selectableResolutions.size() )
|
||||
if ( index < 0 || index >= supportedResolutions.size() )
|
||||
return;
|
||||
|
||||
Point resolution = selectableResolutions[index];
|
||||
Point resolution = supportedResolutions[index];
|
||||
|
||||
Settings gameRes = settings.write["video"]["screenRes"];
|
||||
Settings gameRes = settings.write["video"]["resolution"];
|
||||
gameRes["width"].Float() = resolution.x;
|
||||
gameRes["height"].Float() = resolution.y;
|
||||
|
||||
@ -211,48 +225,57 @@ void GeneralOptionsTab::setGameResolution(int index)
|
||||
|
||||
void GeneralOptionsTab::setFullscreenMode(bool on)
|
||||
{
|
||||
fillSelectableResolutions();
|
||||
|
||||
const auto & screenRes = settings["video"]["screenRes"];
|
||||
const Point desiredResolution(screenRes["width"].Integer(), screenRes["height"].Integer());
|
||||
const Point currentResolution = GH.screenDimensions();
|
||||
|
||||
if (!isResolutionSupported(currentResolution, on))
|
||||
{
|
||||
widget<CToggleButton>("fullscreenCheckbox")->setSelected(!on);
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.systemOptions.fullscreenFailed"));
|
||||
return;
|
||||
}
|
||||
|
||||
setBoolSetting("video", "fullscreen", on);
|
||||
|
||||
if (!isResolutionSupported(desiredResolution, on))
|
||||
{
|
||||
// user changed his desired resolution and switched to fullscreen
|
||||
// however resolution he selected before is not available in fullscreen
|
||||
// so reset it back to currect resolution which is confirmed to be supported earlier
|
||||
Settings gameRes = settings.write["video"]["screenRes"];
|
||||
gameRes["width"].Float() = currentResolution.x;
|
||||
gameRes["height"].Float() = currentResolution.y;
|
||||
|
||||
widget<CLabel>("resolutionLabel")->setText(resolutionToLabelString(currentResolution.x, currentResolution.y));
|
||||
}
|
||||
}
|
||||
|
||||
void GeneralOptionsTab::fillSelectableResolutions()
|
||||
void GeneralOptionsTab::selectGameScaling()
|
||||
{
|
||||
selectableResolutions.clear();
|
||||
supportedScaling.clear();
|
||||
|
||||
for(const auto & it : conf.guiOptions)
|
||||
auto [minimalScaling, maximalScaling] = GH.screenHandler().getSupportedScalingRange();
|
||||
for (int i = 0; i <= maximalScaling; i += 10)
|
||||
{
|
||||
const Point dimensions(it.first.first, it.first.second);
|
||||
|
||||
if(isResolutionSupported(dimensions))
|
||||
selectableResolutions.push_back(dimensions);
|
||||
if (i >= minimalScaling)
|
||||
supportedScaling.push_back(i);
|
||||
}
|
||||
|
||||
boost::range::sort(selectableResolutions, [](const auto & left, const auto & right)
|
||||
std::vector<std::string> items;
|
||||
size_t currentIndex = 0;
|
||||
size_t i = 0;
|
||||
for(const auto & it : supportedScaling)
|
||||
{
|
||||
return left.x * left.y < right.x * right.y;
|
||||
});
|
||||
auto resolutionStr = scalingToEntryString(it);
|
||||
if(widget<CLabel>("scalingLabel")->getText() == scalingToLabelString(it))
|
||||
currentIndex = i;
|
||||
|
||||
items.push_back(std::move(resolutionStr));
|
||||
++i;
|
||||
}
|
||||
|
||||
GH.pushIntT<CObjectListWindow>(
|
||||
items,
|
||||
nullptr,
|
||||
CGI->generaltexth->translate("vcmi.systemOptions.scalingMenu.hover"),
|
||||
CGI->generaltexth->translate("vcmi.systemOptions.scalingMenu.help"),
|
||||
[this](int index)
|
||||
{
|
||||
setGameScaling(index);
|
||||
},
|
||||
currentIndex
|
||||
);
|
||||
}
|
||||
|
||||
void GeneralOptionsTab::setGameScaling(int index)
|
||||
{
|
||||
assert(index >= 0 && index < supportedScaling.size());
|
||||
|
||||
if ( index < 0 || index >= supportedScaling.size() )
|
||||
return;
|
||||
|
||||
int scaling = supportedScaling[index];
|
||||
|
||||
Settings gameRes = settings.write["video"]["resolution"];
|
||||
gameRes["scaling"].Float() = scaling;
|
||||
|
||||
widget<CLabel>("scalingLabel")->setText(scalingToLabelString(scaling));
|
||||
}
|
||||
|
@ -19,16 +19,16 @@ private:
|
||||
SettingsListener onFullscreenChanged;
|
||||
|
||||
std::vector<Point> supportedResolutions;
|
||||
std::vector<Point> selectableResolutions;
|
||||
std::vector<int> supportedScaling;
|
||||
|
||||
void setFullscreenMode( bool on);
|
||||
void fillSelectableResolutions();
|
||||
bool isResolutionSupported(const Point & resolution);
|
||||
bool isResolutionSupported(const Point & resolution, bool fullscreen);
|
||||
|
||||
void selectGameResolution();
|
||||
void setGameResolution(int index);
|
||||
|
||||
void selectGameScaling();
|
||||
void setGameScaling(int index);
|
||||
|
||||
public:
|
||||
GeneralOptionsTab();
|
||||
};
|
||||
};
|
||||
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"GUISettings":
|
||||
[
|
||||
{
|
||||
"resolution": { "x": 800, "y": 600 },
|
||||
"InGameConsole": { "maxInputPerLine": 60, "maxOutputPerLine": 60 },
|
||||
"AdvMap": { "x": 7, "y": 7, "width": 594, "height": 546, "smoothMove": 1, "puzzleSepia": 1, "objectFading" : 1, "screenFading" : 1 },
|
||||
"InfoBox": { "x": 605, "y": 389 },
|
||||
"gem0": { "x": 6, "y": 508, "graphic": "agemLL.def" },
|
||||
"gem1": { "x": 556, "y": 508, "graphic": "agemLR.def" },
|
||||
"gem2": { "x": 6, "y": 6, "graphic": "agemUL.def" },
|
||||
"gem3": { "x": 556, "y": 6, "graphic": "agemUR.def" },
|
||||
"background": "AdvMap.bmp",
|
||||
"backgroundWorldView": "VWorld.bmp",
|
||||
"HeroList": { "size": 5, "x": 609, "y": 196, "movePoints": "IMOBIL.DEF", "manaPoints": "IMANA.DEF", "arrowUp": "IAM012.DEF", "arrowDown": "IAM013.DEF" },
|
||||
"TownList": { "size": 5, "x": 747, "y": 196, "arrowUp": "IAM014.DEF", "arrowDown": "IAM015.DEF" },
|
||||
"Minimap": { "width": 144, "height": 144, "x": 630, "y": 26 },
|
||||
"Overview": { "pics": 4, "size": 4, "graphic": "OvCast.pcx" },
|
||||
"Statusbar": { "x": 7, "y": 556, "graphic": "AdRollvr.bmp" },
|
||||
"ResDataBar": { "x": 3, "y": 575, "graphic": "ARESBAR.bmp", "offsetX": 32, "offsetY": 2, "resSpace": 85, "resDateSpace": 85 },
|
||||
"ButtonKingdomOv": { "x": 679, "y": 196, "graphic": "IAM002.DEF", "playerColoured": 1 },
|
||||
"ButtonUnderground": { "x": 711, "y": 196, "graphic": "IAM010.DEF", "playerColoured": 1, "additionalDefs": [ "IAM003.DEF" ] },
|
||||
"ButtonQuestLog": { "x": 679, "y": 228, "graphic": "IAM004.DEF", "playerColoured": 1 },
|
||||
"ButtonSleepWake": { "x": 711, "y": 228, "graphic": "IAM005.DEF", "playerColoured": 1, "additionalDefs":["IAM011.DEF"] },
|
||||
"ButtonMoveHero": { "x": 679, "y": 260, "graphic": "IAM006.DEF", "playerColoured": 1 },
|
||||
"ButtonSpellbook": { "x": 711, "y": 260, "graphic": "IAM007.DEF", "playerColoured": 1 },
|
||||
"ButtonAdvOptions": { "x": 679, "y": 292, "graphic": "IAM008.DEF", "playerColoured": 1 },
|
||||
"ButtonSysOptions": { "x": 711, "y": 292, "graphic": "IAM009.DEF", "playerColoured": 1 },
|
||||
"ButtonNextHero": { "x": 679, "y": 324, "graphic": "IAM000.DEF", "playerColoured": 1 },
|
||||
"ButtonEndTurn": { "x": 679, "y": 356, "graphic": "IAM001.DEF", "playerColoured": 1 }
|
||||
}
|
||||
]
|
||||
}
|
@ -111,7 +111,7 @@
|
||||
"additionalProperties" : false,
|
||||
"default": {},
|
||||
"required" : [
|
||||
"screenRes",
|
||||
"resolution",
|
||||
"bitsPerPixel",
|
||||
"fullscreen",
|
||||
"realFullscreen",
|
||||
@ -124,15 +124,16 @@
|
||||
"targetfps"
|
||||
],
|
||||
"properties" : {
|
||||
"screenRes" : {
|
||||
"resolution" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "width", "height" ],
|
||||
"required" : [ "width", "height", "scaling" ],
|
||||
"properties" : {
|
||||
"width" : { "type" : "number" },
|
||||
"height" : { "type" : "number" }
|
||||
"height" : { "type" : "number" },
|
||||
"scaling" : { "type" : "number" }
|
||||
},
|
||||
"default": {"width" : 800, "height": 600 }
|
||||
"default": {"width" : 800, "height": 600, "scaling" : 100 }
|
||||
},
|
||||
"bitsPerPixel" : {
|
||||
"type" : "number",
|
||||
|
869
config/widgets/adventureMap.json
Normal file
@ -0,0 +1,869 @@
|
||||
{
|
||||
"options" : {
|
||||
// player-colored images used for background
|
||||
"imagesPlayerColored" : [ "AdvMap.pcx" ],
|
||||
},
|
||||
|
||||
"items":
|
||||
[
|
||||
// Background sections - left side
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundLeftTop",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 0, "top" : 0, "width" : 7, "height" : 52 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundLeftCenter",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 0, "top" : 52, "width" : 7, "bottom" : 91 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundLeftBottom",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 0, "bottom" : 0, "width" : 7, "height" : 91 }
|
||||
},
|
||||
// Background sections - top side
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundTopLeft",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 7, "top" : 0, "width" : 193, "height" : 7 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundTopCenter",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 200, "top" : 0, "right" : 244, "height" : 7 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundTopRight",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "width": 45, "top" : 0, "right" : 199, "height" : 7 }
|
||||
},
|
||||
// Background sections - bottom side
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBottomLeft",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 7, "bottom" : 0, "width" : 44, "height" : 47 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBottomCenter",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "left": 51, "bottom" : 0, "right" : 244, "height" : 47 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBottomRight",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "width": 45, "bottom" : 0, "right" : 199, "height" : 47 }
|
||||
},
|
||||
// Background sections - side panel
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundRightMinimap",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "right": 0, "top" : 0, "width" : 199, "height" : 196 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "backgroundHeroListBorderLeft",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 196, "bottom" : 211, "right" : 191, "width" : 8 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "backgroundTownListBorderRight",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 196, "bottom" : 211, "right" : 0, "width" : 5 }
|
||||
},
|
||||
// Game area
|
||||
{
|
||||
"type": "adventureMapGameArea",
|
||||
"name": "mapView",
|
||||
"area": { "top": 7, "bottom" : 47, "left" : 7, "right" : 199 }
|
||||
},
|
||||
|
||||
// Minimap
|
||||
{
|
||||
"type": "adventureMinimap",
|
||||
"name": "minimap",
|
||||
"area": { "top": 26, "right" : 26, "width" : 144, "height" : 144 }
|
||||
},
|
||||
|
||||
// Adventure map buttons
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "buttonsContainer",
|
||||
"hideWhen" : "worldViewMode",
|
||||
"area": { "top": 196, "right" : 57, "width" : 64, "height" : 192 },
|
||||
"items" : [
|
||||
{
|
||||
"type" : "adventureMapButton",
|
||||
"name" : "buttonKingdomOverview",
|
||||
"image" : "IAM002.DEF",
|
||||
"help" : "core.help.293",
|
||||
"hotkey": "adventureKingdomOverview",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"hideWhen" : "mapLayerSurface",
|
||||
"area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonUnderground",
|
||||
"image" : "IAM010.DEF",
|
||||
"help" : "core.help.294",
|
||||
"hotkey": "adventureToggleMapLevel",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"hideWhen" : "mapLayerUnderground",
|
||||
"area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonSurface",
|
||||
"image" : "IAM003.DEF",
|
||||
"help" : "core.help.294",
|
||||
"hotkey": "adventureToggleMapLevel",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonQuestLog",
|
||||
"image" : "IAM004.DEF",
|
||||
"help" : "core.help.295",
|
||||
"hotkey": "adventureQuestLog",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 32, "left": 0, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"hideWhen" : "heroAwake",
|
||||
"area": { "top" : 32, "left": 32, "width" : 32, "height" : 32 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonSleep",
|
||||
"image" : "IAM005.DEF",
|
||||
"help" : "core.help.296",
|
||||
"hotkey": "adventureSetHeroAsleep",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"hideWhen" : "heroSleeping",
|
||||
"area": { "top" : 32, "left": 32, "width" : 32, "height" : 32 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonWake",
|
||||
"image" : "IAM011.DEF",
|
||||
"help" : "core.help.296",
|
||||
"hotkey": "adventureSetHeroAwake",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonMove",
|
||||
"image" : "IAM006.DEF",
|
||||
"help" : "core.help.297",
|
||||
"hotkey": "adventureMoveHero",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 64, "left": 0, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonCast",
|
||||
"image" : "IAM007.DEF",
|
||||
"help" : "core.help.298",
|
||||
"hotkey": "adventureCastSpell",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 64, "left": 32, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonAdventureOptions",
|
||||
"image" : "IAM008.DEF",
|
||||
"help" : "core.help.299",
|
||||
"hotkey": "adventureGameOptions",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 96, "left": 0, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonSystemOptions",
|
||||
"image" : "IAM009.DEF",
|
||||
"help" : "core.help.300",
|
||||
"hotkey": "globalOptions",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 96, "left": 32, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonNextHero",
|
||||
"image" : "IAM000.DEF",
|
||||
"help" : "core.help.301",
|
||||
"hotkey": "adventureNextHero",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 128, "left": 0, "width" : 64, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "buttonEndTurn",
|
||||
"image" : "IAM001.DEF",
|
||||
"hotkey": "gameEndTurn",
|
||||
"help" : "core.help.302",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 160, "left": 0, "width" : 64, "height" : 32 }
|
||||
}
|
||||
]
|
||||
},
|
||||
// Town / Hero lists for small (600-664) vertical resolution
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "listContainerSmall",
|
||||
"hideWhen" : "worldViewMode",
|
||||
"area": { "top": 196, "right" : 0, "width" : 193, "height" : 196 },
|
||||
"exists" : { "heightMax" : 664 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "backgroundHeroListBorderRight",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "right" : 121, "width" : 6 },
|
||||
"sourceArea": { "top": 196, "bottom" : 211, "right" : 121, "width" : 6 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "backgroundTownListBorderLeft",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "right" : 53, "width" : 4 },
|
||||
"sourceArea": { "top": 196, "bottom" : 211, "right" : 53, "width" : 4 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBelowHeroTownList",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "right": 0, "left" : 0, "bottom" : 0, "height" : 4 },
|
||||
"sourceArea": { "bottom" : 208, "height" : 4, "right" : 0, "width" : 193 }
|
||||
},
|
||||
// Hero List
|
||||
{
|
||||
"type": "adventureMapHeroList",
|
||||
"name" : "heroList",
|
||||
"area": { "top": 0, "right" : 125, "width" : 68, "height" : 193 },
|
||||
"scrollUp" : {
|
||||
"type": "adventureMapButton",
|
||||
"name": "heroListScrollUp",
|
||||
"image" : "IAM012.DEF",
|
||||
"help" : "core.help.303",
|
||||
"area": { "top" : 0, "left": 2, "width" : 64, "height" : 16 }
|
||||
},
|
||||
"scrollDown" : {
|
||||
"type": "adventureMapButton",
|
||||
"name": "heroListScrollDown",
|
||||
"image" : "IAM013.DEF",
|
||||
"help" : "core.help.304",
|
||||
"area": { "bottom" : 0, "left": 2, "width" : 64, "height" : 16 }
|
||||
},
|
||||
"item" : { "top" : 16, "left": 3, "width" : 62, "height" : 32 },
|
||||
"itemsOffset" : { "x" : 0, "y" : 32 },
|
||||
"itemsCount" : 5
|
||||
},
|
||||
// Town List
|
||||
{
|
||||
"type": "adventureMapTownList",
|
||||
"name" : "townList",
|
||||
"area": { "top": 0, "right" : 3, "width" : 51, "height" : 193 },
|
||||
"scrollUp" : {
|
||||
"type": "adventureMapButton",
|
||||
"name": "townListScrollUp",
|
||||
"image" : "IAM014.DEF",
|
||||
"help" : "core.help.306",
|
||||
"area": { "top" : 0, "left": 1, "width" : 48, "height" : 16 }
|
||||
},
|
||||
"scrollDown" : {
|
||||
"type": "adventureMapButton",
|
||||
"name": "townListScrollDown",
|
||||
"image" : "IAM015.DEF",
|
||||
"help" : "core.help.307",
|
||||
"area": { "bottom" : 0, "left": 1, "width" : 48, "height" : 16 }
|
||||
},
|
||||
"item" : { "top" : 16, "left": 2, "width" : 48, "height" : 32 },
|
||||
"itemsOffset" : { "x" : 0, "y" : 32 },
|
||||
"itemsCount" : 5
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "emptyAreaFillSmall",
|
||||
"hideWhen" : "worldViewMode",
|
||||
"area": { "top": 392, "right" : 3, "width" : 190, "bottom" : 211 },
|
||||
"exists" : { "heightMax" : 664 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "emptyAreaFillSmallImage",
|
||||
"image" : "DiBoxBck.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 },
|
||||
"sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 }
|
||||
},
|
||||
]
|
||||
},
|
||||
// Town / Hero lists for large (664+) vertical resolution
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "listContainerLarge",
|
||||
"hideWhen" : "worldViewMode",
|
||||
"area": { "top": 196, "right" : 0, "width" : 193, "height" : 260 },
|
||||
"exists" : { "heightMin" : 664 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "backgroundHeroListBorderRight",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "right" : 121, "width" : 6 },
|
||||
"sourceArea": { "top": 196, "bottom" : 211, "right" : 121, "width" : 6 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "backgroundTownListBorderLeft",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "right" : 53, "width" : 4 },
|
||||
"sourceArea": { "top": 196, "bottom" : 211, "right" : 53, "width" : 4 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBelowHeroTownList",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area" : { "right": 0, "left" : 0, "bottom" : 0, "height" : 4 },
|
||||
"sourceArea": { "bottom" : 208, "height" : 4, "right" : 0, "width" : 193 }
|
||||
},
|
||||
// Hero List
|
||||
{
|
||||
"type": "adventureMapHeroList",
|
||||
"name" : "heroList",
|
||||
"area": { "top": 0, "right" : 125, "width" : 68, "height" : 257 },
|
||||
"item" : { "top" : 1, "left": 3, "width" : 62, "height" : 32 },
|
||||
"itemsOffset" : { "x" : 0, "y" : 32 },
|
||||
"itemsCount" : 8
|
||||
},
|
||||
// Town List
|
||||
{
|
||||
"type": "adventureMapTownList",
|
||||
"name" : "townList",
|
||||
"area": { "top": 0, "right" : 3, "width" : 51, "height" : 257 },
|
||||
"scrollUp" : {
|
||||
"type": "adventureMapButton",
|
||||
"name": "townListScrollUp",
|
||||
"image" : "IAM014.DEF",
|
||||
"help" : "core.help.306",
|
||||
"area": { "top" : 0, "left": 1, "width" : 48, "height" : 16 }
|
||||
},
|
||||
"scrollDown" : {
|
||||
"type": "adventureMapButton",
|
||||
"name": "townListScrollDown",
|
||||
"image" : "IAM015.DEF",
|
||||
"help" : "core.help.307",
|
||||
"area": { "bottom" : 0, "left": 1, "width" : 48, "height" : 16 }
|
||||
},
|
||||
"item" : { "top" : 16, "left": 1, "width" : 48, "height" : 32 },
|
||||
"itemsOffset" : { "x" : 0, "y" : 32 },
|
||||
"itemsCount" : 7
|
||||
},
|
||||
// Fill empty area below buttons
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBelowButtons",
|
||||
"image" : "DiBoxBck.pcx",
|
||||
"area": { "top": 192, "bottom" : 3, "right" : 57, "width" : 64 },
|
||||
"sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 }
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "emptyAreaFillLarge",
|
||||
"hideWhen" : "worldViewMode",
|
||||
"area": { "top": 456, "right" : 3, "width" : 190, "bottom" : 211 },
|
||||
"exists" : { "heightMin" : 664 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "emptyAreaFillLargeImage",
|
||||
"image" : "DiBoxBck.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 },
|
||||
"sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 }
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "adventureInfobarContainer",
|
||||
"hideWhen" : "worldViewMode",
|
||||
"area" : { "bottom": 0, "right" : 0, "width" : 199, "height" : 211 },
|
||||
"items" : [
|
||||
// Infobar
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundRightInfobar",
|
||||
"image" : "AdvMap.pcx",
|
||||
"area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 },
|
||||
"sourceArea" : { "bottom": 0, "right" : 0, "width" : 199, "height" : 211 }
|
||||
},
|
||||
{
|
||||
"type": "adventureInfobar",
|
||||
"name": "infoBar",
|
||||
"area": { "bottom": 44, "right" : 19, "width" : 175, "height" : 168 }
|
||||
}
|
||||
]
|
||||
},
|
||||
// Status bar
|
||||
{
|
||||
"type": "adventureStatusBar",
|
||||
"name": "statusBar",
|
||||
"image" : "DiBoxBck.pcx",
|
||||
"area": { "left": 8, "bottom" : 26, "right" : 199, "height" : 18 }
|
||||
},
|
||||
// Resource & Data bar
|
||||
{
|
||||
"type": "adventurePlayerTexture",
|
||||
"name" : "backgroundLeftOfResourceDateBar",
|
||||
"image" : "DiBoxBck.pcx",
|
||||
"area" : { "left": 3, "bottom" : 4, "right" : 797, "height" : 21 }
|
||||
},
|
||||
{
|
||||
"type": "adventureResourceDateBar",
|
||||
"name": "resourceDataBar",
|
||||
"image" : "AResBar.pcx",
|
||||
"area": { "bottom" : 3, "right" : 3, "height" : 22, "width" : 794 },
|
||||
|
||||
"wood" : { "x" : 37, "y" : 3 },
|
||||
"mercury" : { "x" : 121, "y" : 3 },
|
||||
"ore" : { "x" : 205, "y" : 3 },
|
||||
"sulfur" : { "x" : 289, "y" : 3 },
|
||||
"crystal" : { "x" : 373, "y" : 3 },
|
||||
"gems" : { "x" : 457, "y" : 3 },
|
||||
"gold" : { "x" : 541, "y" : 3 },
|
||||
"date" : { "x" : 619, "y" : 3 }
|
||||
},
|
||||
// World view mode widgets
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "worldViewContainer",
|
||||
"hideWhen" : "mapViewMode",
|
||||
"area": { "top": 195, "right" : 3, "width" : 190, "bottom" : 26 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name": "worldViewBackground",
|
||||
"image" : "VWorld.pcx",
|
||||
"area": { "left" : 0, "right" : 0, "top" : 0, "height" : 381 },
|
||||
"sourceArea": { "left" : 0, "right" : 0, "top" : 0, "bottom" : 0 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewZoom1",
|
||||
"image" : "VWMAG1.DEF",
|
||||
"hotkey": "adventureViewWorld1",
|
||||
"area": { "top" : 23, "left": 1, "width" : 60, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewZoom2",
|
||||
"image" : "VWMAG2.DEF",
|
||||
"hotkey": "adventureViewWorld2",
|
||||
"area": { "top" : 23, "left": 64, "width" : 60, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewZoom4",
|
||||
"image" : "VWMAG4.DEF",
|
||||
"hotkey": "adventureViewWorld4",
|
||||
"area": { "top" : 23, "left": 128, "width" : 60, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewSurface",
|
||||
"image" : "IAM003.DEF",
|
||||
"hotkey": "adventureToggleMapLevel",
|
||||
"playerColored" : true,
|
||||
"area": { "top" : 79, "left": 343, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewPuzzle",
|
||||
"borderColor" : "gold",
|
||||
"image" : "VWPUZ.DEF",
|
||||
"hotkey": "adventureViewPuzzle",
|
||||
"area": { "top" : 343, "left": 5, "width" : 66, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewUnderground",
|
||||
"image" : "IAM010.DEF",
|
||||
"playerColored" : true,
|
||||
"hotkey": "adventureToggleMapLevel",
|
||||
"area": { "top" : 343, "left": 79, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapButton",
|
||||
"name": "worldViewExit",
|
||||
"borderColor" : "gold",
|
||||
"image" : "IOKAY32.DEF",
|
||||
"hotkey": "adventureExitWorldView",
|
||||
"area": { "top" : 343, "left": 119, "width" : 66, "height" : 32 }
|
||||
},
|
||||
// World view - objects icons
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconTown",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 0,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 59, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconHero",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 1,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 79, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconArtifact",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 2,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 99, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconTeleporter",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 3,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 119, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconGate",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 4,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 139, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
|
||||
// World view - mines icons
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineWood",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 5,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 183, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineMercury",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 6,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 203, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineOre",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 7,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 223, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineSulfur",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 8,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 243, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineCrystal",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 9,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 263, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineGems",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 10,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 283, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconMineGold",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 11,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 303, "left": 5, "width" : 32, "height" : 32 }
|
||||
},
|
||||
|
||||
// World view - resources icons
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceWood",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 12,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 183, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceMercury",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 13,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 203, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceOre",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 14,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 223, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceSulfur",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 15,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 243, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceCrystal",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 16,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 263, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceGems",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 17,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 283, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name": "worldViewIconResourceGold",
|
||||
"image" : "VwSymbol.def",
|
||||
"index" : 18,
|
||||
"perPlayer" : 19,
|
||||
"area": { "top" : 303, "left": 154, "width" : 32, "height" : 32 }
|
||||
},
|
||||
|
||||
{
|
||||
"name": "worldViewLabelTitle",
|
||||
"type": "label",
|
||||
"font": "big",
|
||||
"alignment": "center",
|
||||
"color": "yellow",
|
||||
"position": {"x": 94, "y": 11},
|
||||
"text": "core.genrltxt.611"
|
||||
},
|
||||
{
|
||||
"name": "worldViewLabelMine",
|
||||
"type": "label",
|
||||
"font": "calisto",
|
||||
"alignment": "left",
|
||||
"color": "white",
|
||||
"position": {"x": 7, "y": 173},
|
||||
"text": "core.genrltxt.617"
|
||||
},
|
||||
{
|
||||
"name": "worldViewLabelResource",
|
||||
"type": "label",
|
||||
"font": "calisto",
|
||||
"alignment": "right",
|
||||
"color": "white",
|
||||
"position": {"x": 185, "y": 190},
|
||||
"text": "core.genrltxt.618"
|
||||
},
|
||||
{
|
||||
"name": "worldViewLabelsObjects",
|
||||
"type": "labelGroup",
|
||||
"font": "calisto",
|
||||
"alignment": "left",
|
||||
"color": "white",
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"position": {"x": 43, "y": 66},
|
||||
"text": "core.genrltxt.612"
|
||||
},
|
||||
{
|
||||
"position": {"x": 43, "y": 86},
|
||||
"text": "core.genrltxt.613"
|
||||
},
|
||||
{
|
||||
"position": {"x": 43, "y": 106},
|
||||
"text": "core.genrltxt.614"
|
||||
},
|
||||
{
|
||||
"position": {"x": 43, "y": 126},
|
||||
"text": "core.genrltxt.615"
|
||||
},
|
||||
{
|
||||
"position": {"x": 43, "y": 146},
|
||||
"text": "core.genrltxt.616"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "worldViewLabelsResources",
|
||||
"type": "labelGroup",
|
||||
"font": "calisto",
|
||||
"alignment": "center",
|
||||
"color": "white",
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"position": {"x": 101, "y": 198},
|
||||
"text": "core.genrltxt.619"
|
||||
},
|
||||
{
|
||||
"position": {"x": 101, "y": 218},
|
||||
"text": "core.genrltxt.620"
|
||||
},
|
||||
{
|
||||
"position": {"x": 101, "y": 238},
|
||||
"text": "core.genrltxt.621"
|
||||
},
|
||||
{
|
||||
"position": {"x": 101, "y": 258},
|
||||
"text": "core.genrltxt.622"
|
||||
},
|
||||
{
|
||||
"position": {"x": 101, "y": 278},
|
||||
"text": "core.genrltxt.623"
|
||||
},
|
||||
{
|
||||
"position": {"x": 101, "y": 298},
|
||||
"text": "core.genrltxt.624"
|
||||
},
|
||||
{
|
||||
"position": {"x": 101, "y": 318},
|
||||
"text": "core.genrltxt.625"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapImage",
|
||||
"name" : "backgroundBelowWorldView",
|
||||
"image" : "DiBoxBck.pcx",
|
||||
"area": { "top": 381, "bottom" : 0, "left" : 0, "right" : 0 },
|
||||
"sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 }
|
||||
}
|
||||
]
|
||||
},
|
||||
// GEMS - set of images with different image for each player
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "overlayGemTopLeft",
|
||||
"overlay" : true,
|
||||
"area": { "left": 6, "top" : 6, "width" : 46, "height" : 46 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name" : "gemTopLeft",
|
||||
"image" : "agemUL.def",
|
||||
"index" : 0,
|
||||
"perPlayer" : 1,
|
||||
"area" : { "left": 0, "top" : 0, "right" : 0, "bottom" : 0 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "overlayGemTopRight",
|
||||
"overlay" : true,
|
||||
"area": { "right": 198, "top" : 6, "width" : 46, "height" : 46 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name" : "gemTopRight",
|
||||
"image" : "agemUR.def",
|
||||
"index" : 0,
|
||||
"perPlayer" : 1,
|
||||
"area" : { "left": 0, "top" : 0, "right" : 0, "bottom" : 0 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "overlayGemBottomLeft",
|
||||
"overlay" : true,
|
||||
"area": { "left": 6, "bottom" : 46, "width" : 46, "height" : 46 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name" : "gemBottomLeft",
|
||||
"image" : "agemLL.def",
|
||||
"index" : 0,
|
||||
"perPlayer" : 1,
|
||||
"area" : { "left": 0, "top" : 0, "right" : 0, "bottom" : 0 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "adventureMapContainer",
|
||||
"name" : "overlayGemBottomRight",
|
||||
"overlay" : true,
|
||||
"area": { "right": 198, "bottom" : 46, "width" : 46, "height" : 46 },
|
||||
"items" : [
|
||||
{
|
||||
"type": "adventureMapIcon",
|
||||
"name" : "gemBottomRight",
|
||||
"image" : "agemLR.def",
|
||||
"index" : 0,
|
||||
"perPlayer" : 1,
|
||||
"area" : { "left": 0, "top" : 0, "right" : 0, "bottom" : 0 }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -149,10 +149,6 @@
|
||||
"text": "core.genrltxt.406",
|
||||
"position": {"x": 45, "y": 145}
|
||||
},
|
||||
{
|
||||
"text": "core.genrltxt.407",
|
||||
"position": {"x": 45, "y": 175}
|
||||
},
|
||||
{
|
||||
"text": "vcmi.battleOptions.skipBattleIntroMusic.hover",
|
||||
"position": {"x": 45, "y": 175}
|
||||
@ -196,18 +192,12 @@
|
||||
"position": {"x": 10, "y": 143},
|
||||
"callback": "mouseShadowChanged"
|
||||
},
|
||||
{
|
||||
"name": "battleFieldCasualtiesPlaceholder",
|
||||
"type": "picture",
|
||||
"image": "settingsWindow/checkBoxEmpty",
|
||||
"position": {"x": 10, "y": 173},
|
||||
},
|
||||
{
|
||||
"name": "skipBattleIntroMusicCheckbox",
|
||||
"type": "toggleButton",
|
||||
"image": "sysopchk.def",
|
||||
"help": "vcmi.battleOptions.skipBattleIntroMusic",
|
||||
"position": {"x": 10, "y": 203},
|
||||
"position": {"x": 10, "y": 173},
|
||||
"callback": "skipBattleIntroMusicChanged"
|
||||
},
|
||||
{
|
||||
|
@ -58,6 +58,33 @@
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "scalingLabel",
|
||||
"type": "label",
|
||||
"font": "medium",
|
||||
"alignment": "left",
|
||||
"color": "white",
|
||||
"position": {"x": 45, "y": 115},
|
||||
"text": "vcmi.systemOptions.scalingButton.hover"
|
||||
},
|
||||
{
|
||||
"name": "resolutionButton",
|
||||
"type": "button",
|
||||
"position": {"x": 10, "y": 113},
|
||||
"image": "settingsWindow/button32",
|
||||
"help": "vcmi.systemOptions.scalingButton",
|
||||
"callback": "setGameScaling",
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"name": "gearIcon",
|
||||
"type": "picture",
|
||||
"image": "settingsWindow/gear",
|
||||
"position": {"x": 0, "y": 0 }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"name": "topCheckboxesLabels",
|
||||
"type": "labelGroup",
|
||||
@ -67,15 +94,15 @@
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"position": {"x": 45, "y": 115},
|
||||
"position": {"x": 45, "y": 145},
|
||||
"text": "vcmi.systemOptions.fullscreenButton.hover"
|
||||
},
|
||||
{
|
||||
"position": {"x": 45, "y": 145},
|
||||
"position": {"x": 45, "y": 175},
|
||||
"text": "vcmi.systemOptions.framerateButton.hover"
|
||||
},
|
||||
{
|
||||
"position": {"x": 45, "y": 175},
|
||||
"position": {"x": 45, "y": 205},
|
||||
"text": "core.genrltxt.577"
|
||||
},
|
||||
|
||||
@ -86,7 +113,7 @@
|
||||
"type": "toggleButton",
|
||||
"image": "sysopchk.def",
|
||||
"help": "vcmi.systemOptions.fullscreenButton",
|
||||
"position": {"x": 10, "y": 113},
|
||||
"position": {"x": 10, "y": 143},
|
||||
"callback": "fullscreenChanged"
|
||||
},
|
||||
{
|
||||
@ -94,7 +121,7 @@
|
||||
"type": "toggleButton",
|
||||
"image": "sysopchk.def",
|
||||
"help": "vcmi.systemOptions.framerateButton",
|
||||
"position": {"x": 10, "y": 143},
|
||||
"position": {"x": 10, "y": 173},
|
||||
"callback": "framerateChanged"
|
||||
},
|
||||
|
||||
@ -103,7 +130,7 @@
|
||||
"type": "toggleButton",
|
||||
"image": "sysopchk.def",
|
||||
"help": "core.help.364",
|
||||
"position": {"x": 10, "y": 173},
|
||||
"position": {"x": 10, "y": 203},
|
||||
"callback": "spellbookAnimationChanged"
|
||||
},
|
||||
|
||||
|
@ -133,6 +133,10 @@ if(APPLE)
|
||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER vcmilauncher)
|
||||
endif()
|
||||
|
||||
if (NOT APPLE_IOS AND NOT ANDROID)
|
||||
target_link_libraries(vcmilauncher SDL2::SDL2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(vcmilauncher ${VCMI_LIB_TARGET} Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
|
||||
target_include_directories(vcmilauncher
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
|
@ -110,7 +110,6 @@ MainWindow::MainWindow(QWidget * parent)
|
||||
else
|
||||
enterSetup();
|
||||
|
||||
ui->settingsView->isExtraResolutionsModEnabled = ui->modlistView->isExtraResolutionsModEnabled();
|
||||
ui->settingsView->setDisplayList();
|
||||
connect(ui->modlistView, &CModListView::extraResolutionsEnabledChanged,
|
||||
ui->settingsView, &CSettingsView::fillValidResolutions);
|
||||
|
@ -25,6 +25,10 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/VCMIDirs.h"
|
||||
|
||||
#ifndef VCMI_MOBILE
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
QString resolutionToString(const QSize & resolution)
|
||||
@ -67,9 +71,9 @@ void CSettingsView::loadSettings()
|
||||
{
|
||||
ui->comboBoxShowIntro->setCurrentIndex(settings["video"]["showIntro"].Bool());
|
||||
|
||||
#ifdef Q_OS_IOS
|
||||
ui->comboBoxFullScreen->setCurrentIndex(1);
|
||||
ui->comboBoxFullScreen->setDisabled(true);
|
||||
#ifdef VCMI_MOBILE
|
||||
ui->comboBoxFullScreen->hide();
|
||||
ui->labelFullScreen->hide();
|
||||
#else
|
||||
if (settings["video"]["realFullscreen"].Bool())
|
||||
ui->comboBoxFullScreen->setCurrentIndex(2);
|
||||
@ -106,66 +110,74 @@ void CSettingsView::loadSettings()
|
||||
ui->comboBoxCursorType->setCurrentIndex((int)cursorTypeIndex);
|
||||
}
|
||||
|
||||
void CSettingsView::fillValidResolutions(bool isExtraResolutionsModEnabled)
|
||||
void CSettingsView::fillValidResolutions()
|
||||
{
|
||||
this->isExtraResolutionsModEnabled = isExtraResolutionsModEnabled;
|
||||
fillValidResolutionsForScreen(ui->comboBoxDisplayIndex->isVisible() ? ui->comboBoxDisplayIndex->currentIndex() : 0);
|
||||
}
|
||||
|
||||
#ifndef VCMI_MOBILE
|
||||
|
||||
static QVector<QSize> findAvailableResolutions(int displayIndex)
|
||||
{
|
||||
// Ugly workaround since we don't actually need SDL in Launcher
|
||||
// However Qt at the moment provides no way to query list of available resolutions
|
||||
QVector<QSize> result;
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
|
||||
int modesCount = SDL_GetNumDisplayModes(displayIndex);
|
||||
|
||||
for (int i =0; i < modesCount; ++i)
|
||||
{
|
||||
SDL_DisplayMode mode;
|
||||
if (SDL_GetDisplayMode(displayIndex, i, &mode) != 0)
|
||||
continue;
|
||||
|
||||
QSize resolution(mode.w, mode.h);
|
||||
|
||||
result.push_back(resolution);
|
||||
}
|
||||
|
||||
boost::range::sort(result, [](const auto & left, const auto & right)
|
||||
{
|
||||
return left.height() * left.width() < right.height() * right.width();
|
||||
});
|
||||
|
||||
result.erase(boost::unique(result).end(), result.end());
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CSettingsView::fillValidResolutionsForScreen(int screenIndex)
|
||||
{
|
||||
ui->comboBoxResolution->blockSignals(true); // avoid saving wrong resolution after adding first item from the list
|
||||
ui->comboBoxResolution->clear();
|
||||
|
||||
// TODO: read available resolutions from all mods
|
||||
QVariantList resolutions;
|
||||
if(isExtraResolutionsModEnabled)
|
||||
{
|
||||
const auto extrasResolutionsPath = settings["launcher"]["extraResolutionsModPath"].String().c_str();
|
||||
const auto extrasResolutionsJson = JsonUtils::JsonFromFile(CLauncherDirs::get().modsPath() + extrasResolutionsPath);
|
||||
resolutions = extrasResolutionsJson.toMap().value(QLatin1String{"GUISettings"}).toList();
|
||||
}
|
||||
if(resolutions.isEmpty())
|
||||
{
|
||||
ui->comboBoxResolution->blockSignals(false);
|
||||
ui->comboBoxResolution->addItem(resolutionToString({800, 600}));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto screens = qGuiApp->screens();
|
||||
const auto currentScreen = screenIndex < screens.size() ? screens[screenIndex] : qGuiApp->primaryScreen();
|
||||
[[maybe_unused]] const auto screenSize = currentScreen->size();
|
||||
QVector<QSize> resolutions = findAvailableResolutions(screenIndex);
|
||||
|
||||
for(const auto & entry : resolutions)
|
||||
{
|
||||
const auto resolutionMap = entry.toMap().value(QLatin1String{"resolution"}).toMap();
|
||||
if(resolutionMap.isEmpty())
|
||||
continue;
|
||||
ui->comboBoxResolution->addItem(resolutionToString(entry));
|
||||
|
||||
const auto widthValue = resolutionMap[QLatin1String{"x"}];
|
||||
const auto heightValue = resolutionMap[QLatin1String{"y"}];
|
||||
if(!widthValue.isValid() || !heightValue.isValid())
|
||||
continue;
|
||||
|
||||
const QSize resolution{widthValue.toInt(), heightValue.toInt()};
|
||||
#ifndef VCMI_IOS
|
||||
if(screenSize.width() < resolution.width() || screenSize.height() < resolution.height())
|
||||
continue;
|
||||
#endif
|
||||
ui->comboBoxResolution->addItem(resolutionToString(resolution));
|
||||
}
|
||||
|
||||
int resX = settings["video"]["screenRes"]["width"].Integer();
|
||||
int resY = settings["video"]["screenRes"]["height"].Integer();
|
||||
int resX = settings["video"]["resolution"]["width"].Integer();
|
||||
int resY = settings["video"]["resolution"]["height"].Integer();
|
||||
int resIndex = ui->comboBoxResolution->findText(resolutionToString({resX, resY}));
|
||||
ui->comboBoxResolution->setCurrentIndex(resIndex);
|
||||
|
||||
ui->comboBoxResolution->blockSignals(false);
|
||||
|
||||
// if selected resolution no longer exists, force update value to the first resolution
|
||||
// if selected resolution no longer exists, force update value to the largest (last) resolution
|
||||
if(resIndex == -1)
|
||||
ui->comboBoxResolution->setCurrentIndex(0);
|
||||
ui->comboBoxResolution->setCurrentIndex(ui->comboBoxResolution->count() - 1);
|
||||
}
|
||||
#else
|
||||
void CSettingsView::fillValidResolutionsForScreen(int screenIndex)
|
||||
{
|
||||
// resolutions are not selectable on mobile platforms
|
||||
ui->comboBoxResolution->hide();
|
||||
ui->labelResolution->hide();
|
||||
}
|
||||
#endif
|
||||
|
||||
CSettingsView::CSettingsView(QWidget * parent)
|
||||
: QWidget(parent), ui(new Ui::CSettingsView)
|
||||
@ -186,7 +198,7 @@ void CSettingsView::on_comboBoxResolution_currentTextChanged(const QString & arg
|
||||
{
|
||||
QStringList list = arg1.split("x");
|
||||
|
||||
Settings node = settings.write["video"]["screenRes"];
|
||||
Settings node = settings.write["video"]["resolution"];
|
||||
node["width"].Float() = list[0].toInt();
|
||||
node["height"].Float() = list[1].toInt();
|
||||
}
|
||||
|
@ -29,10 +29,8 @@ public:
|
||||
void changeEvent(QEvent *event) override;
|
||||
void showEvent(QShowEvent * event) override;
|
||||
|
||||
bool isExtraResolutionsModEnabled{};
|
||||
|
||||
public slots:
|
||||
void fillValidResolutions(bool isExtraResolutionsModEnabled);
|
||||
void fillValidResolutions();
|
||||
|
||||
private slots:
|
||||
void on_comboBoxResolution_currentTextChanged(const QString & arg1);
|
||||
|
@ -112,9 +112,9 @@
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<y>-107</y>
|
||||
<width>620</width>
|
||||
<height>793</height>
|
||||
<height>745</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="3,0,0,0">
|
||||
@ -253,17 +253,17 @@
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
<string>Windowed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>On</string>
|
||||
<string>Borderless fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Real</string>
|
||||
<string>Exclusive fullscreen</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
|
@ -17,10 +17,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
using namespace config;
|
||||
|
||||
SettingsStorage settings;
|
||||
CConfigHandler conf;
|
||||
|
||||
template<typename Accessor>
|
||||
SettingsStorage::NodeAccessor<Accessor>::NodeAccessor(SettingsStorage & _parent, std::vector<std::string> _path):
|
||||
@ -178,123 +175,6 @@ JsonNode & Settings::operator[](const std::string & value)
|
||||
{
|
||||
return node[value];
|
||||
}
|
||||
//
|
||||
// template DLL_LINKAGE struct SettingsStorage::NodeAccessor<SettingsListener>;
|
||||
// template DLL_LINKAGE struct SettingsStorage::NodeAccessor<Settings>;
|
||||
|
||||
static void setButton(ButtonInfo &button, const JsonNode &g)
|
||||
{
|
||||
button.x = static_cast<int>(g["x"].Float());
|
||||
button.y = static_cast<int>(g["y"].Float());
|
||||
button.playerColoured = g["playerColoured"].Float();
|
||||
button.defName = g["graphic"].String();
|
||||
|
||||
if (!g["additionalDefs"].isNull()) {
|
||||
const JsonVector &defs_vec = g["additionalDefs"].Vector();
|
||||
|
||||
for(const JsonNode &def : defs_vec) {
|
||||
button.additionalDefs.push_back(def.String());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void setGem(AdventureMapConfig &ac, const int gem, const JsonNode &g)
|
||||
{
|
||||
ac.gemX[gem] = static_cast<int>(g["x"].Float());
|
||||
ac.gemY[gem] = static_cast<int>(g["y"].Float());
|
||||
ac.gemG.push_back(g["graphic"].String());
|
||||
}
|
||||
|
||||
CConfigHandler::CConfigHandler()
|
||||
: current(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void config::CConfigHandler::init()
|
||||
{
|
||||
/* Read resolutions. */
|
||||
const JsonNode config(ResourceID("config/resolutions.json"));
|
||||
const JsonVector &guisettings_vec = config["GUISettings"].Vector();
|
||||
|
||||
for(const JsonNode &g : guisettings_vec)
|
||||
{
|
||||
std::pair<int, int> curRes(static_cast<int>(g["resolution"]["x"].Float()), static_cast<int>(g["resolution"]["y"].Float()));
|
||||
GUIOptions *current = &conf.guiOptions[curRes];
|
||||
|
||||
current->ac.inputLineLength = static_cast<int>(g["InGameConsole"]["maxInputPerLine"].Float());
|
||||
current->ac.outputLineLength = static_cast<int>(g["InGameConsole"]["maxOutputPerLine"].Float());
|
||||
|
||||
current->ac.advmapX = static_cast<int>(g["AdvMap"]["x"].Float());
|
||||
current->ac.advmapY = static_cast<int>(g["AdvMap"]["y"].Float());
|
||||
current->ac.advmapW = static_cast<int>(g["AdvMap"]["width"].Float());
|
||||
current->ac.advmapH = static_cast<int>(g["AdvMap"]["height"].Float());
|
||||
current->ac.smoothMove = g["AdvMap"]["smoothMove"].Float();
|
||||
current->ac.puzzleSepia = g["AdvMap"]["puzzleSepia"].Float();
|
||||
current->ac.screenFading = g["AdvMap"]["screenFading"].isNull() ? true : g["AdvMap"]["screenFading"].Float(); // enabled by default
|
||||
current->ac.objectFading = g["AdvMap"]["objectFading"].isNull() ? true : g["AdvMap"]["objectFading"].Float();
|
||||
|
||||
current->ac.infoboxX = static_cast<int>(g["InfoBox"]["x"].Float());
|
||||
current->ac.infoboxY = static_cast<int>(g["InfoBox"]["y"].Float());
|
||||
|
||||
setGem(current->ac, 0, g["gem0"]);
|
||||
setGem(current->ac, 1, g["gem1"]);
|
||||
setGem(current->ac, 2, g["gem2"]);
|
||||
setGem(current->ac, 3, g["gem3"]);
|
||||
|
||||
current->ac.mainGraphic = g["background"].String();
|
||||
current->ac.worldViewGraphic = g["backgroundWorldView"].String();
|
||||
|
||||
current->ac.hlistX = static_cast<int>(g["HeroList"]["x"].Float());
|
||||
current->ac.hlistY = static_cast<int>(g["HeroList"]["y"].Float());
|
||||
current->ac.hlistSize = static_cast<int>(g["HeroList"]["size"].Float());
|
||||
current->ac.hlistMB = g["HeroList"]["movePoints"].String();
|
||||
current->ac.hlistMN = g["HeroList"]["manaPoints"].String();
|
||||
current->ac.hlistAU = g["HeroList"]["arrowUp"].String();
|
||||
current->ac.hlistAD = g["HeroList"]["arrowDown"].String();
|
||||
|
||||
current->ac.tlistX = static_cast<int>(g["TownList"]["x"].Float());
|
||||
current->ac.tlistY = static_cast<int>(g["TownList"]["y"].Float());
|
||||
current->ac.tlistSize = static_cast<int>(g["TownList"]["size"].Float());
|
||||
current->ac.tlistAU = g["TownList"]["arrowUp"].String();
|
||||
current->ac.tlistAD = g["TownList"]["arrowDown"].String();
|
||||
|
||||
current->ac.minimapW = static_cast<int>(g["Minimap"]["width"].Float());
|
||||
current->ac.minimapH = static_cast<int>(g["Minimap"]["height"].Float());
|
||||
current->ac.minimapX = static_cast<int>(g["Minimap"]["x"].Float());
|
||||
current->ac.minimapY = static_cast<int>(g["Minimap"]["y"].Float());
|
||||
|
||||
current->ac.overviewPics = static_cast<int>(g["Overview"]["pics"].Float());
|
||||
current->ac.overviewSize = static_cast<int>(g["Overview"]["size"].Float());
|
||||
current->ac.overviewBg = g["Overview"]["graphic"].String();
|
||||
|
||||
current->ac.statusbarX = static_cast<int>(g["Statusbar"]["x"].Float());
|
||||
current->ac.statusbarY = static_cast<int>(g["Statusbar"]["y"].Float());
|
||||
current->ac.statusbarG = g["Statusbar"]["graphic"].String();
|
||||
|
||||
current->ac.resdatabarX = static_cast<int>(g["ResDataBar"]["x"].Float());
|
||||
current->ac.resdatabarY = static_cast<int>(g["ResDataBar"]["y"].Float());
|
||||
current->ac.resOffsetX = static_cast<int>(g["ResDataBar"]["offsetX"].Float());
|
||||
current->ac.resOffsetY = static_cast<int>(g["ResDataBar"]["offsetY"].Float());
|
||||
current->ac.resDist = static_cast<int>(g["ResDataBar"]["resSpace"].Float());
|
||||
current->ac.resDateDist = static_cast<int>(g["ResDataBar"]["resDateSpace"].Float());
|
||||
current->ac.resdatabarG = g["ResDataBar"]["graphic"].String();
|
||||
|
||||
setButton(current->ac.kingOverview, g["ButtonKingdomOv"]);
|
||||
setButton(current->ac.underground, g["ButtonUnderground"]);
|
||||
setButton(current->ac.questlog, g["ButtonQuestLog"]);
|
||||
setButton(current->ac.sleepWake, g["ButtonSleepWake"]);
|
||||
setButton(current->ac.moveHero, g["ButtonMoveHero"]);
|
||||
setButton(current->ac.spellbook, g["ButtonSpellbook"]);
|
||||
setButton(current->ac.advOptions, g["ButtonAdvOptions"]);
|
||||
setButton(current->ac.sysOptions, g["ButtonSysOptions"]);
|
||||
setButton(current->ac.nextHero, g["ButtonNextHero"]);
|
||||
setButton(current->ac.endTurn, g["ButtonEndTurn"]);
|
||||
}
|
||||
|
||||
const JsonNode& screenRes = settings["video"]["screenRes"];
|
||||
|
||||
SetResolution(static_cast<int>(screenRes["width"].Float()), static_cast<int>(screenRes["height"].Float()));
|
||||
}
|
||||
|
||||
// Force instantiation of the SettingsStorage::NodeAccessor class template.
|
||||
// That way method definitions can sit in the cpp file
|
||||
|
@ -112,83 +112,6 @@ public:
|
||||
friend class SettingsStorage;
|
||||
};
|
||||
|
||||
namespace config
|
||||
{
|
||||
struct DLL_LINKAGE ButtonInfo
|
||||
{
|
||||
std::string defName;
|
||||
std::vector<std::string> additionalDefs;
|
||||
int x, y; //position on the screen
|
||||
bool playerColoured; //if true button will be colored to main player's color (works properly only for appropriate 8bpp graphics)
|
||||
};
|
||||
/// Struct which holds data about position of several GUI elements at the adventure map screen
|
||||
struct DLL_LINKAGE AdventureMapConfig
|
||||
{
|
||||
//minimap properties
|
||||
int minimapX, minimapY, minimapW, minimapH;
|
||||
//statusbar
|
||||
int statusbarX, statusbarY; //pos
|
||||
std::string statusbarG; //graphic name
|
||||
//resdatabar
|
||||
int resdatabarX, resdatabarY, resDist, resDateDist, resOffsetX, resOffsetY; //pos
|
||||
std::string resdatabarG; //graphic name
|
||||
//infobox
|
||||
int infoboxX, infoboxY;
|
||||
//advmap
|
||||
int advmapX, advmapY, advmapW, advmapH;
|
||||
bool smoothMove;
|
||||
bool puzzleSepia;
|
||||
bool screenFading;
|
||||
bool objectFading;
|
||||
//general properties
|
||||
std::string mainGraphic;
|
||||
std::string worldViewGraphic;
|
||||
//buttons
|
||||
ButtonInfo kingOverview, underground, questlog, sleepWake, moveHero, spellbook, advOptions,
|
||||
sysOptions, nextHero, endTurn;
|
||||
//hero list
|
||||
int hlistX, hlistY, hlistSize;
|
||||
std::string hlistMB, hlistMN, hlistAU, hlistAD;
|
||||
//town list
|
||||
int tlistX, tlistY, tlistSize;
|
||||
std::string tlistAU, tlistAD;
|
||||
//gems
|
||||
int gemX[4], gemY[4];
|
||||
std::vector<std::string> gemG;
|
||||
//in-game console
|
||||
int inputLineLength, outputLineLength;
|
||||
//kingdom overview
|
||||
int overviewPics, overviewSize; //pic count in def and count of visible slots
|
||||
std::string overviewBg; //background name
|
||||
};
|
||||
struct DLL_LINKAGE GUIOptions
|
||||
{
|
||||
AdventureMapConfig ac;
|
||||
};
|
||||
/// Handles adventure map screen settings
|
||||
class DLL_LINKAGE CConfigHandler
|
||||
{
|
||||
GUIOptions *current; // pointer to current gui options
|
||||
|
||||
public:
|
||||
using GuiOptionsMap = std::map<std::pair<int, int>, GUIOptions>;
|
||||
GuiOptionsMap guiOptions;
|
||||
void init();
|
||||
CConfigHandler();
|
||||
|
||||
GUIOptions *go() { return current; };
|
||||
void SetResolution(int x, int y)
|
||||
{
|
||||
std::pair<int,int> index(x, y);
|
||||
if (guiOptions.count(index) == 0)
|
||||
current = nullptr;
|
||||
else
|
||||
current = &guiOptions.at(index);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
extern DLL_LINKAGE SettingsStorage settings;
|
||||
extern DLL_LINKAGE config::CConfigHandler conf;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -191,9 +191,6 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
QApplication::quit();
|
||||
}
|
||||
|
||||
conf.init();
|
||||
logGlobal->info("Loading settings");
|
||||
|
||||
loadTranslation();
|
||||
|
||||
ui->setupUi(this);
|
||||
|