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:
commit
8a93b1083f
@ -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",
|
||||||
|
@ -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": "",
|
||||||
|
@ -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",
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -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
6
debian/changelog
vendored
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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" />
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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));
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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 &&
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user