You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
5.6 KiB

#include "control_flasks.hpp"
#include "control.hpp"
#include "engine/surface.hpp"
#include "utils/str_cat.hpp"
namespace devilution {
std::optional<OwnedSurface> pLifeBuff;
std::optional<OwnedSurface> pManaBuff;
namespace {
Rectangle FlaskTopRect { { 11, 3 }, { 62, 13 } };
Rectangle FlaskBottomRect { { 0, 16 }, { 88, 69 } };
/**
* Draws the dome of the flask that protrudes above the panel top line.
* It draws a rectangle of fixed width 59 and height 'h' from the source buffer
* into the target buffer.
* @param out The target buffer.
* @param celBuf Buffer of the flask cel.
* @param targetPosition Target buffer coordinate.
*/
void DrawFlaskAbovePanel(const Surface &out, const Surface &celBuf, Point targetPosition)
{
out.BlitFromSkipColorIndexZero(celBuf, MakeSdlRect(0, 0, celBuf.w(), celBuf.h()), targetPosition);
}
/**
* @brief Draws the part of the life/mana flasks protruding above the bottom panel
* @see DrawFlaskLower()
* @param out The display region to draw to
* @param sourceBuffer A sprite representing the appropriate background/empty flask style
* @param offset X coordinate offset for where the flask should be drawn
* @param fillPer How full the flask is (a value from 0 to 81)
*/
void DrawFlaskUpper(const Surface &out, const Surface &sourceBuffer, int offset, int fillPer)
{
const Rectangle &rect = FlaskTopRect;
const int emptyRows = std::clamp(81 - fillPer, 0, rect.size.height);
const int filledRows = rect.size.height - emptyRows;
// Draw the empty part of the flask
DrawFlaskAbovePanel(out,
sourceBuffer.subregion(rect.position.x, rect.position.y, rect.size.width, rect.size.height),
GetMainPanel().position + Displacement { offset, -rect.size.height });
// Draw the filled part of the flask over the empty part
if (filledRows > 0) {
DrawFlaskAbovePanel(out,
BottomBuffer->subregion(offset, rect.position.y + emptyRows, rect.size.width, filledRows),
GetMainPanel().position + Displacement { offset, -rect.size.height + emptyRows });
}
}
/**
* Draws a section of the empty flask cel on top of the panel to create the illusion
* of the flask getting empty. This function takes a cel and draws a
* horizontal stripe of height (max-min) onto the given buffer.
* @param out Target buffer.
* @param celBuf Buffer of the flask cel.
* @param targetPosition Target buffer coordinate.
*/
void DrawFlaskOnPanel(const Surface &out, const Surface &celBuf, Point targetPosition)
{
out.BlitFrom(celBuf, MakeSdlRect(0, 0, celBuf.w(), celBuf.h()), targetPosition);
}
/**
* @brief Draws the part of the life/mana flasks inside the bottom panel
* @see DrawFlaskUpper()
* @param out The display region to draw to
* @param sourceBuffer A sprite representing the appropriate background/empty flask style
* @param offset X coordinate offset for where the flask should be drawn
* @param fillPer How full the flask is (a value from 0 to 80)
* @param drawFilledPortion Indicates whether to draw the filled portion of the flask
*/
void DrawFlaskLower(const Surface &out, const Surface &sourceBuffer, int offset, int fillPer, bool drawFilledPortion)
{
const Rectangle &rect = FlaskBottomRect;
const int filledRows = std::clamp(fillPer, 0, rect.size.height);
const int emptyRows = rect.size.height - filledRows;
// Draw the empty part of the flask
if (emptyRows > 0) {
DrawFlaskOnPanel(out,
sourceBuffer.subregion(rect.position.x, rect.position.y, rect.size.width, emptyRows),
GetMainPanel().position + Displacement { offset, 0 });
}
// Draw the filled part of the flask
if (drawFilledPortion && filledRows > 0) {
DrawFlaskOnPanel(out,
BottomBuffer->subregion(offset, rect.position.y + emptyRows, rect.size.width, filledRows),
GetMainPanel().position + Displacement { offset, emptyRows });
}
}
} // namespace
void DrawLifeFlaskUpper(const Surface &out)
{
constexpr int LifeFlaskUpperOffset = 107;
DrawFlaskUpper(out, *pLifeBuff, LifeFlaskUpperOffset, MyPlayer->_pHPPer);
}
void DrawManaFlaskUpper(const Surface &out)
{
constexpr int ManaFlaskUpperOffset = 475;
DrawFlaskUpper(out, *pManaBuff, ManaFlaskUpperOffset, MyPlayer->_pManaPer);
}
void DrawLifeFlaskLower(const Surface &out, bool drawFilledPortion)
{
constexpr int LifeFlaskLowerOffset = 96;
DrawFlaskLower(out, *pLifeBuff, LifeFlaskLowerOffset, MyPlayer->_pHPPer, drawFilledPortion);
}
void DrawManaFlaskLower(const Surface &out, bool drawFilledPortion)
{
constexpr int ManaFlaskLowerOffset = 464;
DrawFlaskLower(out, *pManaBuff, ManaFlaskLowerOffset, MyPlayer->_pManaPer, drawFilledPortion);
}
void DrawFlaskValues(const Surface &out, Point pos, int currValue, int maxValue)
{
const UiFlags color = (currValue > 0 ? (currValue == maxValue ? UiFlags::ColorGold : UiFlags::ColorWhite) : UiFlags::ColorRed);
auto drawStringWithShadow = [out, color](std::string_view text, Point pos) {
DrawString(out, text, pos + Displacement { -1, -1 },
{ .flags = UiFlags::ColorBlack | UiFlags::KerningFitSpacing, .spacing = 0 });
DrawString(out, text, pos,
{ .flags = color | UiFlags::KerningFitSpacing, .spacing = 0 });
};
const std::string currText = StrCat(currValue);
drawStringWithShadow(currText, pos - Displacement { GetLineWidth(currText, GameFont12) + 1, 0 });
drawStringWithShadow("/", pos);
drawStringWithShadow(StrCat(maxValue), pos + Displacement { GetLineWidth("/", GameFont12) + 1, 0 });
}
void UpdateLifeManaPercent()
{
MyPlayer->UpdateManaPercentage();
MyPlayer->UpdateHitPointPercentage();
}
} // namespace devilution