Browse Source

Migrate gold drop/withdrawal to `text_input`

With this, all text input is handled in a unified way.
pull/6741/head
Gleb Mazovetskiy 2 years ago
parent
commit
09ab58343c
  1. 102
      Source/DiabloUI/text_input.cpp
  2. 82
      Source/DiabloUI/text_input.hpp
  3. 108
      Source/control.cpp
  4. 12
      Source/control.h
  5. 42
      Source/diablo.cpp
  6. 4
      Source/engine/render/scrollrt.cpp
  7. 5
      Source/engine/render/text_render.hpp
  8. 33
      Source/inv.cpp
  9. 5
      Source/objects.cpp
  10. 77
      Source/qol/stash.cpp
  11. 4
      Source/qol/stash.h

102
Source/DiabloUI/text_input.cpp

@ -1,20 +1,29 @@
#include "DiabloUI/text_input.hpp"
#include <memory>
#include <string>
#include <string_view>
#include <SDL.h>
#include <function_ref.hpp>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
#include "utils/log.hpp"
#include "utils/parse_int.hpp"
#include "utils/sdl_ptrs.h"
#include "utils/str_cat.hpp"
#include "utils/utf8.hpp"
namespace devilution {
bool HandleTextInputEvent(const SDL_Event &event, TextInputState &state)
namespace {
bool HandleInputEvent(const SDL_Event &event, TextInputState &state,
tl::function_ref<bool(std::string_view)> typeFn,
[[maybe_unused]] tl::function_ref<bool(std::string_view)> assignFn)
{
switch (event.type) {
case SDL_KEYDOWN: {
@ -27,7 +36,7 @@ bool HandleTextInputEvent(const SDL_Event &event, TextInputState &state)
if (clipboard == nullptr || *clipboard == '\0') {
Log("{}", SDL_GetError());
} else {
state.type(clipboard.get());
typeFn(clipboard.get());
}
}
}
@ -58,7 +67,7 @@ bool HandleTextInputEvent(const SDL_Event &event, TextInputState &state)
if ((event.key.keysym.mod & KMOD_CTRL) == 0 && event.key.keysym.unicode >= ' ') {
std::string utf8;
AppendUtf8(event.key.keysym.unicode, utf8);
state.type(utf8);
typeFn(utf8);
return true;
}
#endif
@ -67,15 +76,98 @@ bool HandleTextInputEvent(const SDL_Event &event, TextInputState &state)
#ifndef USE_SDL1
case SDL_TEXTINPUT:
#ifdef __vita__
state.assign(event.text.text);
assignFn(event.text.text);
#else
state.type(event.text.text);
typeFn(event.text.text);
#endif
return true;
case SDL_TEXTEDITING:
return true;
#endif
default:
return false;
}
}
} // namespace
bool HandleTextInputEvent(const SDL_Event &event, TextInputState &state)
{
return HandleInputEvent(
event, state, [&](std::string_view str) {
state.type(str);
return true; },
[&](std::string_view str) {
state.assign(str);
return true;
});
}
[[nodiscard]] int NumberInputState::value(int defaultValue) const
{
return ParseInt<int>(textInput_.value()).value_or(defaultValue);
}
std::string NumberInputState::filterStr(std::string_view str, bool allowMinus)
{
std::string result;
if (allowMinus && !str.empty() && str[0] == '-') {
str.remove_prefix(1);
result += '-';
}
for (const char c : str) {
if (c >= '0' && c <= '9') {
result += c;
}
}
return result;
}
void NumberInputState::type(std::string_view str)
{
const std::string filtered = filterStr(
str, /*allowMinus=*/min_ < 0 && textInput_.cursorPosition() == 0);
if (filtered.empty())
return;
textInput_.type(filtered);
enforceRange();
}
void NumberInputState::assign(std::string_view str)
{
const std::string filtered = filterStr(str, /*allowMinus=*/min_ < 0);
if (filtered.empty()) {
textInput_.clear();
return;
}
textInput_.assign(filtered);
enforceRange();
}
void NumberInputState::enforceRange()
{
if (textInput_.empty())
return;
ParseIntResult<int> parsed = ParseInt<int>(textInput_.value());
if (parsed.has_value()) {
if (*parsed > max_) {
textInput_.assign(StrCat(max_));
} else if (*parsed < min_) {
textInput_.assign(StrCat(min_));
}
}
}
bool HandleNumberInputEvent(const SDL_Event &event, NumberInputState &state)
{
return HandleInputEvent(
event, state.textInput(), [&](std::string_view str) {
state.type(str);
return true; },
[&](std::string_view str) {
state.assign(str);
return true;
});
}
} // namespace devilution

