1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

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

This commit is contained in:
Tomasz Zieliński 2023-12-11 07:49:43 +01:00
commit 8a93b1083f
36 changed files with 165 additions and 37 deletions

View File

@ -114,6 +114,8 @@
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Enhancements}\n\nToggle various quality of life interface improvements. Such as a backpack button etc. Disable to have a more classic experience.", "vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Enhancements}\n\nToggle various quality of life interface improvements. Such as a backpack button etc. Disable to have a more classic experience.",
"vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Large Spell Book", "vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Large Spell Book",
"vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Large Spell Book}\n\nEnables larger spell book that fits more spells per page. Spell book page change animation does not work with this setting enabled.", "vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Large Spell Book}\n\nEnables larger spell book that fits more spells per page. Spell book page change animation does not work with this setting enabled.",
"vcmi.systemOptions.audioMuteFocus.hover" : "Mute on inactivity",
"vcmi.systemOptions.audioMuteFocus.help" : "{Mute on inactivity}\n\nMute audio on inactive window focus. Exceptions are ingame messages and new turn sound.",
"vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel", "vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel",
"vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info panel, instead of popping up in a separate window.", "vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info panel, instead of popping up in a separate window.",
@ -129,6 +131,8 @@
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info Panel Creature Management}\n\nAllows rearranging creatures in info panel instead of cycling between default components.", "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info Panel Creature Management}\n\nAllows rearranging creatures in info panel instead of cycling between default components.",
"vcmi.adventureOptions.leftButtonDrag.hover" : "Left Click Drag Map", "vcmi.adventureOptions.leftButtonDrag.hover" : "Left Click Drag Map",
"vcmi.adventureOptions.leftButtonDrag.help" : "{Left Click Drag Map}\n\nWhen enabled, moving mouse with left button pressed will drag adventure map view.", "vcmi.adventureOptions.leftButtonDrag.help" : "{Left Click Drag Map}\n\nWhen enabled, moving mouse with left button pressed will drag adventure map view.",
"vcmi.adventureOptions.smoothDragging.hover" : "Smooth Map Dragging",
"vcmi.adventureOptions.smoothDragging.help" : "{Smooth Map Dragging}\n\nWhen enabled, map dragging has a modern run out effect.",
"vcmi.adventureOptions.mapScrollSpeed1.hover": "", "vcmi.adventureOptions.mapScrollSpeed1.hover": "",
"vcmi.adventureOptions.mapScrollSpeed5.hover": "", "vcmi.adventureOptions.mapScrollSpeed5.hover": "",
"vcmi.adventureOptions.mapScrollSpeed6.hover": "", "vcmi.adventureOptions.mapScrollSpeed6.hover": "",
@ -258,7 +262,7 @@
"vcmi.optionsTab.simturnsMin.help" : "Play simultaneously for specified number of days. Contacts between players during this period are blocked", "vcmi.optionsTab.simturnsMin.help" : "Play simultaneously for specified number of days. Contacts between players during this period are blocked",
"vcmi.optionsTab.simturnsMax.help" : "Play simultaneously for specified number of days or until contact with another player", "vcmi.optionsTab.simturnsMax.help" : "Play simultaneously for specified number of days or until contact with another player",
"vcmi.optionsTab.simturnsAI.help" : "{Simultaneous AI Turns}\nExperimental option. Allows AI players to act at the same time as human player when simultaneous turns are enabled.", "vcmi.optionsTab.simturnsAI.help" : "{Simultaneous AI Turns}\nExperimental option. Allows AI players to act at the same time as human player when simultaneous turns are enabled.",
"vcmi.optionsTab.turnTime.select" : "Select turn timer preset", "vcmi.optionsTab.turnTime.select" : "Select turn timer preset",
"vcmi.optionsTab.turnTime.unlimited" : "Unlimited turn time", "vcmi.optionsTab.turnTime.unlimited" : "Unlimited turn time",
"vcmi.optionsTab.turnTime.classic.1" : "Classic timer: 1 minute", "vcmi.optionsTab.turnTime.classic.1" : "Classic timer: 1 minute",

View File

