Browse Source

access: speak class + difficulty descriptions

pull/8474/head
mojsior 2 months ago
parent
commit
ff82e243b8
  1. 17
      Source/DiabloUI/diabloui.cpp
  2. 5
      Source/DiabloUI/diabloui.h
  3. 29
      Source/DiabloUI/hero/selhero.cpp
  4. 20
      Source/DiabloUI/multi/selgame.cpp
  5. 24
      Translations/pl.po

17
Source/DiabloUI/diabloui.cpp

@ -114,6 +114,8 @@ bool UiItemsWraps;
std::optional<TextInputState> UiTextInputState; std::optional<TextInputState> UiTextInputState;
bool allowEmptyTextInput = false; bool allowEmptyTextInput = false;
std::optional<std::string> UiSpokenTextOverride;
constexpr Uint32 ListDoubleClickTimeMs = 500; constexpr Uint32 ListDoubleClickTimeMs = 500;
std::size_t lastListClickIndex = static_cast<std::size_t>(-1); std::size_t lastListClickIndex = static_cast<std::size_t>(-1);
Uint32 lastListClickTicks = 0; Uint32 lastListClickTicks = 0;
@ -160,6 +162,12 @@ void SpeakListItem(std::size_t index, bool force = false)
if (gUiList == nullptr || index > SelectedItemMax) if (gUiList == nullptr || index > SelectedItemMax)
return; return;
if (UiSpokenTextOverride) {
SpeakText(*UiSpokenTextOverride, force);
UiSpokenTextOverride = std::nullopt;
return;
}
const UiListItem *pItem = gUiList->GetItem(index); const UiListItem *pItem = gUiList->GetItem(index);
if (pItem == nullptr) if (pItem == nullptr)
return; return;
@ -235,6 +243,11 @@ void UiUpdateFadePalette()
} // namespace } // namespace
void UiSetSpokenTextOverride(std::string text)
{
UiSpokenTextOverride = std::move(text);
}
bool IsTextInputActive() bool IsTextInputActive()
{ {
return UiTextInputState.has_value(); return UiTextInputState.has_value();
@ -365,8 +378,6 @@ void UiFocus(std::size_t itemIndex, bool checkUp, bool ignoreItemsWraps = false)
} }
pItem = gUiList->GetItem(itemIndex); pItem = gUiList->GetItem(itemIndex);
} }
SpeakListItem(itemIndex);
if (HasAnyOf(pItem->uiFlags, UiFlags::NeedsNextElement)) if (HasAnyOf(pItem->uiFlags, UiFlags::NeedsNextElement))
AdjustListOffset(itemIndex + 1); AdjustListOffset(itemIndex + 1);
AdjustListOffset(itemIndex); AdjustListOffset(itemIndex);
@ -377,6 +388,8 @@ void UiFocus(std::size_t itemIndex, bool checkUp, bool ignoreItemsWraps = false)
if (gfnListFocus != nullptr) if (gfnListFocus != nullptr)
gfnListFocus(itemIndex); gfnListFocus(itemIndex);
SpeakListItem(itemIndex);
} }
void UiFocusUp() void UiFocusUp()

5
Source/DiabloUI/diabloui.h

@ -4,6 +4,8 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <string>
#include <string_view>
#ifdef USE_SDL3 #ifdef USE_SDL3
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
@ -113,6 +115,9 @@ void UiFocusNavigationSelect();
void UiFocusNavigationEsc(); void UiFocusNavigationEsc();
void UiFocusNavigationYesNo(); void UiFocusNavigationYesNo();
/** Overrides what the screen reader will speak for the next focused list item. */
void UiSetSpokenTextOverride(std::string text);
void UiInitList(void (*fnFocus)(size_t value), void (*fnSelect)(size_t value), void (*fnEsc)(), const std::vector<std::unique_ptr<UiItemBase>> &items, bool wraps = false, void (*fnFullscreen)() = nullptr, bool (*fnYesNo)() = nullptr, size_t selectedItem = 0); void UiInitList(void (*fnFocus)(size_t value), void (*fnSelect)(size_t value), void (*fnEsc)(), const std::vector<std::unique_ptr<UiItemBase>> &items, bool wraps = false, void (*fnFullscreen)() = nullptr, bool (*fnYesNo)() = nullptr, size_t selectedItem = 0);
void UiRenderListItems(); void UiRenderListItems();
void UiInitList_clear(); void UiInitList_clear();

29
Source/DiabloUI/hero/selhero.cpp

@ -79,6 +79,26 @@ void SelheroClassSelectorSelect(size_t value);
void SelheroClassSelectorEsc(); void SelheroClassSelectorEsc();
const char *SelheroGenerateName(HeroClass heroClass); const char *SelheroGenerateName(HeroClass heroClass);
std::string_view HeroClassDescriptionForSpeech(HeroClass heroClass)
{
switch (heroClass) {
case HeroClass::Warrior:
return _("A powerful fighter who excels in melee combat.");
case HeroClass::Rogue:
return _("A nimble archer who excels at ranged combat.");
case HeroClass::Sorcerer:
return _("A master of arcane magic who casts powerful spells.");
case HeroClass::Monk:
return _("A holy warrior skilled in martial arts and staves.");
case HeroClass::Bard:
return _("A versatile fighter who blends melee and archery.");
case HeroClass::Barbarian:
return _("A fierce warrior who relies on brute strength.");
default:
return {};
}
}
void SelheroUiFocusNavigationYesNo() void SelheroUiFocusNavigationYesNo()
{ {
if (selhero_isSavegame) if (selhero_isSavegame)
@ -263,6 +283,15 @@ void SelheroClassSelectorFocus(size_t value)
selhero_heroInfo.vitality = defaults.vitality; selhero_heroInfo.vitality = defaults.vitality;
SelheroSetStats(); SelheroSetStats();
const PlayerData &playerData = GetPlayerDataForClass(heroClass);
const std::string_view description = HeroClassDescriptionForSpeech(heroClass);
std::string spoken = std::string(_(playerData.className));
if (!description.empty()) {
spoken.append("\n");
spoken.append(description);
}
UiSetSpokenTextOverride(std::move(spoken));
} }
bool ShouldPrefillHeroName() bool ShouldPrefillHeroName()