82
Source/DiabloUI/text_input.hpp

@ -3,6 +3,7 @@
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <string>
#include <string_view>
#include <SDL.h>
@ -65,6 +66,12 @@ class TextInputState {
buf_[len_] = '\0';
}
void clear()
{
len_ = 0;
buf_[0] = '\0';
}
explicit operator std::string_view() const
{
return { buf_, len_ };
@ -112,6 +119,11 @@ public:
return value_.empty();
}
[[nodiscard]] size_t cursorPosition() const
{
return *cursorPosition_;
}
/**
* @brief Overwrites the value with the given text and moves cursor to the end.
*/
@ -121,6 +133,12 @@ public:
*cursorPosition_ = value_.size();
}
void clear()
{
value_.clear();
*cursorPosition_ = 0;
}
/**
* @brief Truncate to precisely `length` bytes.
*/
@ -165,7 +183,7 @@ public:
void setCursorToEnd()
{
*cursorPosition_ = std::string_view(value_).size();
*cursorPosition_ = value_.size();
}
void moveCursorLeft()
@ -185,18 +203,76 @@ public:
private:
[[nodiscard]] std::string_view beforeCursor() const
{
return std::string_view(value_).substr(0, *cursorPosition_);
return value().substr(0, *cursorPosition_);
}
[[nodiscard]] std::string_view afterCursor() const
{
return std::string_view(value_).substr(*cursorPosition_);
return value().substr(*cursorPosition_);
}
Buffer value_;
size_t *cursorPosition_; // unowned
};
/**
* @brief Manages state for a number input with a cursor.
*/
class NumberInputState {
public:
struct Options {
TextInputState::Options textOptions;
int min;
int max;
};
NumberInputState(const Options &options)
: textInput_(options.textOptions)
, min_(options.min)
, max_(options.max)
{
}
[[nodiscard]] bool empty() const
{
return textInput_.empty();
}
[[nodiscard]] int value(int defaultValue = 0) const;
[[nodiscard]] int max() const
{
return max_;
}
/**
* @brief Inserts the text at the current cursor position.
*
* Ignores non-numeric characters.
*/
void type(std::string_view str);
/**
* @brief Sets the text of the input.
*
* Ignores non-numeric characters.
*/
void assign(std::string_view str);
TextInputState &textInput()
{
return textInput_;
}
private:
void enforceRange();
std::string filterStr(std::string_view str, bool allowMinus);
TextInputState textInput_;
int min_;
int max_;
};
bool HandleTextInputEvent(const SDL_Event &event, TextInputState &state);
bool HandleNumberInputEvent(const SDL_Event &event, NumberInputState &state);
} // namespace devilution

108
Source/control.cpp