@ -114,6 +114,8 @@
"vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Verbesserungen}\n\nSchaltet verschiedene Interface Verbesserungen um. Wie z.B. ein Rucksack-Button, etc. Deaktivieren, um ein klassischeres Erlebnis zu haben.", "vcmi.systemOptions.enableUiEnhancementsButton.help" : "{Interface Verbesserungen}\n\nSchaltet verschiedene Interface Verbesserungen um. Wie z.B. ein Rucksack-Button, etc. Deaktivieren, um ein klassischeres Erlebnis zu haben.",
"vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Großes Zauberbuch", "vcmi.systemOptions.enableLargeSpellbookButton.hover" : "Großes Zauberbuch",
"vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Großes Zauberbuch}\n\nErmöglicht ein größeres Zauberbuch, in das mehr Zaubersprüche pro Seite passen. Die Animation des Seitenwechsels im Zauberbuch funktioniert nicht, wenn diese Einstellung aktiviert ist.", "vcmi.systemOptions.enableLargeSpellbookButton.help" : "{Großes Zauberbuch}\n\nErmöglicht ein größeres Zauberbuch, in das mehr Zaubersprüche pro Seite passen. Die Animation des Seitenwechsels im Zauberbuch funktioniert nicht, wenn diese Einstellung aktiviert ist.",
"vcmi.systemOptions.audioMuteFocus.hover" : "Stumm bei Inaktivität",
"vcmi.systemOptions.audioMuteFocus.help" : "{Stumm bei Inaktivität}\n\nSchaltet Audio bei inaktiven Fenster-Fokus stumm. Ausnahmen sind Ingame-Nachrichten und der Neuer-Zug-Sound.",
"vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen", "vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen",
"vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen", "vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen",
@ -129,6 +131,8 @@
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info-Panel Kreaturenmanagement}\n\nErmöglicht die Neuanordnung von Kreaturen im Info-Panel, anstatt zwischen den Standardkomponenten zu wechseln", "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info-Panel Kreaturenmanagement}\n\nErmöglicht die Neuanordnung von Kreaturen im Info-Panel, anstatt zwischen den Standardkomponenten zu wechseln",
"vcmi.adventureOptions.leftButtonDrag.hover" : "Ziehen der Karte mit Links", "vcmi.adventureOptions.leftButtonDrag.hover" : "Ziehen der Karte mit Links",
"vcmi.adventureOptions.leftButtonDrag.help" : "{Ziehen der Karte mit Links}\n\nWenn aktiviert, wird die Maus bei gedrückter linker Taste in die Kartenansicht gezogen", "vcmi.adventureOptions.leftButtonDrag.help" : "{Ziehen der Karte mit Links}\n\nWenn aktiviert, wird die Maus bei gedrückter linker Taste in die Kartenansicht gezogen",
"vcmi.adventureOptions.smoothDragging.hover" : "Nahtloses Ziehen der Karte",
"vcmi.adventureOptions.smoothDragging.help" : "{Nahtloses Ziehen der Karte}\n\nWenn aktiviert hat das Ziehen der Karte einen sanften Auslaufeffekt.",
"vcmi.adventureOptions.mapScrollSpeed1.hover": "", "vcmi.adventureOptions.mapScrollSpeed1.hover": "",
"vcmi.adventureOptions.mapScrollSpeed5.hover": "", "vcmi.adventureOptions.mapScrollSpeed5.hover": "",
"vcmi.adventureOptions.mapScrollSpeed6.hover": "", "vcmi.adventureOptions.mapScrollSpeed6.hover": "",

View File

@ -98,7 +98,7 @@
] ]
}, },
"version" : "1.3", "version" : "1.4",
"author" : "VCMI Team", "author" : "VCMI Team",
"contact" : "http://forum.vcmi.eu/index.php", "contact" : "http://forum.vcmi.eu/index.php",
"modType" : "Graphical", "modType" : "Graphical",

View File

@ -10,8 +10,8 @@ android {
applicationId "is.xyz.vcmi" applicationId "is.xyz.vcmi"
minSdk 19 minSdk 19
targetSdk 33 targetSdk 33
versionCode 1400 versionCode 1410
versionName "1.4.0" versionName "1.4.1"
setProperty("archivesBaseName", "vcmi") setProperty("archivesBaseName", "vcmi")
} }

View File

