Adding fancier XP bar (#1597)
This commit is contained in:
parent
c89bd9c001
commit
50dcf587f4
10 changed files with 237 additions and 51 deletions
|
|
@ -294,6 +294,8 @@ set(devilutionx_SRCS
|
|||
Source/controls/modifier_hints.cpp
|
||||
Source/controls/plrctrls.cpp
|
||||
Source/controls/touch.cpp
|
||||
Source/qol/common.cpp
|
||||
Source/qol/xpbar.cpp
|
||||
Source/utils/console.cpp
|
||||
Source/utils/display.cpp
|
||||
Source/utils/file_util.cpp
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -17,6 +17,7 @@
|
|||
#include "lighting.h"
|
||||
#include "minitext.h"
|
||||
#include "missiles.h"
|
||||
#include "qol/xpbar.h"
|
||||
#include "stores.h"
|
||||
#include "towners.h"
|
||||
#include "trigs.h"
|
||||
|
|
@ -1116,6 +1117,11 @@ void CheckPanelInfo()
|
|||
}
|
||||
if (MouseX > 190 + PANEL_LEFT && MouseX < 437 + PANEL_LEFT && MouseY > 4 + PANEL_TOP && MouseY < 33 + PANEL_TOP)
|
||||
pcursinvitem = CheckInvHLight();
|
||||
|
||||
if (CheckXPBarInfo()) {
|
||||
panelflag = true;
|
||||
pinfoflag = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include "cursor.h"
|
||||
#include "DiabloUI/art_draw.h"
|
||||
#include "options.h"
|
||||
#include "qol/common.h"
|
||||
#include "qol/xpbar.h"
|
||||
|
||||
namespace devilution {
|
||||
namespace {
|
||||
|
|
@ -29,11 +31,6 @@ int GetTextWidth(const char *s)
|
|||
return l;
|
||||
}
|
||||
|
||||
void FastDrawHorizLine(CelOutputBuffer out, int x, int y, int width, BYTE col)
|
||||
{
|
||||
memset(out.at(x, y), col, width);
|
||||
}
|
||||
|
||||
void FastDrawVertLine(CelOutputBuffer out, int x, int y, int height, BYTE col)
|
||||
{
|
||||
BYTE *p = out.at(x, y);
|
||||
|
|
@ -43,19 +40,14 @@ void FastDrawVertLine(CelOutputBuffer out, int x, int y, int height, BYTE col)
|
|||
}
|
||||
}
|
||||
|
||||
void FillRect(CelOutputBuffer out, int x, int y, int width, int height, BYTE col)
|
||||
{
|
||||
for (int j = 0; j < height; j++) {
|
||||
FastDrawHorizLine(out, x, y + j, width, col);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void FreeQol()
|
||||
{
|
||||
delete qolArt;
|
||||
qolArt = nullptr;
|
||||
|
||||
FreeXPBar();
|
||||
}
|
||||
|
||||
void InitQol()
|
||||
|
|
@ -72,6 +64,8 @@ void InitQol()
|
|||
app_fatal("Failed to load UI resources. Is devilutionx.mpq accessible and up to date?");
|
||||
}
|
||||
}
|
||||
|
||||
InitXPBar();
|
||||
}
|
||||
|
||||
void DrawMonsterHealthBar(CelOutputBuffer out)
|
||||
|
|
@ -152,45 +146,7 @@ void DrawMonsterHealthBar(CelOutputBuffer out)
|
|||
}
|
||||
}
|
||||
|
||||
void DrawXPBar(CelOutputBuffer out)
|
||||
{
|
||||
if (!sgOptions.Gameplay.bExperienceBar)
|
||||
return;
|
||||
|
||||
int barWidth = 306;
|
||||
int barHeight = 5;
|
||||
int yPos = gnScreenHeight - 9; // y position of xp bar
|
||||
int xPos = (gnScreenWidth - barWidth) / 2 + 5; // x position of xp bar
|
||||
int dividerHeight = 3;
|
||||
int numDividers = 10;
|
||||
int barColor = 198;
|
||||
int emptyBarColor = 0;
|
||||
int frameColor = 196;
|
||||
bool space = true; // add 1 pixel separator on top/bottom of the bar
|
||||
|
||||
PrintGameStr(out, xPos - 22, yPos + 6, "XP", COL_WHITE);
|
||||
int charLevel = plr[myplr]._pLevel;
|
||||
if (charLevel == MAXCHARLEVEL - 1)
|
||||
return;
|
||||
|
||||
int prevXp = ExpLvlsTbl[charLevel - 1];
|
||||
if (plr[myplr]._pExperience < prevXp)
|
||||
return;
|
||||
|
||||
Uint64 prevXpDelta_1 = plr[myplr]._pExperience - prevXp;
|
||||
int prevXpDelta = ExpLvlsTbl[charLevel] - prevXp;
|
||||
int visibleBar = barWidth * prevXpDelta_1 / prevXpDelta;
|
||||
|
||||
FillRect(out, xPos, yPos, barWidth, barHeight, emptyBarColor);
|
||||
FastDrawHorizLine(out, xPos - 1, yPos - 1, barWidth + 2, frameColor);
|
||||
FastDrawHorizLine(out, xPos - 1, yPos + barHeight, barWidth + 2, frameColor);
|
||||
FastDrawVertLine(out, xPos - 1, yPos - 1, barHeight + 2, frameColor);
|
||||
FastDrawVertLine(out, xPos + barWidth, yPos - 1, barHeight + 2, frameColor);
|
||||
for (int i = 1; i < numDividers; i++)
|
||||
FastDrawVertLine(out, xPos - 1 + (barWidth * i / numDividers), yPos - dividerHeight + 3, barHeight, 245);
|
||||
|
||||
FillRect(out, xPos, yPos + (space ? 1 : 0), visibleBar, barHeight - (space ? 2 : 0), barColor);
|
||||
}
|
||||
|
||||
bool HasRoomForGold()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ namespace devilution {
|
|||
void FreeQol();
|
||||
void InitQol();
|
||||
void DrawMonsterHealthBar(CelOutputBuffer out);
|
||||
void DrawXPBar(CelOutputBuffer out);
|
||||
void AutoGoldPickup(int pnum);
|
||||
|
||||
} // namespace devilution
|
||||
|
|
|
|||
27
Source/qol/common.cpp
Normal file
27
Source/qol/common.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* @file common.h
|
||||
*
|
||||
* Common functions for QoL features
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "engine.h"
|
||||
|
||||
namespace devilution {
|
||||
|
||||
void FastDrawHorizLine(const CelOutputBuffer &out, int x, int y, int width, BYTE col)
|
||||
{
|
||||
memset(out.at(x, y), col, width);
|
||||
}
|
||||
|
||||
char *PrintWithSeparator(char *out, long long n)
|
||||
{
|
||||
if (n < 1000) {
|
||||
return out + sprintf(out, "%lld", n);
|
||||
}
|
||||
|
||||
char *append = PrintWithSeparator(out, n / 1000);
|
||||
return append + sprintf(append, ",%03lld", n % 1000);
|
||||
}
|
||||
|
||||
} // namespace devilution
|
||||
23
Source/qol/common.h
Normal file
23
Source/qol/common.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @file common.h
|
||||
*
|
||||
* Common functions for QoL features
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "SDL_stdinc.h" // for Uint8
|
||||
|
||||
namespace devilution {
|
||||
|
||||
struct CelOutputBuffer;
|
||||
|
||||
void FastDrawHorizLine(const CelOutputBuffer &out, int x, int y, int width, Uint8 col);
|
||||
/**
|
||||
* @brief Prints integer into buffer, using ',' as thousands separator.
|
||||
* @param out Destination buffer
|
||||
* @param n Number to print
|
||||
* @return Address of first character after printed number
|
||||
*/
|
||||
char *PrintWithSeparator(char *out, long long n);
|
||||
|
||||
} // namespace devilution
|
||||
154
Source/qol/xpbar.cpp
Normal file
154
Source/qol/xpbar.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/**
|
||||
* @file xpbar.cpp
|
||||
*
|
||||
* Adds XP bar QoL feature
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "control.h"
|
||||
#include "DiabloUI/art_draw.h"
|
||||
#include "options.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace devilution {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int BAR_WIDTH = 307;
|
||||
constexpr int BAR_HEIGHT = 5;
|
||||
|
||||
using ColorGradient = std::array<Uint8, 12>;
|
||||
constexpr ColorGradient GOLD_GRADIENT = { 0xCF, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA, 0xC9, 0xC8, 0xC7, 0xC6, 0xC5, 0xC4 };
|
||||
constexpr ColorGradient SILVER_GRADIENT = { 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3 };
|
||||
|
||||
constexpr int BACK_WIDTH = 313;
|
||||
constexpr int BACK_HEIGHT = 9;
|
||||
|
||||
Art xpbarArt;
|
||||
|
||||
void DrawBar(const CelOutputBuffer &out, int x, int y, int width, const ColorGradient &gradient)
|
||||
{
|
||||
FastDrawHorizLine(out, x, y + 1, width, gradient[gradient.size() * 3 / 4 - 1]);
|
||||
FastDrawHorizLine(out, x, y + 2, width, gradient[gradient.size() - 1]);
|
||||
FastDrawHorizLine(out, x, y + 3, width, gradient[gradient.size() / 2 - 1]);
|
||||
}
|
||||
|
||||
void DrawEndCap(const CelOutputBuffer &out, int x, int y, int idx, const ColorGradient &gradient)
|
||||
{
|
||||
SetPixel(out, x, y + 1, gradient[idx * 3 / 4]);
|
||||
SetPixel(out, x, y + 2, gradient[idx]);
|
||||
SetPixel(out, x, y + 3, gradient[idx / 2]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void InitXPBar()
|
||||
{
|
||||
if (sgOptions.Gameplay.bExperienceBar) {
|
||||
LoadMaskedArt("data\\xpbar.pcx", &xpbarArt, 1, 1);
|
||||
|
||||
if (xpbarArt.surface == nullptr) {
|
||||
app_fatal("Failed to load UI resources. Is devilutionx.mpq accessible and up to date?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeXPBar()
|
||||
{
|
||||
xpbarArt.Unload();
|
||||
}
|
||||
|
||||
void DrawXPBar(const CelOutputBuffer &out)
|
||||
{
|
||||
if (!sgOptions.Gameplay.bExperienceBar)
|
||||
return;
|
||||
|
||||
const PlayerStruct &player = plr[myplr];
|
||||
|
||||
const int backX = PANEL_LEFT + PANEL_WIDTH / 2 - 155;
|
||||
const int backY = PANEL_TOP + PANEL_HEIGHT - 11;
|
||||
|
||||
const int xPos = backX + 3;
|
||||
const int yPos = backY + 2;
|
||||
|
||||
DrawArt(out, backX, backY, &xpbarArt);
|
||||
|
||||
const int charLevel = player._pLevel;
|
||||
|
||||
if (charLevel == MAXCHARLEVEL - 1) {
|
||||
// Draw a nice golden bar for max level characters.
|
||||
DrawBar(out, xPos, yPos, BAR_WIDTH, GOLD_GRADIENT);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const int prevXp = ExpLvlsTbl[charLevel - 1];
|
||||
if (player._pExperience < prevXp)
|
||||
return;
|
||||
|
||||
Uint64 prevXpDelta_1 = player._pExperience - prevXp;
|
||||
Uint64 prevXpDelta = ExpLvlsTbl[charLevel] - prevXp;
|
||||
Uint64 fullBar = BAR_WIDTH * prevXpDelta_1 / prevXpDelta;
|
||||
|
||||
// Figure out how much to fill the last pixel of the XP bar, to make it gradually appear with gained XP
|
||||
Uint64 onePx = prevXpDelta / BAR_WIDTH;
|
||||
Uint64 lastFullPx = fullBar * prevXpDelta / BAR_WIDTH;
|
||||
|
||||
const Uint64 fade = (prevXpDelta_1 - lastFullPx) * (SILVER_GRADIENT.size() - 1) / onePx;
|
||||
|
||||
// Draw beginning of bar full brightness
|
||||
DrawBar(out, xPos, yPos, fullBar, SILVER_GRADIENT);
|
||||
|
||||
// End pixels appear gradually
|
||||
DrawEndCap(out, xPos + fullBar, yPos, fade, SILVER_GRADIENT);
|
||||
}
|
||||
|
||||
bool CheckXPBarInfo()
|
||||
{
|
||||
if (!sgOptions.Gameplay.bExperienceBar)
|
||||
return false;
|
||||
|
||||
const int backX = PANEL_LEFT + PANEL_WIDTH / 2 - 155;
|
||||
const int backY = PANEL_TOP + PANEL_HEIGHT - 11;
|
||||
|
||||
if (MouseX < backX || MouseX >= backX + BACK_WIDTH || MouseY < backY || MouseY >= backY + BACK_HEIGHT)
|
||||
return false;
|
||||
|
||||
const PlayerStruct &player = plr[myplr];
|
||||
|
||||
const int charLevel = player._pLevel;
|
||||
|
||||
sprintf(tempstr, "Level %d", charLevel);
|
||||
AddPanelString(tempstr, true);
|
||||
|
||||
if (charLevel == MAXCHARLEVEL - 1) {
|
||||
// Show a maximum level indicator for max level players.
|
||||
infoclr = COL_GOLD;
|
||||
|
||||
sprintf(tempstr, "Experience: ");
|
||||
PrintWithSeparator(tempstr + SDL_arraysize("Experience: ") - 1, ExpLvlsTbl[charLevel - 1]);
|
||||
AddPanelString(tempstr, true);
|
||||
|
||||
AddPanelString("Maximum Level", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
infoclr = COL_WHITE;
|
||||
|
||||
sprintf(tempstr, "Experience: ");
|
||||
PrintWithSeparator(tempstr + SDL_arraysize("Experience: ") - 1, player._pExperience);
|
||||
AddPanelString(tempstr, true);
|
||||
|
||||
sprintf(tempstr, "Next Level: ");
|
||||
PrintWithSeparator(tempstr + SDL_arraysize("Next Level: ") - 1, ExpLvlsTbl[charLevel]);
|
||||
AddPanelString(tempstr, true);
|
||||
|
||||
sprintf(PrintWithSeparator(tempstr, ExpLvlsTbl[charLevel] - player._pExperience), " to Level %d", charLevel + 1);
|
||||
AddPanelString(tempstr, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace devilution
|
||||
18
Source/qol/xpbar.h
Normal file
18
Source/qol/xpbar.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* @file xpbar.h
|
||||
*
|
||||
* Adds XP bar QoL feature
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace devilution {
|
||||
|
||||
struct CelOutputBuffer;
|
||||
|
||||
void InitXPBar();
|
||||
void FreeXPBar();
|
||||
|
||||
void DrawXPBar(const CelOutputBuffer &out);
|
||||
bool CheckXPBarInfo();
|
||||
|
||||
} // namespace devilution
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
#include "nthread.h"
|
||||
#include "plrmsg.h"
|
||||
#include "qol.h"
|
||||
#include "qol/xpbar.h"
|
||||
#include "render.h"
|
||||
#include "stores.h"
|
||||
#include "towners.h"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue