1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Merge remote-tracking branch 'origin/beta' into fix_pandora_amounts

This commit is contained in:
Tomasz Zieliński 2023-04-07 21:26:36 +02:00
commit 37f1bf9d78
24 changed files with 393 additions and 159 deletions

View File

@ -89,6 +89,7 @@ void BattleWindow::createQueue()
//create stack queue and adjust our own position
bool embedQueue;
bool showQueue = settings["battle"]["showQueue"].Bool();
std::string queueSize = settings["battle"]["queueSize"].String();
if(queueSize == "auto")
@ -97,13 +98,16 @@ void BattleWindow::createQueue()
embedQueue = GH.screenDimensions().y < 700 || queueSize == "small";
queue = std::make_shared<StackQueue>(embedQueue, owner);
if(!embedQueue && settings["battle"]["showQueue"].Bool())
if(!embedQueue && showQueue)
{
//re-center, taking into account stack queue position
pos.y -= queue->pos.h;
pos.h += queue->pos.h;
pos = center();
}
if (!showQueue)
queue->disable();
}
BattleWindow::~BattleWindow()
@ -143,8 +147,8 @@ void BattleWindow::hideQueue()
pos.y += queue->pos.h;
pos.h -= queue->pos.h;
pos = center();
GH.totalRedraw();
}
GH.totalRedraw();
}
void BattleWindow::showQueue()
@ -213,9 +217,12 @@ void BattleWindow::tacticPhaseStarted()
auto menuTactics = widget<CIntObject>("menuTactics");
auto tacticNext = widget<CIntObject>("tacticNext");
auto tacticEnd = widget<CIntObject>("tacticEnd");
auto alternativeAction = widget<CIntObject>("alternativeAction");
menuBattle->disable();
console->disable();
if (alternativeAction)
alternativeAction->disable();
menuTactics->enable();
tacticNext->enable();
@ -231,9 +238,12 @@ void BattleWindow::tacticPhaseEnded()
auto menuTactics = widget<CIntObject>("menuTactics");
auto tacticNext = widget<CIntObject>("tacticNext");
auto tacticEnd = widget<CIntObject>("tacticEnd");
auto alternativeAction = widget<CIntObject>("alternativeAction");
menuBattle->enable();
console->enable();
if (alternativeAction)
alternativeAction->enable();
menuTactics->disable();
tacticNext->disable();

View File

@ -372,7 +372,15 @@ std::shared_ptr<CSlider> InterfaceObjectConfigurable::buildSlider(const JsonNode
auto itemsTotal = config["itemsTotal"].Integer();
auto value = config["selected"].Integer();
bool horizontal = config["orientation"].String() == "horizontal";
return std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
auto const & result = std::make_shared<CSlider>(position, length, callbacks.at(config["callback"].String()), itemsVisible, itemsTotal, value, horizontal, style);
if (!config["scrollBounds"].isNull())
{
Rect bounds = readRect(config["scrollBounds"]);
result->setScrollBounds(bounds);
}
return result;
}
std::shared_ptr<CAnimImage> InterfaceObjectConfigurable::buildImage(const JsonNode & config) const

View File