@ -170,7 +170,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_GAINED:
{ {
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
if(settings["general"]["enableUiEnhancements"].Bool()) { if(settings["general"]["audioMuteFocus"].Bool()) {
CCS->musich->setVolume(settings["general"]["music"].Integer()); CCS->musich->setVolume(settings["general"]["music"].Integer());
CCS->soundh->setVolume(settings["general"]["sound"].Integer()); CCS->soundh->setVolume(settings["general"]["sound"].Integer());
} }
@ -179,7 +179,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_LOST:
{ {
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex); boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
if(settings["general"]["enableUiEnhancements"].Bool()) { if(settings["general"]["audioMuteFocus"].Bool()) {
CCS->musich->setVolume(0); CCS->musich->setVolume(0);
CCS->soundh->setVolume(0); CCS->soundh->setVolume(0);
} }

View File

@ -122,7 +122,10 @@ void MapView::onMapLevelSwitched()
void MapView::onMapScrolled(const Point & distance) void MapView::onMapScrolled(const Point & distance)
{ {
if(!isGesturing()) if(!isGesturing())
{
postSwipeSpeed = 0.0;
controller->setViewCenter(model->getMapViewCenter() + distance, model->getLevel()); controller->setViewCenter(model->getMapViewCenter() + distance, model->getLevel());
}
} }
void MapView::onMapSwiped(const Point & viewPosition) void MapView::onMapSwiped(const Point & viewPosition)

View File

@ -156,6 +156,11 @@ void CSlider::clickPressed(const Point & cursorPosition)
bool CSlider::receiveEvent(const Point &position, int eventType) const bool CSlider::receiveEvent(const Point &position, int eventType) const
{ {
if (eventType == LCLICK)
{
return pos.isInside(position) && !left->pos.isInside(position) && !right->pos.isInside(position);
}
if(eventType != WHEEL && eventType != GESTURE) if(eventType != WHEEL && eventType != GESTURE)
{ {
return CIntObject::receiveEvent(position, eventType); return CIntObject::receiveEvent(position, eventType);

View File

@ -126,6 +126,10 @@ AdventureOptionsTab::AdventureOptionsTab()
{ {
return setBoolSetting("adventure", "leftButtonDrag", value); return setBoolSetting("adventure", "leftButtonDrag", value);
}); });
addCallback("smoothDraggingChanged", [](bool value)
{
return setBoolSetting("adventure", "smoothDragging", value);
});
build(config); build(config);
std::shared_ptr<CToggleGroup> playerHeroSpeedToggle = widget<CToggleGroup>("heroMovementSpeedPicker"); std::shared_ptr<CToggleGroup> playerHeroSpeedToggle = widget<CToggleGroup>("heroMovementSpeedPicker");
@ -164,4 +168,8 @@ AdventureOptionsTab::AdventureOptionsTab()
std::shared_ptr<CToggleButton> leftButtonDragCheckbox = widget<CToggleButton>("leftButtonDragCheckbox"); std::shared_ptr<CToggleButton> leftButtonDragCheckbox = widget<CToggleButton>("leftButtonDragCheckbox");
if (leftButtonDragCheckbox) if (leftButtonDragCheckbox)
leftButtonDragCheckbox->setSelected(settings["adventure"]["leftButtonDrag"].Bool()); leftButtonDragCheckbox->setSelected(settings["adventure"]["leftButtonDrag"].Bool());
std::shared_ptr<CToggleButton> smoothDraggingCheckbox = widget<CToggleButton>("smoothDraggingCheckbox");
if (smoothDraggingCheckbox)
smoothDraggingCheckbox->setSelected(settings["adventure"]["smoothDragging"].Bool());
} }

View File

@ -167,6 +167,11 @@ GeneralOptionsTab::GeneralOptionsTab()
setBoolSetting("gameTweaks", "enableLargeSpellbook", value); setBoolSetting("gameTweaks", "enableLargeSpellbook", value);
}); });
addCallback("audioMuteFocusChanged", [](bool value)
{
setBoolSetting("general", "audioMuteFocus", value);
});
//moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content //moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content
addCallback("availableCreaturesAsDwellingChanged", [=](int value) addCallback("availableCreaturesAsDwellingChanged", [=](int value)
{ {
@ -215,6 +220,10 @@ GeneralOptionsTab::GeneralOptionsTab()
if (enableLargeSpellbookCheckbox) if (enableLargeSpellbookCheckbox)
enableLargeSpellbookCheckbox->setSelected(settings["gameTweaks"]["enableLargeSpellbook"].Bool()); enableLargeSpellbookCheckbox->setSelected(settings["gameTweaks"]["enableLargeSpellbook"].Bool());
std::shared_ptr<CToggleButton> audioMuteFocusCheckbox = widget<CToggleButton>("audioMuteFocusCheckbox");
if (audioMuteFocusCheckbox)
audioMuteFocusCheckbox->setSelected(settings["general"]["audioMuteFocus"].Bool());
std::shared_ptr<CSlider> musicSlider = widget<CSlider>("musicSlider"); std::shared_ptr<CSlider> musicSlider = widget<CSlider>("musicSlider");
musicSlider->scrollTo(CCS->musich->getVolume()); musicSlider->scrollTo(CCS->musich->getVolume());

View File

@ -1,6 +1,6 @@
set(VCMI_VERSION_MAJOR 1) set(VCMI_VERSION_MAJOR 1)
set(VCMI_VERSION_MINOR 4) set(VCMI_VERSION_MINOR 4)
set(VCMI_VERSION_PATCH 0) set(VCMI_VERSION_PATCH 1)
add_definitions( add_definitions(
-DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR} -DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR}
-DVCMI_VERSION_MINOR=${VCMI_VERSION_MINOR} -DVCMI_VERSION_MINOR=${VCMI_VERSION_MINOR}

View File

@ -39,7 +39,8 @@
"useSavePrefix", "useSavePrefix",
"savePrefix", "savePrefix",
"startTurnAutosave", "startTurnAutosave",
"enableUiEnhancements" "enableUiEnhancements",
"audioMuteFocus"
], ],
"properties" : { "properties" : {
"playerName" : { "playerName" : {
@ -131,6 +132,10 @@
"enableUiEnhancements" : { "enableUiEnhancements" : {
"type": "boolean", "type": "boolean",
"default": true "default": true
},
"audioMuteFocus" : {
"type": "boolean",
"default": false
} }
} }
}, },

View File

@ -347,6 +347,9 @@
{ {
"text": "vcmi.adventureOptions.leftButtonDrag.hover", "text": "vcmi.adventureOptions.leftButtonDrag.hover",
"created" : "desktop" "created" : "desktop"
},
{
"text": "vcmi.adventureOptions.smoothDragging.hover"
} }
] ]
}, },
@ -391,6 +394,11 @@
"help": "vcmi.adventureOptions.leftButtonDrag", "help": "vcmi.adventureOptions.leftButtonDrag",
"callback": "leftButtonDragChanged", "callback": "leftButtonDragChanged",
"created" : "desktop" "created" : "desktop"
},
{
"name": "smoothDraggingCheckbox",
"help": "vcmi.adventureOptions.smoothDragging",
"callback": "smoothDraggingChanged"
} }
] ]
} }