@ -67,19 +67,23 @@
namespace devilution {
bool dropGoldFlag;
size_t GoldDropCursorPosition;
char GoldDropText[21];
namespace {
int8_t GoldDropInvIndex;
std::optional<NumberInputState> GoldDropInputState;
} // namespace
bool chrbtn[4];
bool lvlbtndown;
int dropGoldValue;
bool chrbtnactive;
UiFlags InfoColor;
int sbooktab;
int8_t initialDropGoldIndex;
bool talkflag;
bool sbookflag;
bool chrflag;
StringOrView InfoString;
bool panelflag;
int initialDropGoldValue;
bool panbtndown;
bool spselflag;
Rectangle MainPanel;
@ -625,10 +629,10 @@ void ControlUpDown(int v)
}
}
void RemoveGold(Player &player, int goldIndex)
void RemoveGold(Player &player, int goldIndex, int amount)
{
int gi = goldIndex - INVITEM_INV_FIRST;
player.InvList[gi]._ivalue -= dropGoldValue;
const int gi = goldIndex - INVITEM_INV_FIRST;
player.InvList[gi]._ivalue -= amount;
if (player.InvList[gi]._ivalue > 0) {
SetPlrHandGoldCurs(player.InvList[gi]);
NetSyncInvItem(player, gi);
@ -636,11 +640,10 @@ void RemoveGold(Player &player, int goldIndex)
player.RemoveInvItem(gi);
}
MakeGoldStack(player.HoldItem, dropGoldValue);
MakeGoldStack(player.HoldItem, amount);
NewCursor(player.HoldItem);
player._pGold = CalculateGold(player);
dropGoldValue = 0;
}
bool IsLevelUpButtonVisible()
@ -914,10 +917,6 @@ void InitControlPan()
pGBoxBuff = LoadCel("ctrlpan\\golddrop", 261);
}
CloseGoldDrop();
dropGoldValue = 0;
initialDropGoldValue = 0;
initialDropGoldIndex = 0;
CalculatePanelAreas();
if (!HeadlessMode)
@ -1143,17 +1142,11 @@ void CheckBtnUp()
CloseGoldWithdraw();
CloseStash();
invflag = !invflag;
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
break;
case PanelButtonSpellbook:
CloseInventory();
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
sbookflag = !sbookflag;
break;
case PanelButtonSendmsg:
@ -1367,19 +1360,23 @@ void RedBack(const Surface &out)
}
}
void DrawGoldSplit(const Surface &out, int amount)
void DrawGoldSplit(const Surface &out)
{
const int dialogX = 30;
ClxDraw(out, GetPanelPosition(UiPanels::Inventory, { dialogX, 178 }), (*pGBoxBuff)[0]);
const std::string_view amountText = GoldDropText;
const size_t cursorPosition = GoldDropCursorPosition;
const int max = GetGoldDropMax();
const std::string description = fmt::format(
fmt::runtime(ngettext(
/* TRANSLATORS: {:s} is a number with separators. Dialog is shown when splitting a stash of Gold.*/
"You have {:s} gold piece. How many do you want to remove?",
"You have {:s} gold pieces. How many do you want to remove?",
initialDropGoldValue)),
FormatInteger(initialDropGoldValue));
max)),
FormatInteger(max));
// Pre-wrap the string at spaces, otherwise DrawString would hard wrap in the middle of words
const std::string wrapped = WordWrapString(description, 200);
@ -1389,13 +1386,10 @@ void DrawGoldSplit(const Surface &out, int amount)
// for the text entered by the player.
DrawString(out, wrapped, { GetPanelPosition(UiPanels::Inventory, { dialogX + 31, 75 }), { 200, 50 } }, UiFlags::ColorWhitegold | UiFlags::AlignCenter, 1, 17);
std::string value;
if (amount > 0) {
value = StrCat(amount);
}
// Even a ten digit amount of gold only takes up about half a line. There's no need to wrap or clip text here so we
// use the Point form of DrawString.
DrawString(out, value, GetPanelPosition(UiPanels::Inventory, { dialogX + 37, 128 }), UiFlags::ColorWhite | UiFlags::PentaCursor);
DrawString(out, amountText, GetPanelPosition(UiPanels::Inventory, { dialogX + 37, 128 }),
UiFlags::ColorWhite | UiFlags::PentaCursor, /*spacing=*/1, /*lineHeight=*/-1, cursorPosition);
}
void control_drop_gold(SDL_Keycode vkey)
@ -1404,19 +1398,22 @@ void control_drop_gold(SDL_Keycode vkey)
if (myPlayer._pHitPoints >> 6 <= 0) {
CloseGoldDrop();
dropGoldValue = 0;
return;
}
if (vkey == SDLK_RETURN || vkey == SDLK_KP_ENTER) {
if (dropGoldValue > 0)
RemoveGold(myPlayer, initialDropGoldIndex);
switch (vkey) {
case SDLK_RETURN:
case SDLK_KP_ENTER:
if (const int value = GoldDropInputState->value(); value != 0) {
RemoveGold(myPlayer, GoldDropInvIndex, value);
}
CloseGoldDrop();
} else if (vkey == SDLK_ESCAPE) {
break;
case SDLK_ESCAPE:
CloseGoldDrop();
dropGoldValue = 0;
} else if (vkey == SDLK_BACKSPACE) {
dropGoldValue = dropGoldValue / 10;
break;
default:
break;
}
}
@ -1626,26 +1623,41 @@ void DiabloHotkeyMsg(uint32_t dwMsg)
}
}
void OpenGoldDrop(int8_t invIndex, int max)
{
dropGoldFlag = true;
GoldDropInvIndex = invIndex;
GoldDropText[0] = '\0';
GoldDropInputState.emplace(NumberInputState::Options {
.textOptions {
.value = GoldDropText,
.cursorPosition = &GoldDropCursorPosition,
.maxLength = sizeof(GoldDropText) - 1,
},
.min = 0,
.max = max,
});
SDL_StartTextInput();
}
void CloseGoldDrop()
{
if (!dropGoldFlag)
return;
dropGoldFlag = false;
SDL_StopTextInput();
dropGoldFlag = false;
GoldDropInputState = std::nullopt;
GoldDropInvIndex = 0;
}
void GoldDropNewText(std::string_view text)
int GetGoldDropMax()
{
for (char vkey : text) {
int digit = vkey - '0';
if (digit >= 0 && digit <= 9) {
int newGoldValue = dropGoldValue * 10;
newGoldValue += digit;
if (newGoldValue <= initialDropGoldValue) {
dropGoldValue = newGoldValue;
}
}
}
return GoldDropInputState->max();
}
bool HandleGoldDropTextInputEvent(const SDL_Event &event)
{
return HandleNumberInputEvent(event, *GoldDropInputState);
}
} // namespace devilution