@ -13,7 +13,6 @@
#include "../../lib/Point.h"
#include "../../lib/TextOperations.h"
//
size_t IFont::getStringWidth(const std::string & data) const
{

View File

@ -70,7 +70,7 @@ CBitmapFont::CBitmapFont(const std::string & filename):
loadModFont("core", resource);
for (auto const & modName : VLC->modh->getActiveMods())
for(const auto & modName : VLC->modh->getActiveMods())
{
if (CResourceHandler::get(modName)->existsResource(resource))
loadModFont(modName, resource);
@ -94,6 +94,24 @@ size_t CBitmapFont::getGlyphWidth(const char * data) const
return iter->second.leftOffset + iter->second.width + iter->second.rightOffset;
}
bool CBitmapFont::canRepresentCharacter(const char *data) const
{
CodePoint localChar = TextOperations::getUnicodeCodepoint(data, 4);
auto iter = chars.find(localChar);
return iter != chars.end();
}
bool CBitmapFont::canRepresentString(const std::string & data) const
{
for(size_t i=0; i<data.size(); i += TextOperations::getUnicodeCharacterSize(data[i]))
if (!canRepresentCharacter(data.data() + i))
return false;
return true;
}
void CBitmapFont::renderCharacter(SDL_Surface * surface, const BitmapChar & character, const SDL_Color & color, int &posX, int &posY) const
{
Rect clipRect;

View File

@ -41,6 +41,11 @@ public:
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
/// returns true if this font contains provided utf-8 character
bool canRepresentCharacter(const char * data) const;
bool canRepresentString(const std::string & data) const;
friend class CBitmapHanFont;
friend class CTrueTypeFont;
};

View File

@ -10,6 +10,8 @@
#include "StdInc.h"
#include "CTrueTypeFont.h"
#include "CBitmapFont.h"
#include "../render/Colors.h"
#include "../renderSDL/SDL_Extensions.h"
@ -52,30 +54,45 @@ int CTrueTypeFont::getFontStyle(const JsonNode &config)
CTrueTypeFont::CTrueTypeFont(const JsonNode & fontConfig):
data(loadData(fontConfig)),
font(loadFont(fontConfig), TTF_CloseFont),
dropShadow(fontConfig["blend"].Bool()),
blended(fontConfig["blend"].Bool())
{
assert(font);
TTF_SetFontStyle(font.get(), getFontStyle(fontConfig));
std::string fallbackName = fontConfig["fallback"].String();
if (!fallbackName.empty())
fallbackFont = std::make_unique<CBitmapFont>(fallbackName);
}
CTrueTypeFont::~CTrueTypeFont() = default;
size_t CTrueTypeFont::getLineHeight() const
{
if (fallbackFont)
fallbackFont->getLineHeight();
return TTF_FontHeight(font.get());
}
size_t CTrueTypeFont::getGlyphWidth(const char *data) const
{
if (fallbackFont && fallbackFont->canRepresentCharacter(data))
return fallbackFont->getGlyphWidth(data);
return getStringWidth(std::string(data, TextOperations::getUnicodeCharacterSize(*data)));
/*
int advance;
TTF_GlyphMetrics(font.get(), *data, nullptr, nullptr, nullptr, nullptr, &advance);
return advance;
*/
}
size_t CTrueTypeFont::getStringWidth(const std::string & data) const
{
if (fallbackFont && fallbackFont->canRepresentString(data))
return fallbackFont->getStringWidth(data);
int width;
TTF_SizeUTF8(font.get(), data.c_str(), &width, nullptr);
return width;
@ -83,7 +100,13 @@ size_t CTrueTypeFont::getStringWidth(const std::string & data) const
void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
if (color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow
if (fallbackFont && fallbackFont->canRepresentString(data))
{
fallbackFont->renderText(surface, data, color, pos);
return;
}
if (dropShadow && color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow
renderText(surface, data, Colors::BLACK, pos + Point(1,1));
if (!data.empty())

View File

@ -15,14 +15,18 @@ VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
VCMI_LIB_NAMESPACE_END
class CBitmapFont;
typedef struct _TTF_Font TTF_Font;
class CTrueTypeFont : public IFont
{
std::unique_ptr<CBitmapFont> fallbackFont;
const std::pair<std::unique_ptr<ui8[]>, ui64> data;
const std::unique_ptr<TTF_Font, void (*)(TTF_Font*)> font;
const bool blended;
const bool dropShadow;
std::pair<std::unique_ptr<ui8[]>, ui64> loadData(const JsonNode & config);
TTF_Font * loadFont(const JsonNode & config);
@ -31,6 +35,7 @@ class CTrueTypeFont : public IFont
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CTrueTypeFont(const JsonNode & fontConfig);
~CTrueTypeFont();
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;

View File

@ -575,7 +575,7 @@ void CSlider::mouseMoved (const Point & cursorPosition)
v += 0.5;
if(v!=value)
{
moveTo((int)v);
moveTo(static_cast<int>(v));
}
}
@ -584,6 +584,16 @@ void CSlider::setScrollStep(int to)
scrollStep = to;
}
void CSlider::setScrollBounds(const Rect & bounds )
{
scrollBounds = bounds;
}
void CSlider::clearScrollBounds()
{
scrollBounds = boost::none;
}
int CSlider::getAmount() const
{
return amount;
@ -775,7 +785,19 @@ void CSlider::showAll(SDL_Surface * to)
void CSlider::wheelScrolled(bool down, bool in)
{
moveTo(value + 3 * (down ? +scrollStep : -scrollStep));
if (scrollBounds)
{
Rect testTarget = *scrollBounds + pos.topLeft();
if (!testTarget.isInside(GH.getCursorPosition()))
return;
}
// vertical slider -> scrolling up move slider upwards
// horizontal slider -> scrolling up moves slider towards right
bool positive = (down != horizontal);
moveTo(value + 3 * (positive ? +scrollStep : -scrollStep));
}
void CSlider::keyPressed(const SDL_Keycode & key)

View File

@ -229,6 +229,8 @@ class CSlider : public CIntObject
std::shared_ptr<CButton> right;
std::shared_ptr<CButton> slider;
boost::optional<Rect> scrollBounds;
int capacity;//how many elements can be active at same time (e.g. hero list = 5)
int positions; //number of highest position (0 if there is only one)
bool horizontal;
@ -252,6 +254,10 @@ public:
/// Controls how many items wil be scrolled via one click
void setScrollStep(int to);
/// If set, mouse scroll will only scroll slider when inside of this area
void setScrollBounds(const Rect & bounds );
void clearScrollBounds();
/// Value modifiers
void moveLeft();
void moveRight();

View File

@ -405,31 +405,7 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
}
else if(other->hero && other->isSelected())
{
bool allow = true;
if(upg) //moving hero out of town - check if it is allowed
{
if(!hero && LOCPLINT->cb->howManyHeroes(false) >= CGI->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
{
std::string tmp = CGI->generaltexth->allTexts[18]; //You already have %d adventuring heroes under your command.
boost::algorithm::replace_first(tmp,"%d",std::to_string(LOCPLINT->cb->howManyHeroes(false)));
LOCPLINT->showInfoDialog(tmp, std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
allow = false;
}
else if(!other->hero->stacksCount()) //hero has no creatures - strange, but if we have appropriate error message...
{
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[19], std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo); //This hero has no creatures. A hero must have creatures before he can brave the dangers of the countryside.
allow = false;
}
}
setHighlight(false);
other->setHighlight(false);
if(allow)
{
owner->swapArmies();
hero = other->hero;
}
owner->swapArmies();
}
else if(hero)
{
@ -524,17 +500,42 @@ void HeroSlots::splitClicked()
void HeroSlots::swapArmies()
{
bool allow = true;
//moving hero out of town - check if it is allowed
if (town->garrisonHero)
{
if (!town->visitingHero && LOCPLINT->cb->howManyHeroes(false) >= CGI->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
{
std::string text = CGI->generaltexth->translate("core.genrltxt.18"); //You already have %d adventuring heroes under your command.
boost::algorithm::replace_first(text,"%d",std::to_string(LOCPLINT->cb->howManyHeroes(false)));
LOCPLINT->showInfoDialog(text, std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
allow = false;
}
else if (town->garrisonHero->stacksCount() == 0)
{
//This hero has no creatures. A hero must have creatures before he can brave the dangers of the countryside.
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("core.genrltxt.19"), {}, soundBase::sound_todo);
allow = false;
}
}
if(!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies: town army => hero army
{
if(!town->visitingHero->canBeMergedWith(*town))
{
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[275], std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
return;
allow = false;
}
}
LOCPLINT->cb->swapGarrisonHero(town);
}
garrisonedHero->setHighlight(false);
visitingHero->setHighlight(false);
if (allow)
LOCPLINT->cb->swapGarrisonHero(town);
}
class SORTHELP
{

View File

@ -84,14 +84,19 @@ int BattleOptionsTab::getAnimSpeed() const
int BattleOptionsTab::getQueueSizeId() const
{
std::string text = settings["battle"]["queueSize"].String();
if(text == "none")
std::string sizeText = settings["battle"]["queueSize"].String();
bool visible = settings["battle"]["showQueue"].Bool();
if(!visible)
return -1;
if(text == "auto")
if(sizeText == "none")
return -1;
if(sizeText == "auto")
return 0;
if(text == "small")
if(sizeText == "small")
return 1;
if(text == "big")
if(sizeText == "big")
return 2;
return 0;

View File

@ -147,7 +147,7 @@ GeneralOptionsTab::GeneralOptionsTab()
musicVolumeLabel->setText(std::to_string(CCS->musich->getVolume()) + "%");
std::shared_ptr<CLabel> soundVolumeLabel = widget<CLabel>("soundValueLabel");
musicVolumeLabel->setText(std::to_string(CCS->soundh->getVolume()) + "%");
soundVolumeLabel->setText(std::to_string(CCS->soundh->getVolume()) + "%");
}

View File

@ -138,6 +138,7 @@
"name": "musicSlider",
"type": "slider",
"position": {"x": 385, "y": 115},
"scrollBounds" : { "x" : -4, "y" : -34, "w" : 208, "h" : 52 },
"size": 200,
"style": "brown",
"orientation": "horizontal",
@ -165,6 +166,7 @@
"name": "soundVolumeSlider",
"type": "slider",
"position": {"x": 385, "y": 175},
"scrollBounds" : { "x" : -4, "y" : -34, "w" : 208, "h" : 52 },
"size": 200,
"style": "brown",
"orientation": "horizontal",

View File

@ -35,7 +35,10 @@ The following platforms are supported and known to work, others might require ch
if you want x86, otherwise pick **vcmi-deps-windows-conan.tgz**
- [Android](https://github.com/vcmi/vcmi-dependencies/releases)
3. Only if you have Apple Silicon Mac and trying to build for macOS or iOS: follow [instructions how to build Qt host tools for Apple Silicon](https://github.com/vcmi/vcmi-ios-deps#note-for-arm-macs), on step 3 copy them to `~/.conan/data/qt/5.15.x/_/_/package/SOME_HASH/bin` (`5.15.x` and `SOME_HASH` are placeholders).
3. Only if you have Apple Silicon Mac and trying to build for macOS or iOS:
1. Open file `~/.conan/data/qt/5.15.x/_/_/export/conanfile.py` (`5.15.x` is a placeholder), search for string `Designer` (there should be only one match), comment this line and the one above it by inserting `#` in the beginning, and save the file.
2. (optional) If you don't want to use Rosetta, follow [instructions how to build Qt host tools for Apple Silicon](https://github.com/vcmi/vcmi-ios-deps#note-for-arm-macs), on step 3 copy them to `~/.conan/data/qt/5.15.x/_/_/package/SOME_HASH/bin` (`5.15.x` and `SOME_HASH` are placeholders). Make sure **not** to copy `qt.conf`!
## Generate CMake integration

View File

@ -335,6 +335,7 @@ void FirstLaunchView::modPresetUpdate()
bool translationExists = !findTranslationModName().isEmpty();
ui->labelPresetLanguage->setVisible(translationExists);
ui->labelPresetLanguageDescr->setVisible(translationExists);
ui->checkBoxPresetLanguage->setVisible(translationExists);
ui->checkBoxPresetLanguage->setEnabled(checkCanInstallTranslation());

View File

@ -372,7 +372,7 @@ void CSettingsView::loadTranslation()
ui->labelTranslationStatus->setVisible(showTranslation);
ui->pushButtonTranslation->setVisible(showTranslation);
if (!translationExists)
if (!translationExists || !translationNeeded)
return;
bool translationAvailable = mainWindow->getModView()->isModAvailable(modName);

File diff suppressed because it is too large Load Diff

View File

@ -432,8 +432,11 @@ const CCreature * CCreatureHandler::getCreature(const std::string & scope, const
void CCreatureHandler::loadCommanders()
{
JsonNode data(ResourceID("config/commanders.json"));
data.setMeta(CModHandler::scopeBuiltin()); // assume that commanders are in core mod (for proper bonuses resolution)
ResourceID configResource("config/commanders.json");
std::string modSource = VLC->modh->findResourceOrigin(configResource);
JsonNode data(configResource);
data.setMeta(modSource);
const JsonNode & config = data; // switch to const data accessors
@ -960,9 +963,6 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
{
for (const JsonNode &exp : input.Vector())
{
auto bonus = JsonUtils::parseBonus (exp["bonus"]);
bonus->source = Bonus::STACK_EXPERIENCE;
bonus->duration = Bonus::PERMANENT;
const JsonVector &values = exp["values"].Vector();
int lowerLimit = 1;//, upperLimit = 255;
if (values[0].getType() == JsonNode::JsonType::DATA_BOOL)
@ -971,8 +971,14 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
{
if(val.Bool())
{
// parse each bonus separately
// we can not create copies since identifiers resolution does not tracks copies
// leading to unset identifier values in copies
auto bonus = JsonUtils::parseBonus (exp["bonus"]);
bonus->source = Bonus::STACK_EXPERIENCE;
bonus->duration = Bonus::PERMANENT;
bonus->limiter = std::make_shared<RankRangeLimiter>(RankRangeLimiter(lowerLimit));
creature->addNewBonus (std::make_shared<Bonus>(*bonus)); //bonuses must be unique objects
creature->addNewBonus (bonus);
break; //TODO: allow bonuses to turn off?
}
++lowerLimit;
@ -985,9 +991,14 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode
{
if (val.Float() != lastVal)
{
bonus->val = static_cast<int>(val.Float()) - lastVal;
JsonNode bonusInput = exp["bonus"];
bonusInput["val"].Float() = static_cast<int>(val.Float()) - lastVal;
auto bonus = JsonUtils::parseBonus (bonusInput);
bonus->source = Bonus::STACK_EXPERIENCE;
bonus->duration = Bonus::PERMANENT;
bonus->limiter.reset (new RankRangeLimiter(lowerLimit));
creature->addNewBonus (std::make_shared<Bonus>(*bonus));
creature->addNewBonus (bonus);
}
lastVal = static_cast<int>(val.Float());
++lowerLimit;

View File

@ -355,17 +355,17 @@ bool CGeneralTextHandler::validateTranslation(const std::string & language, cons
bool allFound = true;
for(const auto & string : config.Struct())
{
if (stringsLocalizations.count(string.first) > 0)
continue;
if (allFound)
logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
logMod->warn(R"( "%s" : "%s",)", string.first, TextOperations::escapeString(string.second.String()));
allFound = false;
}
// for(const auto & string : config.Struct())
// {
// if (stringsLocalizations.count(string.first) > 0)
// continue;
//
// if (allFound)
// logMod->warn("Translation into language '%s' in mod '%s' has unused lines:", language, modContext);
//
// logMod->warn(R"( "%s" : "%s",)", string.first, TextOperations::escapeString(string.second.String()));
// allFound = false;
// }
return allPresent && allFound;
}

View File

@ -367,7 +367,12 @@ class IVCMIDirsUNIX : public IVCMIDirs
bool IVCMIDirsUNIX::developmentMode() const
{
// We want to be able to run VCMI from single directory. E.g to run from build output directory
return bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiserver") && bfs::exists("vcmiclient");
const bool result = bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiclient");
#if SINGLE_PROCESS_APP
return result;
#else
return result && bfs::exists("vcmiserver");
#endif
}
bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; }

View File

@ -160,7 +160,7 @@ std::string CMapGenerator::getMapDescription() const
std::stringstream ss;
ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
randomSeed % map->map().width % map->map().height % map->map().levels() % static_cast<int>(mapGenOptions.getPlayerCount()) %
randomSeed % map->map().width % map->map().height % static_cast<int>(map->map().levels()) % static_cast<int>(mapGenOptions.getPlayerCount()) %
static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
monsterStrengthStr[monsterStrengthIndex]);

View File

@ -14,7 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN
const ui32 SERIALIZATION_VERSION = 820;
const ui32 SERIALIZATION_VERSION = 821;
const ui32 MINIMAL_SERIALIZATION_VERSION = 820;
const std::string SAVEGAME_MAGIC = "VCMISVG";

View File

@ -272,6 +272,8 @@ public:
template <typename Handler> void serialize(Handler & h, const int version)
{
h & identifier;
if (version > 820)
h & modScope;
h & id;
h & level;
h & power;

View File

@ -4922,8 +4922,9 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
{
const CSpell * spell = SpellID(healerAbility->subtype).toSpell();
spells::BattleCast parameters(gs->curB, healer, spells::Mode::SPELL_LIKE_ATTACK, spell); //We can heal infinitely by first aid tent
auto dest = battle::Destination(destStack, target.at(0).hexValue);
parameters.setSpellLevel(0);
parameters.cast(spellEnv, target);
parameters.cast(spellEnv, {dest});
}
break;
}