View File

@ -180,6 +180,28 @@
"type": "labelCentered", "type": "labelCentered",
"position": {"x": 565, "y": 158} "position": {"x": 565, "y": 158}
}, },
{
"type" : "verticalLayout",
"customType" : "labelDescription",
"position" : {"x": 415, "y": 202},
"items" : [
{
"text": "vcmi.systemOptions.audioMuteFocus.hover"
}
]
},
{
"type" : "verticalLayout",
"customType" : "checkbox",
"position" : {"x": 380, "y": 200},
"items" : [
{
"name": "audioMuteFocusCheckbox",
"help": "vcmi.systemOptions.audioMuteFocus",
"callback": "audioMuteFocusChanged"
}
]
},
/////////////////////////////////////// Bottom section - Towns Settings /////////////////////////////////////// Bottom section - Towns Settings
{ {
"type" : "verticalLayout", "type" : "verticalLayout",

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
vcmi (1.4.1) jammy; urgency=medium
* New upstream release
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 22 Dec 2023 16:00:00 +0200
vcmi (1.4.0) jammy; urgency=medium vcmi (1.4.0) jammy; urgency=medium
* New upstream release * New upstream release

View File

@ -4,7 +4,15 @@
# VCMI Project # VCMI Project
VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities. VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving it new and extended possibilities.
<p>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Castle%20Siege.jpg?raw=true" alt="Vanilla town siege in extended window" style="height:120px;"/>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Town%20Screen%20with%20Radial%20Menu.jpg?raw=true" alt="Vanilla town view with radial menu for touchscreen devices" style="height:120px;"/>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Big%20spellbook.jpg?raw=true" alt="Large Spellbook with German translation" style="height:120px;"/>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Quick%20Hero%20Select%20Bastion.jpg?raw=true" alt="New widget for Hero selection, featuring Pavillon Town" style="height:120px;"/>
</p>
## Links ## Links
@ -27,6 +35,13 @@ Please see corresponding installation guide articles for details for your platfo
- [Android](players/Installation_Android.md) - [Android](players/Installation_Android.md)
- [iOS](players/Installation_iOS.md) - [iOS](players/Installation_iOS.md)
<p>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Antagarich%20Burning%20Battle.jpg?raw=true" alt="Forge Town in battle" style="height:120px;"/>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Town%20and%20Unit.jpg?raw=true" alt="Asylum town with new creature dialog" style="height:120px;"/>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Siege.jpg?raw=true" alt="Ruins town siege" style="height:120px;"/>
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Editor.jpg?raw=true" alt="Map editor" style="height:120px;"/>
</p>
## Documentation and guidelines for players ## Documentation and guidelines for players
- [General information about VCMI Project](players/Manual.md) - [General information about VCMI Project](players/Manual.md)

View File

@ -68,6 +68,7 @@
<category>StrategyGame</category> <category>StrategyGame</category>
</categories> </categories>
<releases> <releases>
<release version="1.4.1" date="2023-12-22" />
<release version="1.4.0" date="2023-12-08" /> <release version="1.4.0" date="2023-12-08" />
<release version="1.3.2" date="2023-09-15" /> <release version="1.3.2" date="2023-09-15" />
<release version="1.3.1" date="2023-08-18" /> <release version="1.3.1" date="2023-08-18" />

View File

@ -313,7 +313,7 @@ QString CModListView::genModInfoText(CModEntry & mod)
result += replaceIfNotEmpty(getModNames(mod.getDependencies()), lineTemplate.arg(tr("Required mods"))); result += replaceIfNotEmpty(getModNames(mod.getDependencies()), lineTemplate.arg(tr("Required mods")));
result += replaceIfNotEmpty(getModNames(mod.getConflicts()), lineTemplate.arg(tr("Conflicting mods"))); result += replaceIfNotEmpty(getModNames(mod.getConflicts()), lineTemplate.arg(tr("Conflicting mods")));
result += replaceIfNotEmpty(getModNames(mod.getValue("description").toStringList()), textTemplate.arg(tr("Description"))); result += replaceIfNotEmpty(mod.getValue("description"), textTemplate.arg(tr("Description")));
result += "<p></p>"; // to get some empty space result += "<p></p>"; // to get some empty space

View File

@ -168,7 +168,10 @@ DLL_LINKAGE std::string MetaString::toString() const
boost::replace_first(dst, "%d", std::to_string(numbers[nums++])); boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
break; break;
case EMessage::REPLACE_POSITIVE_NUMBER: case EMessage::REPLACE_POSITIVE_NUMBER:
boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++])); if (dst.find("%+d") != std::string::npos)
boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++]));
else
boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
break; break;
default: default:
logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem)); logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem));