20
Source/DiabloUI/multi/selgame.cpp

@ -411,21 +411,35 @@ void selgame_GameSelection_Esc()
void selgame_Diff_Focus(size_t value) void selgame_Diff_Focus(size_t value)
{ {
std::string_view tooltip;
switch (vecSelGameDlgItems[value]->m_value) { switch (vecSelGameDlgItems[value]->m_value) {
case DIFF_NORMAL: case DIFF_NORMAL:
CopyUtf8(selgame_Label, _("Normal"), sizeof(selgame_Label)); CopyUtf8(selgame_Label, _("Normal"), sizeof(selgame_Label));
CopyUtf8(selgame_Description, _("Normal Difficulty\nThis is where a starting character should begin the quest to defeat Diablo."), sizeof(selgame_Description)); tooltip = _("Normal Difficulty\nThis is where a starting character should begin the quest to defeat Diablo.");
CopyUtf8(selgame_Description, tooltip, sizeof(selgame_Description));
break; break;
case DIFF_NIGHTMARE: case DIFF_NIGHTMARE:
CopyUtf8(selgame_Label, _("Nightmare"), sizeof(selgame_Label)); CopyUtf8(selgame_Label, _("Nightmare"), sizeof(selgame_Label));
CopyUtf8(selgame_Description, _("Nightmare Difficulty\nThe denizens of the Labyrinth have been bolstered and will prove to be a greater challenge. This is recommended for experienced characters only."), sizeof(selgame_Description)); tooltip = _("Nightmare Difficulty\nThe denizens of the Labyrinth have been bolstered and will prove to be a greater challenge. This is recommended for experienced characters only.");
CopyUtf8(selgame_Description, tooltip, sizeof(selgame_Description));
break; break;
case DIFF_HELL: case DIFF_HELL:
CopyUtf8(selgame_Label, _("Hell"), sizeof(selgame_Label)); CopyUtf8(selgame_Label, _("Hell"), sizeof(selgame_Label));
CopyUtf8(selgame_Description, _("Hell Difficulty\nThe most powerful of the underworld's creatures lurk at the gateway into Hell. Only the most experienced characters should venture in this realm."), sizeof(selgame_Description)); tooltip = _("Hell Difficulty\nThe most powerful of the underworld's creatures lurk at the gateway into Hell. Only the most experienced characters should venture in this realm.");
CopyUtf8(selgame_Description, tooltip, sizeof(selgame_Description));
break; break;
} }
CopyUtf8(selgame_Description, WordWrapString(selgame_Description, DESCRIPTION_WIDTH), sizeof(selgame_Description)); CopyUtf8(selgame_Description, WordWrapString(selgame_Description, DESCRIPTION_WIDTH), sizeof(selgame_Description));
std::string spoken = selgame_Label;
std::string_view spokenDescription = tooltip;
if (const size_t newlinePos = spokenDescription.find('\n'); newlinePos != std::string_view::npos)
spokenDescription = spokenDescription.substr(newlinePos + 1);
if (!spokenDescription.empty()) {
spoken.append("\n");
spoken.append(spokenDescription);
}
UiSetSpokenTextOverride(std::move(spoken));
} }
bool IsDifficultyAllowed(int value) bool IsDifficultyAllowed(int value)

24
Translations/pl.po

@ -5409,6 +5409,30 @@ msgstr "Barda"
msgid "Barbarian" msgid "Barbarian"
msgstr "Barbarzyńca" msgstr "Barbarzyńca"
#: Source/DiabloUI/hero/selhero.cpp:86
msgid "A powerful fighter who excels in melee combat."
msgstr "Potężny wojownik, który świetnie radzi sobie w walce wręcz."
#: Source/DiabloUI/hero/selhero.cpp:88
msgid "A nimble archer who excels at ranged combat."
msgstr "Zwinna łuczniczka, która świetnie radzi sobie w walce na dystans."
#: Source/DiabloUI/hero/selhero.cpp:90
msgid "A master of arcane magic who casts powerful spells."
msgstr "Mistrz magii tajemnej, który rzuca potężne zaklęcia."
#: Source/DiabloUI/hero/selhero.cpp:92
msgid "A holy warrior skilled in martial arts and staves."
msgstr "Święty wojownik biegły w sztukach walki i władaniu kosturami."
#: Source/DiabloUI/hero/selhero.cpp:94
msgid "A versatile fighter who blends melee and archery."
msgstr "Wszechstronna wojowniczka, która łączy walkę wręcz i łucznictwo."
#: Source/DiabloUI/hero/selhero.cpp:96
msgid "A fierce warrior who relies on brute strength."
msgstr "Zaciekły wojownik, który polega na brutalnej sile."
#: Source/translation_dummy.cpp:17 #: Source/translation_dummy.cpp:17
msgctxt "monster" msgctxt "monster"
msgid "Zombie" msgid "Zombie"

Loading…
Cancel
Save