1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-19 21:10:12 +02:00
vcmi/client/lobby/OptionsTabBase.cpp
Ivan Savenko cca4c0888c In-memory assets generation
All assets generation (large spellbook, terrain animations, etc) are now
done in memory and used as it, without saving to disk.

This should slightly improve load times since there is no encode png /
decode png, and should help with avoiding strange bug when vcmi fails to
load recently saved assets.

If needed, such assets can be force-dumped on disk using already
existing console command
2025-01-30 22:21:38 +00:00

433 lines
14 KiB
C++

/*
* OptionsTabBase.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 "OptionsTabBase.h"
#include "CSelectionBase.h"
#include "../widgets/ComboBox.h"
#include "../widgets/CTextInput.h"
#include "../widgets/Images.h"
#include "../widgets/Slider.h"
#include "../widgets/TextControls.h"
#include "../CServerHandler.h"
#include "../CGameInfo.h"
#include "../../lib/StartInfo.h"
#include "../../lib/texts/CGeneralTextHandler.h"
#include "../../lib/texts/Languages.h"
#include "../../lib/texts/MetaString.h"
#include "../../lib/CConfigHandler.h"
static std::string timeToString(int time)
{
std::stringstream ss;
ss << time / 1000 / 60 << ":" << std::setw(2) << std::setfill('0') << time / 1000 % 60;
return ss.str();
};
std::vector<TurnTimerInfo> OptionsTabBase::getTimerPresets() const
{
std::vector<TurnTimerInfo> result;
for (auto const & tpreset : variables["timerPresets"].Vector())
{
TurnTimerInfo tinfo;
tinfo.baseTimer = tpreset[0].Integer() * 1000;
tinfo.turnTimer = tpreset[1].Integer() * 1000;
tinfo.battleTimer = tpreset[2].Integer() * 1000;
tinfo.unitTimer = tpreset[3].Integer() * 1000;
tinfo.accumulatingTurnTimer = tpreset[4].Bool();
tinfo.accumulatingUnitTimer = tpreset[5].Bool();
result.push_back(tinfo);
}
return result;
}
std::vector<SimturnsInfo> OptionsTabBase::getSimturnsPresets() const
{
std::vector<SimturnsInfo> result;
for (auto const & tpreset : variables["simturnsPresets"].Vector())
{
SimturnsInfo tinfo;
tinfo.optionalTurns = tpreset[0].Integer();
tinfo.requiredTurns = tpreset[1].Integer();
tinfo.allowHumanWithAI = tpreset[2].Bool();
result.push_back(tinfo);
}
return result;
}
OptionsTabBase::OptionsTabBase(const JsonPath & configPath)
{
recActions = 0;
auto setTimerPresetCallback = [this](int index){
CSH->setTurnTimerInfo(getTimerPresets().at(index));
};
auto setSimturnsPresetCallback = [this](int index){
CSH->setSimturnsInfo(getSimturnsPresets().at(index));
};
addCallback("setTimerPreset", setTimerPresetCallback);
addCallback("setSimturnPreset", setSimturnsPresetCallback);
addCallback("setSimturnDurationMin", [&](int index){
SimturnsInfo info = SEL->getStartInfo()->simturnsInfo;
info.requiredTurns = index;
info.optionalTurns = std::max(info.optionalTurns, index);
CSH->setSimturnsInfo(info);
});
addCallback("setSimturnDurationMax", [&](int index){
SimturnsInfo info = SEL->getStartInfo()->simturnsInfo;
info.optionalTurns = index;
info.requiredTurns = std::min(info.requiredTurns, index);
CSH->setSimturnsInfo(info);
});
addCallback("setSimturnAI", [&](int index){
SimturnsInfo info = SEL->getStartInfo()->simturnsInfo;
info.allowHumanWithAI = index;
CSH->setSimturnsInfo(info);
});
addCallback("setCheatAllowed", [&](int index){
bool isMultiplayer = CSH->loadMode == ELoadMode::MULTI;
Settings entry = persistentStorage.write["startExtraOptions"][isMultiplayer ? "multiPlayer" : "singlePlayer"][isMultiplayer ? "cheatsAllowed" : "cheatsNotAllowed"];
entry->Bool() = isMultiplayer ? index : !index;
ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo;
info.cheatsAllowed = index;
CSH->setExtraOptionsInfo(info);
});
addCallback("setUnlimitedReplay", [&](int index){
bool isMultiplayer = CSH->loadMode == ELoadMode::MULTI;
Settings entry = persistentStorage.write["startExtraOptions"][isMultiplayer ? "multiPlayer" : "singlePlayer"]["unlimitedReplay"];
entry->Bool() = index;
ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo;
info.unlimitedReplay = index;
CSH->setExtraOptionsInfo(info);
});
addCallback("setTurnTimerAccumulate", [&](int index){
TurnTimerInfo info = SEL->getStartInfo()->turnTimerInfo;
info.accumulatingTurnTimer = index;
CSH->setTurnTimerInfo(info);
});
addCallback("setUnitTimerAccumulate", [&](int index){
TurnTimerInfo info = SEL->getStartInfo()->turnTimerInfo;
info.accumulatingUnitTimer = index;
CSH->setTurnTimerInfo(info);
});
//helper function to parse string containing time to integer reflecting time in seconds
//assumed that input string can be modified by user, function shall support user's intention
// normal: 2:00, 12:30
// adding symbol: 2:005 -> 2:05, 2:305 -> 23:05,
// adding symbol (>60 seconds): 12:095 -> 129:05
// removing symbol: 129:0 -> 12:09, 2:0 -> 0:20, 0:2 -> 0:02
auto parseTimerString = [](const std::string & str) -> int
{
auto sc = str.find(":");
if(sc == std::string::npos)
return str.empty() ? 0 : std::stoi(str);
auto l = str.substr(0, sc);
auto r = str.substr(sc + 1, std::string::npos);
if(r.length() == 3) //symbol added
{
l.push_back(r.front());
r.erase(r.begin());
}
else if(r.length() == 1) //symbol removed
{
r.insert(r.begin(), l.back());
l.pop_back();
}
else if(r.empty())
r = "0";
int sec = std::stoi(r);
if(sec >= 60)
{
if(l.empty()) //9:00 -> 0:09
return sec / 10;
l.push_back(r.front()); //0:090 -> 9:00
r.erase(r.begin());
}
else if(l.empty())
return sec;
return std::min(24*60, std::stoi(l)) * 60 + std::stoi(r);
};
addCallback("parseAndSetTimer_base", [this, parseTimerString](const std::string & str){
int time = parseTimerString(str) * 1000;
if(time >= 0)
{
TurnTimerInfo tinfo = SEL->getStartInfo()->turnTimerInfo;
tinfo.baseTimer = time;
CSH->setTurnTimerInfo(tinfo);
if(auto ww = widget<CTextInput>("chessFieldBase"))
ww->setText(timeToString(time));
}
});
addCallback("parseAndSetTimer_turn", [this, parseTimerString](const std::string & str){
int time = parseTimerString(str) * 1000;
if(time >= 0)
{
TurnTimerInfo tinfo = SEL->getStartInfo()->turnTimerInfo;
tinfo.turnTimer = time;
CSH->setTurnTimerInfo(tinfo);
if(auto ww = widget<CTextInput>("chessFieldTurn"))
ww->setText(timeToString(time));
}
});
addCallback("parseAndSetTimer_battle", [this, parseTimerString](const std::string & str){
int time = parseTimerString(str) * 1000;
if(time >= 0)
{
TurnTimerInfo tinfo = SEL->getStartInfo()->turnTimerInfo;
tinfo.battleTimer = time;
CSH->setTurnTimerInfo(tinfo);
if(auto ww = widget<CTextInput>("chessFieldBattle"))
ww->setText(timeToString(time));
}
});
addCallback("parseAndSetTimer_unit", [this, parseTimerString](const std::string & str){
int time = parseTimerString(str) * 1000;
if(time >= 0)
{
TurnTimerInfo tinfo = SEL->getStartInfo()->turnTimerInfo;
tinfo.unitTimer = time;
CSH->setTurnTimerInfo(tinfo);
if(auto ww = widget<CTextInput>("chessFieldUnit"))
ww->setText(timeToString(time));
}
});
const JsonNode config(configPath);
build(config);
//set timers combo box callbacks
if(auto w = widget<ComboBox>("timerModeSwitch"))
{
w->onConstructItems = [&](std::vector<const void *> & curItems){
if(variables["timers"].isNull())
return;
for(auto & p : variables["timers"].Vector())
{
curItems.push_back(&p);
}
};
w->onSetItem = [&](const void * item){
if(item)
{
if(auto * tObj = reinterpret_cast<const JsonNode *>(item))
{
for(auto wname : (*tObj)["hideWidgets"].Vector())
{
if(auto w = widget<CIntObject>(wname.String()))
w->setEnabled(false);
}
for(auto wname : (*tObj)["showWidgets"].Vector())
{
if(auto w = widget<CIntObject>(wname.String()))
w->setEnabled(true);
}
if((*tObj)["default"].isVector())
{
TurnTimerInfo tinfo;
tinfo.baseTimer = (*tObj)["default"].Vector().at(0).Integer() * 1000;
tinfo.turnTimer = (*tObj)["default"].Vector().at(1).Integer() * 1000;
tinfo.battleTimer = (*tObj)["default"].Vector().at(2).Integer() * 1000;
tinfo.unitTimer = (*tObj)["default"].Vector().at(3).Integer() * 1000;
CSH->setTurnTimerInfo(tinfo);
}
}
redraw();
}
};
w->getItemText = [this](int idx, const void * item){
if(item)
{
if(auto * tObj = reinterpret_cast<const JsonNode *>(item))
return readText((*tObj)["text"]);
}
return std::string("");
};
w->setItem(0);
}
if(auto w = widget<ComboBox>("simturnsPresetSelector"))
{
w->onConstructItems = [this](std::vector<const void *> & curItems)
{
for (size_t i = 0; i < variables["simturnsPresets"].Vector().size(); ++i)
curItems.push_back((void*)i);
};
w->onSetItem = [setSimturnsPresetCallback](const void * item){
size_t itemIndex = (size_t)item;
setSimturnsPresetCallback(itemIndex);
};
}
if(auto w = widget<ComboBox>("timerPresetSelector"))
{
w->onConstructItems = [this](std::vector<const void *> & curItems)
{
for (size_t i = 0; i < variables["timerPresets"].Vector().size(); ++i)
curItems.push_back((void*)i);
};
w->onSetItem = [setTimerPresetCallback](const void * item){
size_t itemIndex = (size_t)item;
setTimerPresetCallback(itemIndex);
};
}
}
void OptionsTabBase::recreate(bool campaign)
{
auto const & generateSimturnsDurationText = [](int days) -> std::string
{
if (days == 0)
return CGI->generaltexth->translate("core.genrltxt.523");
if (days >= 1000000) // Not "unlimited" but close enough
return CGI->generaltexth->translate("core.turndur.10");
bool canUseMonth = days % 28 == 0 && days >= 28*2;
bool canUseWeek = days % 7 == 0 && days >= 7*2;
int value = days;
std::string text = "vcmi.optionsTab.simturns.days";
if (canUseWeek && !canUseMonth)
{
value = days / 7;
text = "vcmi.optionsTab.simturns.weeks";
}
if (canUseMonth)
{
value = days / 28;
text = "vcmi.optionsTab.simturns.months";
}
MetaString message;
message.appendTextID(Languages::getPluralFormTextID( CGI->generaltexth->getPreferredLanguage(), value, text));
message.replaceNumber(value);
return message.toString();
};
//Simultaneous turns
if(auto turnSlider = widget<CSlider>("simturnsDurationMin"))
turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.requiredTurns, false);
if(auto turnSlider = widget<CSlider>("simturnsDurationMax"))
turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.optionalTurns, false);
if(auto w = widget<CLabel>("labelSimturnsDurationValueMin"))
w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.requiredTurns));
if(auto w = widget<CLabel>("labelSimturnsDurationValueMax"))
w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.optionalTurns));
if(auto buttonSimturnsAI = widget<CToggleButton>("buttonSimturnsAI"))
buttonSimturnsAI->setSelectedSilent(SEL->getStartInfo()->simturnsInfo.allowHumanWithAI);
if(auto buttonTurnTimerAccumulate = widget<CToggleButton>("buttonTurnTimerAccumulate"))
buttonTurnTimerAccumulate->setSelectedSilent(SEL->getStartInfo()->turnTimerInfo.accumulatingTurnTimer);
if(auto chessFieldTurnLabel = widget<CLabel>("chessFieldTurnLabel"))
{
if (SEL->getStartInfo()->turnTimerInfo.accumulatingTurnTimer)
chessFieldTurnLabel->setText(CGI->generaltexth->translate("vcmi.optionsTab.chessFieldTurnAccumulate.help"));
else
chessFieldTurnLabel->setText(CGI->generaltexth->translate("vcmi.optionsTab.chessFieldTurnDiscard.help"));
}
if(auto chessFieldUnitLabel = widget<CLabel>("chessFieldUnitLabel"))
{
if (SEL->getStartInfo()->turnTimerInfo.accumulatingUnitTimer)
chessFieldUnitLabel->setText(CGI->generaltexth->translate("vcmi.optionsTab.chessFieldUnitAccumulate.help"));
else
chessFieldUnitLabel->setText(CGI->generaltexth->translate("vcmi.optionsTab.chessFieldUnitDiscard.help"));
}
if(auto buttonUnitTimerAccumulate = widget<CToggleButton>("buttonUnitTimerAccumulate"))
buttonUnitTimerAccumulate->setSelectedSilent(SEL->getStartInfo()->turnTimerInfo.accumulatingUnitTimer);
const auto & turnTimerRemote = SEL->getStartInfo()->turnTimerInfo;
//classic timer
if(auto turnSlider = widget<CSlider>("sliderTurnDuration"))
{
if(!variables["timerPresets"].isNull() && !turnTimerRemote.battleTimer && !turnTimerRemote.unitTimer && !turnTimerRemote.baseTimer)
{
for(int idx = 0; idx < variables["timerPresets"].Vector().size(); ++idx)
{
auto & tpreset = variables["timerPresets"].Vector()[idx];
if(tpreset.Vector().at(1).Integer() == turnTimerRemote.turnTimer / 1000)
{
turnSlider->scrollTo(idx, false);
if(auto w = widget<CLabel>("labelTurnDurationValue"))
w->setText(CGI->generaltexth->turnDurations[idx]);
}
}
}
}
if(auto ww = widget<CTextInput>("chessFieldBase"))
ww->setText(timeToString(turnTimerRemote.baseTimer));
if(auto ww = widget<CTextInput>("chessFieldTurn"))
ww->setText(timeToString(turnTimerRemote.turnTimer));
if(auto ww = widget<CTextInput>("chessFieldBattle"))
ww->setText(timeToString(turnTimerRemote.battleTimer));
if(auto ww = widget<CTextInput>("chessFieldUnit"))
ww->setText(timeToString(turnTimerRemote.unitTimer));
if(auto w = widget<ComboBox>("timerModeSwitch"))
{
if(turnTimerRemote.battleTimer || turnTimerRemote.unitTimer || turnTimerRemote.baseTimer)
{
if(auto turnSlider = widget<CSlider>("sliderTurnDuration"))
if(turnSlider->isActive())
w->setItem(1);
}
}
if(auto buttonCheatAllowed = widget<CToggleButton>("buttonCheatAllowed"))
{
buttonCheatAllowed->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.cheatsAllowed);
buttonCheatAllowed->block(CSH->isGuest());
}
if(auto buttonUnlimitedReplay = widget<CToggleButton>("buttonUnlimitedReplay"))
{
buttonUnlimitedReplay->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.unlimitedReplay);
buttonUnlimitedReplay->block(CSH->isGuest());
}
if(auto textureCampaignOverdraw = widget<CFilledTexture>("textureCampaignOverdraw"))
textureCampaignOverdraw->setEnabled(campaign);
}