View File

@ -132,6 +132,9 @@ int DamageCalculator::getActorAttackSlayer() const
const std::string cachingStrSlayer = "type_SLAYER"; const std::string cachingStrSlayer = "type_SLAYER";
static const auto selectorSlayer = Selector::type()(BonusType::SLAYER); static const auto selectorSlayer = Selector::type()(BonusType::SLAYER);
if (!info.defender->hasBonusOfType(BonusType::KING))
return 0;
auto slayerEffects = info.attacker->getBonuses(selectorSlayer, cachingStrSlayer); auto slayerEffects = info.attacker->getBonuses(selectorSlayer, cachingStrSlayer);
auto slayerAffected = info.defender->unitType()->valOfBonuses(Selector::type()(BonusType::KING)); auto slayerAffected = info.defender->unitType()->valOfBonuses(Selector::type()(BonusType::KING));

View File

@ -1444,18 +1444,22 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
{ {
// list of players that need to control object to fulfull condition // list of players that need to control object to fulfull condition
// NOTE: cgameinfocallback specified explicitly in order to get const version // NOTE: cgameinfocallback specified explicitly in order to get const version
const auto & team = CGameInfoCallback::getPlayerTeam(player)->players; const auto * team = CGameInfoCallback::getPlayerTeam(player);
if (condition.objectID != ObjectInstanceID::NONE) // mode A - flag one specific object, like town if (condition.objectID != ObjectInstanceID::NONE) // mode A - flag one specific object, like town
{ {
return team.count(getObjInstance(condition.objectID)->tempOwner) != 0; const auto * object = getObjInstance(condition.objectID);
if (!object)
return false;
return team->players.count(object->getOwner()) != 0;
} }
else else
{ {
for(const auto & elem : map->objects) // mode B - flag all objects of this type for(const auto & elem : map->objects) // mode B - flag all objects of this type
{ {
//check not flagged objs //check not flagged objs
if ( elem && elem->ID == condition.objectType.as<MapObjectID>() && team.count(elem->tempOwner) == 0 ) if ( elem && elem->ID == condition.objectType.as<MapObjectID>() && team->players.count(elem->getOwner()) == 0 )
return false; return false;
} }
return true; return true;

View File

@ -452,10 +452,12 @@ void CObjectClassesHandler::generateExtraMonolithsForRMG(ObjectClass * container
newPortal->type = portal->getIndex(); newPortal->type = portal->getIndex();
newPortal->subtype = portalVec.size(); //indexes must be unique, they are returned as a set newPortal->subtype = portalVec.size(); //indexes must be unique, they are returned as a set
newPortal->blockVisit = portal->blockVisit;
newPortal->removable = portal->removable;
portalVec.push_back(newPortal); portalVec.push_back(newPortal);
registerObject(ModScope::scopeGame(), container->getJsonKey(), newPortal->subTypeName, newPortal->subtype); registerObject(newPortal->modScope, container->getJsonKey(), newPortal->subTypeName, newPortal->subtype);
} }
} }

View File

@ -133,7 +133,7 @@ void CHeroInstanceConstructor::afterLoadFinalization()
{ {
filters[entry.first] = LogicalExpression<HeroTypeID>(entry.second, [](const JsonNode & node) filters[entry.first] = LogicalExpression<HeroTypeID>(entry.second, [](const JsonNode & node)
{ {
return HeroTypeID(VLC->identifiers()->getIdentifier("hero", node.Vector()[0]).value()); return HeroTypeID(VLC->identifiers()->getIdentifier("hero", node.Vector()[0]).value_or(-1));
}); });
} }
} }