12
Source/control.h

@ -33,19 +33,19 @@ namespace devilution {
constexpr Size SidePanelSize { 320, 352 };
extern bool dropGoldFlag;
extern size_t GoldDropCursorPosition;
extern char GoldDropText[21];
extern bool chrbtn[4];
extern bool lvlbtndown;
extern int dropGoldValue;
extern bool chrbtnactive;
extern UiFlags InfoColor;
extern int sbooktab;
extern int8_t initialDropGoldIndex;
extern bool talkflag;
extern bool sbookflag;
extern bool chrflag;
extern StringOrView InfoString;
extern bool panelflag;
extern int initialDropGoldValue;
extern bool panbtndown;
extern bool spselflag;
const Rectangle &GetMainPanel();
@ -180,7 +180,7 @@ void ReleaseChrBtns(bool addAllStatPoints);
void DrawDurIcon(const Surface &out);
void RedBack(const Surface &out);
void DrawSpellBook(const Surface &out);
void DrawGoldSplit(const Surface &out, int amount);
void DrawGoldSplit(const Surface &out);
void control_drop_gold(SDL_Keycode vkey);
void DrawTalkPan(const Surface &out);
bool control_check_talk_btn();
@ -191,8 +191,10 @@ bool IsTalkActive();
bool HandleTalkTextInputEvent(const SDL_Event &event);
bool control_presskeys(SDL_Keycode vkey);
void DiabloHotkeyMsg(uint32_t dwMsg);
void OpenGoldDrop(int8_t invIndex, int max);
void CloseGoldDrop();
void GoldDropNewText(std::string_view text);
int GetGoldDropMax();
bool HandleGoldDropTextInputEvent(const SDL_Event &event);
extern Rectangle ChrBtnsRect[4];
} // namespace devilution

42
Source/diablo.cpp