View File

@ -150,6 +150,11 @@ bool CGHeroInstance::isCoastVisitable() const
return true; return true;
} }
bool CGHeroInstance::isBlockedVisitable() const
{
return true;
}
BattleField CGHeroInstance::getBattlefield() const BattleField CGHeroInstance::getBattlefield() const
{ {
return BattleField::NONE; return BattleField::NONE;
@ -280,7 +285,6 @@ CGHeroInstance::CGHeroInstance():
setNodeType(HERO); setNodeType(HERO);
ID = Obj::HERO; ID = Obj::HERO;
secSkills.emplace_back(SecondarySkill::NONE, -1); secSkills.emplace_back(SecondarySkill::NONE, -1);
blockVisit = true;
} }
PlayerColor CGHeroInstance::getOwner() const PlayerColor CGHeroInstance::getOwner() const

View File

@ -301,6 +301,7 @@ public:
void updateFrom(const JsonNode & data) override; void updateFrom(const JsonNode & data) override;
bool isCoastVisitable() const override; bool isCoastVisitable() const override;
bool isBlockedVisitable() const override;
BattleField getBattlefield() const override; BattleField getBattlefield() const override;
protected: protected:
void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;//synchr void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;//synchr

View File

@ -1224,12 +1224,21 @@ TerrainId CGTownInstance::getNativeTerrain() const
GrowthInfo::Entry::Entry(const std::string &format, int _count) GrowthInfo::Entry::Entry(const std::string &format, int _count)
: count(_count) : count(_count)
{ {
description = boost::str(boost::format(format) % count); MetaString formatter;
formatter.appendRawString(format);
formatter.replacePositiveNumber(count);
description = formatter.toString();
} }
GrowthInfo::Entry::Entry(int subID, const BuildingID & building, int _count): count(_count) GrowthInfo::Entry::Entry(int subID, const BuildingID & building, int _count): count(_count)
{ {
description = boost::str(boost::format("%s %+d") % (*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated() % count); MetaString formatter;
formatter.appendRawString("%s %+d");
formatter.replaceRawString((*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated());
formatter.replacePositiveNumber(count);
description = formatter.toString();
} }
GrowthInfo::Entry::Entry(int _count, std::string fullDescription): GrowthInfo::Entry::Entry(int _count, std::string fullDescription):

View File

@ -59,7 +59,7 @@ std::vector<Component> CRewardableObject::loadComponents(const CGHeroInstance *
if (rewardIndices.empty()) if (rewardIndices.empty())
return result; return result;
if (configuration.selectMode != Rewardable::SELECT_FIRST) if (configuration.selectMode != Rewardable::SELECT_FIRST && rewardIndices.size() > 1)
{ {
for (auto index : rewardIndices) for (auto index : rewardIndices)
result.push_back(configuration.info.at(index).reward.getDisplayedComponent(contextHero)); result.push_back(configuration.info.at(index).reward.getDisplayedComponent(contextHero));

View File

@ -937,7 +937,7 @@ void CGSignBottle::initObj(CRandomGenerator & rand)
{ {
auto vector = VLC->generaltexth->findStringsWithPrefix("core.randsign"); auto vector = VLC->generaltexth->findStringsWithPrefix("core.randsign");
std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, rand); std::string messageIdentifier = *RandomGeneratorUtil::nextItem(vector, rand);
message.appendTextID(TextIdentifier("core", "randsign", messageIdentifier).get()); message.appendTextID(messageIdentifier);
} }
if(ID == Obj::OCEAN_BOTTLE) if(ID == Obj::OCEAN_BOTTLE)

View File

@ -10,7 +10,7 @@
#pragma once #pragma once
#ifdef __UCLIBC__ #if defined(__UCLIBC__) || defined(__FreeBSD__)
#undef major #undef major
#undef minor #undef minor
#undef patch #undef patch

View File

@ -162,9 +162,6 @@ void CPathfinder::calculatePaths()
if(neighbour->locked) if(neighbour->locked)
continue; continue;
if (source.node->theNodeBefore && source.node->theNodeBefore->coord == neighbour->coord )
continue; // block U-turns
if(!hlp->isLayerAvailable(neighbour->layer)) if(!hlp->isLayerAvailable(neighbour->layer))
continue; continue;

View File

@ -30,6 +30,7 @@ Rewardable::Limiter::Limiter()
, heroLevel(-1) , heroLevel(-1)
, manaPercentage(0) , manaPercentage(0)
, manaPoints(0) , manaPoints(0)
, canLearnSkills(false)
, primary(GameConstants::PRIMARY_SKILLS, 0) , primary(GameConstants::PRIMARY_SKILLS, 0)
{ {
} }
@ -45,6 +46,7 @@ bool operator==(const Rewardable::Limiter & l, const Rewardable::Limiter & r)
&& l.manaPoints == r.manaPoints && l.manaPoints == r.manaPoints
&& l.manaPercentage == r.manaPercentage && l.manaPercentage == r.manaPercentage
&& l.secondary == r.secondary && l.secondary == r.secondary
&& l.canLearnSkills == r.canLearnSkills
&& l.creatures == r.creatures && l.creatures == r.creatures
&& l.spells == r.spells && l.spells == r.spells
&& l.artifacts == r.artifacts && l.artifacts == r.artifacts

View File

@ -580,7 +580,7 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
} }
logGlobal->trace("Player %d: %s", player.second.getColor(), playerType); logGlobal->trace("Player %d: %s", player.second.getColor(), playerType);
} }
logGlobal->info("Final player config: %d total, %d cpu-only", players.size(), static_cast<int>(getCompOnlyPlayerCount())); logGlobal->info("Final player config: %d total, %d cpu-only", players.size(), cpuOnlyPlayers);
} }
void CMapGenOptions::updatePlayers() void CMapGenOptions::updatePlayers()
@ -730,12 +730,6 @@ std::vector<const CRmgTemplate *> CMapGenOptions::getPossibleTemplates() const
return true; return true;
} }
if(compOnlyPlayerCount != CMapGenOptions::RANDOM_SIZE)
{
if (!tmpl->getHumanPlayers().isInRange(compOnlyPlayerCount))
return true;
}
return false; return false;
}); });

View File

@ -128,6 +128,16 @@ void Object::Instance::setTemplate(TerrainId terrain, CRandomGenerator & rng)
auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated(); auto terrainName = VLC->terrainTypeHandler->getById(terrain)->getNameTranslated();
throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.getObjTypeIndex() % terrainName)); throw rmgException(boost::str(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.getObjTypeIndex() % terrainName));
} }
//Get terrain-specific template if possible
int leastTerrains = (*boost::min_element(templates, [](const std::shared_ptr<const ObjectTemplate> & tmp1, const std::shared_ptr<const ObjectTemplate> & tmp2)
{
return tmp1->getAllowedTerrains().size() < tmp2->getAllowedTerrains().size();
}))->getAllowedTerrains().size();
vstd::erase_if(templates, [leastTerrains](const std::shared_ptr<const ObjectTemplate> & tmp)
{
return tmp->getAllowedTerrains().size() > leastTerrains;
});
dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng); dObject.appearance = *RandomGeneratorUtil::nextItem(templates, rng);
dAccessibleAreaCache.clear(); dAccessibleAreaCache.clear();

View File

@ -107,8 +107,8 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int32_t level) const
{ {
if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS) if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
{ {
logGlobal->error("CSpell::getLevelInfo: invalid school level %d", level); logGlobal->error("CSpell::getLevelInfo: invalid school mastery level %d", level);
return levels.at(0); return levels.at(MasteryLevel::EXPERT);
} }
return levels.at(level); return levels.at(level);

View File

@ -42,6 +42,12 @@ void Summon::adjustTargetTypes(std::vector<TargetType> & types) const
bool Summon::applicable(Problem & problem, const Mechanics * m) const bool Summon::applicable(Problem & problem, const Mechanics * m) const
{ {
if (creature == CreatureID::NONE)
{
logMod->error("Attempt to summon non-existing creature!");
return m->adaptGenericProblem(problem);
}
if(exclusive) if(exclusive)
{ {
//check if there are summoned creatures of other type //check if there are summoned creatures of other type

View File

@ -1133,7 +1133,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
if (guardian && getVisitingHero(guardian) != nullptr) if (guardian && getVisitingHero(guardian) != nullptr)
return complainRet("Cannot move hero, destination monster is busy!"); return complainRet("Cannot move hero, destination monster is busy!");
if (objectToVisit && getVisitingHero(objectToVisit) != nullptr) if (objectToVisit && getVisitingHero(objectToVisit) != nullptr && getVisitingHero(objectToVisit) != h)
return complainRet("Cannot move hero, destination object is busy!"); return complainRet("Cannot move hero, destination object is busy!");
if (objectToVisit && if (objectToVisit &&

View File

@ -1032,7 +1032,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
const CStack * actor = item.first; const CStack * actor = item.first;
int64_t rawDamage = item.second; int64_t rawDamage = item.second;
const CGHeroInstance * actorOwner = battle.battleGetFightingHero(actor->unitOwner()); const CGHeroInstance * actorOwner = battle.battleGetFightingHero(actor->unitSide());
if(actorOwner) if(actorOwner)
{ {
@ -1088,7 +1088,10 @@ void BattleActionProcessor::attackCasting(const CBattleInfoCallback & battle, bo
TConstBonusListPtr spells = attacker->getBonuses(Selector::type()(attackMode)); TConstBonusListPtr spells = attacker->getBonuses(Selector::type()(attackMode));
for(const auto & sf : *spells) for(const auto & sf : *spells)
{ {
spellsToCast.insert(sf->subtype.as<SpellID>()); if (sf->subtype.as<SpellID>() != SpellID())
spellsToCast.insert(sf->subtype.as<SpellID>());
else
logMod->error("Invalid spell to cast during attack!");
} }
for(SpellID spellID : spellsToCast) for(SpellID spellID : spellsToCast)
{ {