@ -677,19 +677,6 @@ void HandleMouseButtonUp(Uint8 button, uint16_t modState)
}
}
bool HandleTextInput(std::string_view text)
{
if (dropGoldFlag) {
GoldDropNewText(text);
return true;
}
if (IsWithdrawGoldOpen) {
GoldWithdrawNewText(text);
return true;
}
return false;
}
[[maybe_unused]] void LogUnhandledEvent(const char *name, int value)
{
LogVerbose("Unhandled SDL event: {} {}", name, value);
@ -722,35 +709,20 @@ void GameEventHandler(const SDL_Event &event, uint16_t modState)
if (IsTalkActive() && HandleTalkTextInputEvent(event)) {
return;
}
if (dropGoldFlag && HandleGoldDropTextInputEvent(event)) {
return;
}
if (IsWithdrawGoldOpen && HandleGoldWithdrawTextInputEvent(event)) {
return;
}
switch (event.type) {
case SDL_KEYDOWN: {
#ifdef USE_SDL1
// SDL1 does not support TEXTINPUT events, so we emulate them here.
const Uint16 bmpCodePoint = event.key.keysym.unicode;
if (bmpCodePoint >= ' ') {
std::string utf8;
AppendUtf8(bmpCodePoint, utf8);
if (HandleTextInput(utf8)) {
return;
}
}
#endif
case SDL_KEYDOWN:
PressKey(event.key.keysym.sym, modState);
return;
}
case SDL_KEYUP:
ReleaseKey(event.key.keysym.sym);
return;
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_TEXTEDITING:
return;
case SDL_TEXTINPUT:
if (!HandleTextInput(event.text.text)) {
LogUnhandledEvent("SDL_TEXTINPUT", event.text.windowID);
}
return;
#endif
case SDL_MOUSEMOTION:
if (ControlMode == ControlTypes::KeyboardAndMouse && invflag)
InvalidateInventorySlot();

4
Source/engine/render/scrollrt.cpp

@ -1185,9 +1185,9 @@ void DrawView(const Surface &out, Point startPosition)
DrawSpellList(out);
}
if (dropGoldFlag) {
DrawGoldSplit(out, dropGoldValue);
DrawGoldSplit(out);
}
DrawGoldWithdraw(out, WithdrawGoldValue);
DrawGoldWithdraw(out);
if (HelpFlag) {
DrawHelp(out);
}

5
Source/engine/render/text_render.hpp

@ -188,11 +188,12 @@ uint32_t DrawString(const Surface &out, std::string_view text, const Rectangle &
* @param flags A combination of UiFlags to describe font size, color, alignment, etc. See ui_items.h for available options
* @param spacing Additional space to add between characters.
* This value may be adjusted if the flag UIS_FIT_SPACING is passed in the flags parameter.
* @param cursorPosition If non-negative, draws a blinking cursor after the given byte index.
* @param lineHeight Allows overriding the default line height, useful for multi-line strings.
*/
inline void DrawString(const Surface &out, std::string_view text, const Point &position, UiFlags flags = UiFlags::None, int spacing = 1, int lineHeight = -1)
inline void DrawString(const Surface &out, std::string_view text, const Point &position, UiFlags flags = UiFlags::None, int spacing = 1, int lineHeight = -1, int cursorPosition = -1)
{
DrawString(out, text, { position, { out.w() - position.x, 0 } }, flags, spacing, lineHeight);
DrawString(out, text, { position, { out.w() - position.x, 0 } }, flags, spacing, lineHeight, cursorPosition);
}
/**

33
Source/inv.cpp

@ -566,10 +566,7 @@ void CheckInvCut(Player &player, Point cursorPosition, bool automaticMove, bool
return;
}
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
uint32_t r = 0;
for (; r < NUM_XY_SLOTS; r++) {
@ -956,14 +953,13 @@ void StartGoldDrop()
{
CloseGoldWithdraw();
initialDropGoldIndex = pcursinvitem;
const int8_t invIndex = pcursinvitem;
Player &myPlayer = *MyPlayer;
if (pcursinvitem <= INVITEM_INV_LAST)
initialDropGoldValue = myPlayer.InvList[pcursinvitem - INVITEM_INV_FIRST]._ivalue;
else
initialDropGoldValue = myPlayer.SpdList[pcursinvitem - INVITEM_BELT_FIRST]._ivalue;
const size_t max = (invIndex <= INVITEM_INV_LAST)
? myPlayer.InvList[invIndex - INVITEM_INV_FIRST]._ivalue
: myPlayer.SpdList[invIndex - INVITEM_BELT_FIRST]._ivalue;
if (talkflag)
control_reset_talk();
@ -972,9 +968,7 @@ void StartGoldDrop()
SDL_Rect rect = MakeSdlRect(start.x, start.y, 180, 20);
SDL_SetTextInputRect(&rect);
dropGoldFlag = true;
dropGoldValue = 0;
SDL_StartTextInput();
OpenGoldDrop(invIndex, max);
}
int CreateGoldItemInInventorySlot(Player &player, int slotIndex, int value)
@ -1555,10 +1549,7 @@ void CheckInvScrn(bool isShiftHeld, bool isCtrlHeld)
void InvGetItem(Player &player, int ii)
{
auto &item = Items[ii];
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
if (dItem[item.position.x][item.position.y] == 0)
return;
@ -1629,10 +1620,7 @@ void AutoGetItem(Player &player, Item *itemPointer, int ii)
{
Item &item = *itemPointer;
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
if (dItem[item.position.x][item.position.y] == 0)
return;
@ -2057,10 +2045,7 @@ bool UseInvItem(int cii)
return true;
}
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
if (item->isScroll() && leveltype == DTYPE_TOWN && !GetSpellData(item->_iSpell).isAllowedInTown()) {
return true;

5
Source/objects.cpp

@ -2978,10 +2978,7 @@ void OperateShrine(Player &player, Object &shrine, _sfx_id sType)
if (shrine._oSelFlag == 0)
return;
if (dropGoldFlag) {
CloseGoldDrop();
dropGoldValue = 0;
}
CloseGoldDrop();
SetRndSeed(shrine._oRndSeed);
shrine._oSelFlag = 0;

77
Source/qol/stash.cpp

@ -6,6 +6,7 @@
#include <fmt/format.h>
#include "DiabloUI/text_input.hpp"
#include "control.h"
#include "controls/plrctrls.h"
#include "cursor.h"
@ -17,6 +18,7 @@
#include "engine/render/text_render.hpp"
#include "engine/size.hpp"
#include "hwcursor.hpp"
#include "inv.h"
#include "minitext.h"
#include "stores.h"
#include "utils/format_int.hpp"
@ -29,14 +31,15 @@ namespace devilution {
bool IsStashOpen;
StashStruct Stash;
bool IsWithdrawGoldOpen;
int WithdrawGoldValue;
namespace {
constexpr unsigned CountStashPages = 100;
constexpr unsigned LastStashPage = CountStashPages - 1;
int InitialWithdrawGoldValue;
char GoldWithdrawText[21];
size_t GoldWithdrawCursorPosition;
std::optional<NumberInputState> GoldWithdrawInputState;
constexpr Size ButtonSize { 27, 16 };
/** Contains mappings for the buttons in the stash (2 navigation buttons, withdraw gold buttons, 2 navigation buttons) */
@ -181,10 +184,7 @@ void CheckStashCut(Point cursorPosition, bool automaticMove)
{
Player &player = *MyPlayer;
if (IsWithdrawGoldOpen) {
IsWithdrawGoldOpen = false;
WithdrawGoldValue = 0;
}
CloseGoldWithdraw();
Point slot = InvalidStashPoint;
@ -276,8 +276,6 @@ void FreeStashGFX()
void InitStash()
{
InitialWithdrawGoldValue = 0;
if (!HeadlessMode) {
StashPanelArt = LoadClx("data\\stash.clx");
StashNavButtonArt = LoadClx("data\\stashnavbtns.clx");
@ -486,10 +484,7 @@ bool UseStashItem(uint16_t c)
return true;
}
if (IsWithdrawGoldOpen) {
IsWithdrawGoldOpen = false;
WithdrawGoldValue = 0;
}
CloseGoldWithdraw();
if (item->isScroll()) {
return true;
@ -589,8 +584,6 @@ void StartGoldWithdraw()
{
CloseGoldDrop();
InitialWithdrawGoldValue = std::min(RoomForGold(), Stash.gold);
if (talkflag)
control_reset_talk();
@ -599,7 +592,16 @@ void StartGoldWithdraw()
SDL_SetTextInputRect(&rect);
IsWithdrawGoldOpen = true;
WithdrawGoldValue = 0;
GoldWithdrawText[0] = '\0';
GoldWithdrawInputState.emplace(NumberInputState::Options {
.textOptions {
.value = GoldWithdrawText,
.cursorPosition = &GoldWithdrawCursorPosition,
.maxLength = sizeof(GoldWithdrawText) - 1,
},
.min = 0,
.max = std::min(RoomForGold(), Stash.gold),
});
SDL_StartTextInput();
}
@ -612,25 +614,32 @@ void WithdrawGoldKeyPress(SDL_Keycode vkey)
return;
}
if ((vkey == SDLK_RETURN) || (vkey == SDLK_KP_ENTER)) {
if (WithdrawGoldValue > 0) {
WithdrawGold(myPlayer, WithdrawGoldValue);
switch (vkey) {
case SDLK_RETURN:
case SDLK_KP_ENTER:
if (const int value = GoldWithdrawInputState->value(); value != 0) {
WithdrawGold(myPlayer, value);
PlaySFX(IS_GOLD);
}
CloseGoldWithdraw();
} else if (vkey == SDLK_ESCAPE) {
break;
case SDLK_ESCAPE:
CloseGoldWithdraw();
} else if (vkey == SDLK_BACKSPACE) {
WithdrawGoldValue /= 10;
break;
default:
break;
}
}
void DrawGoldWithdraw(const Surface &out, int amount)
void DrawGoldWithdraw(const Surface &out)
{
if (!IsWithdrawGoldOpen) {
return;
}
const std::string_view amountText = GoldWithdrawText;
const size_t cursorPosition = GoldWithdrawCursorPosition;
const int dialogX = 30;
ClxDraw(out, GetPanelPosition(UiPanels::Stash, { dialogX, 178 }), (*pGBoxBuff)[0]);
@ -643,36 +652,24 @@ void DrawGoldWithdraw(const Surface &out, int amount)
// for the text entered by the player.
DrawString(out, wrapped, { GetPanelPosition(UiPanels::Stash, { dialogX + 31, 75 }), { 200, 50 } }, UiFlags::ColorWhitegold | UiFlags::AlignCenter, 1, 17);
std::string value = "";
if (amount > 0) {
value = StrCat(amount);
}
// Even a ten digit amount of gold only takes up about half a line. There's no need to wrap or clip text here so we
// use the Point form of DrawString.
DrawString(out, value, GetPanelPosition(UiPanels::Stash, { dialogX + 37, 128 }), UiFlags::ColorWhite | UiFlags::PentaCursor);
DrawString(out, amountText, GetPanelPosition(UiPanels::Stash, { dialogX + 37, 128 }),
UiFlags::ColorWhite | UiFlags::PentaCursor, /*spacing=*/1, /*lineHeight=*/-1, cursorPosition);
}
void CloseGoldWithdraw()
{
if (!IsWithdrawGoldOpen)
return;
IsWithdrawGoldOpen = false;
WithdrawGoldValue = 0;
SDL_StopTextInput();
IsWithdrawGoldOpen = false;
GoldWithdrawInputState = std::nullopt;
}
void GoldWithdrawNewText(std::string_view text)
bool HandleGoldWithdrawTextInputEvent(const SDL_Event &event)
{
for (char vkey : text) {
int digit = vkey - '0';
if (digit >= 0 && digit <= 9) {
int newGoldValue = WithdrawGoldValue * 10;
newGoldValue += digit;
if (newGoldValue <= InitialWithdrawGoldValue) {
WithdrawGoldValue = newGoldValue;
}
}
}
return HandleNumberInputEvent(event, *GoldWithdrawInputState);
}
bool AutoPlaceItemInStash(Player &player, const Item &item, bool persistItem)

4
Source/qol/stash.h

@ -88,9 +88,9 @@ void CheckStashButtonPress(Point mousePosition);
void StartGoldWithdraw();
void WithdrawGoldKeyPress(SDL_Keycode vkey);
void DrawGoldWithdraw(const Surface &out, int amount);
void DrawGoldWithdraw(const Surface &out);
void CloseGoldWithdraw();
void GoldWithdrawNewText(std::string_view text);
bool HandleGoldWithdrawTextInputEvent(const SDL_Event &event);
/**
* @brief Checks whether the given item can be placed on the specified player's stash.

Loading…
Cancel
Save