Skip to content

Commit 38ed61a

Browse files
committed
sgGameInitInfo options sync and Active Block
1 parent 847541a commit 38ed61a

10 files changed

Lines changed: 181 additions & 41 deletions

File tree

Source/DiabloUI/multi/selgame.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ void selgame_Password_Select(size_t /*value*/)
669669
m_game_data->bSharedExperience = *GetOptions().Gameplay.sharedXP ? 1 : 0;
670670
m_game_data->bRemoveCripplingEffects = *GetOptions().Gameplay.removeCripplingEffects ? 1 : 0;
671671
m_game_data->bDisableSearch = *GetOptions().Gameplay.disableSearch ? 1 : 0;
672+
m_game_data->bActiveBlock = *GetOptions().Gameplay.activeBlock ? 1 : 0;
672673

673674
GameData gameInitInfo = *m_game_data;
674675
gameInitInfo.swapLE();

Source/control.cpp

Lines changed: 128 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
#include "controls/plrctrls.h"
2323
#include "cursor.h"
2424
#include "diablo_msg.hpp"
25+
#include "effects.h"
2526
#include "engine/backbuffer_state.hpp"
2627
#include "engine/clx_sprite.hpp"
2728
#include "engine/load_cel.hpp"
29+
#include "engine/palette.h"
2830
#include "engine/render/clx_render.hpp"
2931
#include "engine/render/text_render.hpp"
3032
#include "engine/trn.hpp"
@@ -337,54 +339,82 @@ int DrawDurIcon4Item(const Surface &out, Item &pItem, int x, int c)
337339
const int durabilityThresholdGold = 5;
338340
const int durabilityThresholdRed = 2;
339341

340-
if (pItem.isEmpty())
341-
return x;
342-
if (pItem._iDurability > durabilityThresholdGold)
343-
return x;
342+
bool isMonk = MyPlayer->_pClass == HeroClass::Monk;
343+
bool isShield = pItem._itype == ItemType::Shield;
344+
bool isStaff = pItem._itype == ItemType::Staff;
345+
bool isBareHandedMonk = pItem.isEmpty() && isMonk && MyPlayer->disableBlock;
346+
347+
bool drawRedX = MyPlayer->disableBlock && (isShield || (isMonk && (isStaff || isBareHandedMonk)));
348+
344349
if (c == 0) {
345-
switch (pItem._itype) {
346-
case ItemType::Sword:
347-
c = 1;
348-
break;
349-
case ItemType::Axe:
350-
c = 5;
351-
break;
352-
case ItemType::Bow:
353-
c = 6;
354-
break;
355-
case ItemType::Mace:
356-
c = 4;
357-
break;
358-
case ItemType::Staff:
350+
if (isShield)
351+
c = 0;
352+
else if (isStaff)
359353
c = 7;
360-
break;
361-
case ItemType::Shield:
362-
default:
354+
else if (isBareHandedMonk)
363355
c = 0;
364-
break;
365-
}
356+
else
357+
return x;
366358
}
367359

368-
// Calculate how much of the icon should be gold and red
369-
int height = (*pDurIcons)[c].height(); // Height of durability icon CEL
360+
int height = (*pDurIcons)[c].height();
361+
int width = (*pDurIcons)[c].width();
370362
int partition = 0;
371-
if (pItem._iDurability > durabilityThresholdRed) {
363+
int y = -17 + GetMainPanel().position.y;
364+
365+
bool hasDurability = !pItem.isEmpty();
366+
if (hasDurability && pItem._iDurability > durabilityThresholdRed) {
372367
int current = pItem._iDurability - durabilityThresholdRed;
373368
partition = (height * current) / (durabilityThresholdGold - durabilityThresholdRed);
374369
}
375370

376-
// Draw icon
377-
int y = -17 + GetMainPanel().position.y;
378-
if (partition > 0) {
379-
const Surface stenciledBuffer = out.subregionY(y - partition, partition);
380-
ClxDraw(stenciledBuffer, { x, partition }, (*pDurIcons)[c + 8]); // Gold icon
371+
bool renderBlendedDurability = hasDurability && pItem._iDurability <= durabilityThresholdGold;
372+
bool renderForcedGold = drawRedX && ((isShield || isStaff) && pItem._iDurability > durabilityThresholdGold || isBareHandedMonk);
373+
374+
if (renderBlendedDurability) {
375+
if (partition > 0) {
376+
const Surface stenciledBuffer = out.subregionY(y - partition, partition);
377+
ClxDraw(stenciledBuffer, { x, partition }, (*pDurIcons)[c + 8]);
378+
}
379+
if (partition != height) {
380+
const Surface stenciledBuffer = out.subregionY(y - height, height - partition);
381+
ClxDraw(stenciledBuffer, { x, height }, (*pDurIcons)[c]);
382+
}
383+
} else if (renderForcedGold) {
384+
ClxDraw(out, { x, y }, (*pDurIcons)[c + 8]);
385+
} else if (!drawRedX) {
386+
return x;
381387
}
382-
if (partition != height) {
383-
const Surface stenciledBuffer = out.subregionY(y - height, height - partition);
384-
ClxDraw(stenciledBuffer, { x, height }, (*pDurIcons)[c]); // Red icon
388+
389+
if (drawRedX) {
390+
uint8_t redColor = PAL8_RED + 5;
391+
uint8_t blackColor = 0;
392+
int crossSize = std::min(width, height) / 2;
393+
int offsetX = x + (width - crossSize) / 2;
394+
int offsetY = y + (height - crossSize) / 2 - 32;
395+
396+
for (int i = 0; i < crossSize; ++i) {
397+
int dx1 = offsetX + i;
398+
int dx2 = offsetX + (crossSize - 1 - i);
399+
int dy = offsetY + i;
400+
401+
for (int ox = -1; ox <= 1; ++ox) {
402+
for (int oy = -1; oy <= 1; ++oy) {
403+
if (ox != 0 || oy != 0) {
404+
out.SetPixel({ dx1 + ox, dy + oy }, blackColor); // ↘ outline
405+
out.SetPixel({ dx2 + ox, dy + oy }, blackColor); // ↙ outline
406+
}
407+
}
408+
}
409+
410+
for (int t = -1; t <= 1; ++t) {
411+
out.SetPixel({ dx1, dy + t }, redColor); //
412+
out.SetPixel({ dx2, dy + t }, redColor); //
413+
}
414+
}
385415
}
386416

387-
return x - (*pDurIcons)[c].height() - 8; // Add in spacing for the next durability icon
417+
return x - height - 8;
388418
}
389419

390420
struct TextCmdItem {
@@ -1067,6 +1097,37 @@ void CycleAutomapType()
10671097
}
10681098
}
10691099

1100+
void ToggleActiveBlock()
1101+
{
1102+
if (MyPlayer == nullptr || !MyPlayer->plractive)
1103+
return;
1104+
1105+
if (sgGameInitInfo.bActiveBlock != 1)
1106+
return;
1107+
1108+
const Item &left = MyPlayer->InvBody[INVLOC_HAND_LEFT];
1109+
const Item &right = MyPlayer->InvBody[INVLOC_HAND_RIGHT];
1110+
1111+
const bool isMonk = MyPlayer->_pClass == HeroClass::Monk;
1112+
const bool hasShield = left._itype == ItemType::Shield || right._itype == ItemType::Shield;
1113+
const bool hasStaff = left._itype == ItemType::Staff;
1114+
const bool isBareHanded = left.isEmpty() && right.isEmpty();
1115+
1116+
const bool canBlock =
1117+
(isMonk && (hasShield || hasStaff || isBareHanded)) ||
1118+
(!isMonk && hasShield);
1119+
1120+
if (!canBlock)
1121+
return;
1122+
1123+
MyPlayer->disableBlock = !MyPlayer->disableBlock;
1124+
1125+
if (MyPlayer->disableBlock)
1126+
PlaySFX(SfxID::ItemShieldFlip);
1127+
else
1128+
PlaySFX(SfxID::ItemShield);
1129+
}
1130+
10701131
void CheckPanelInfo()
10711132
{
10721133
MainPanelFlag = false;
@@ -1409,10 +1470,38 @@ void DrawDurIcon(const Surface &out)
14091470
}
14101471

14111472
Player &myPlayer = *MyPlayer;
1412-
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HEAD], x, 3);
1413-
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_CHEST], x, 2);
1414-
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HAND_LEFT], x, 0);
1415-
DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HAND_RIGHT], x, 0);
1473+
1474+
if (!myPlayer.InvBody[INVLOC_HEAD].isEmpty())
1475+
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HEAD], x, 3);
1476+
if (!myPlayer.InvBody[INVLOC_CHEST].isEmpty())
1477+
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_CHEST], x, 2);
1478+
1479+
const Item &left = myPlayer.InvBody[INVLOC_HAND_LEFT];
1480+
const Item &right = myPlayer.InvBody[INVLOC_HAND_RIGHT];
1481+
1482+
// Show staff if it's in the left hand and Monk
1483+
if (left._itype == ItemType::Staff && myPlayer._pClass == HeroClass::Monk)
1484+
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HAND_LEFT], x, 0);
1485+
1486+
// Show shield if present in either hand
1487+
if (left._itype == ItemType::Shield)
1488+
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HAND_LEFT], x, 0);
1489+
if (right._itype == ItemType::Shield)
1490+
x = DrawDurIcon4Item(out, myPlayer.InvBody[INVLOC_HAND_RIGHT], x, 0);
1491+
1492+
// Barehanded Monk (both hands empty, no staff)
1493+
if (myPlayer._pClass == HeroClass::Monk
1494+
&& left.isEmpty()
1495+
&& right.isEmpty()
1496+
&& myPlayer.disableBlock) {
1497+
1498+
Item dummy;
1499+
dummy._itype = ItemType::None;
1500+
dummy._iDurability = 0;
1501+
dummy._iMaxDur = 0;
1502+
1503+
x = DrawDurIcon4Item(out, dummy, x, 0);
1504+
}
14161505
}
14171506

14181507
void RedBack(const Surface &out)

Source/control.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ void CheckMainPanelButton();
150150
void CheckMainPanelButtonDead();
151151
void DoAutoMap();
152152
void CycleAutomapType();
153+
void ToggleActiveBlock();
153154

154155
/**
155156
* Checks the mouse cursor position within the control panel and sets information

Source/diablo.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,11 @@ uint32_t GetGameId()
151151
{
152152
const auto &options = GetOptions();
153153
if (gbIsHellfire) {
154-
if (*options.Gameplay.disableSearch || *options.Gameplay.removeCripplingEffects || *options.Gameplay.sharedXP)
154+
if (*options.Gameplay.disableSearch || *options.Gameplay.removeCripplingEffects || *options.Gameplay.sharedXP || *options.Gameplay.activeBlock)
155155
return GameIdHellfireEnhanced;
156156
return gbIsSpawn ? GameIdHellfireSpawn : GameIdHellfireFull;
157157
}
158-
if (*options.Gameplay.removeCripplingEffects || *options.Gameplay.sharedXP)
158+
if (*options.Gameplay.removeCripplingEffects || *options.Gameplay.sharedXP || *options.Gameplay.activeBlock)
159159
return GameIdDiabloEnhanced;
160160
return gbIsSpawn ? GameIdDiabloSpawn : GameIdDiabloFull;
161161
}
@@ -1707,6 +1707,11 @@ bool CanAutomapBeToggledOff()
17071707
return false;
17081708
}
17091709

1710+
bool CanUseToggleActiveBlock()
1711+
{
1712+
return CanPlayerTakeAction() && sgGameInitInfo.bActiveBlock == 1;
1713+
}
1714+
17101715
} // namespace
17111716

17121717
void InitKeymapActions()
@@ -1850,6 +1855,14 @@ void InitKeymapActions()
18501855
CycleAutomapType,
18511856
nullptr,
18521857
IsGameRunning);
1858+
options.Keymapper.AddAction(
1859+
"ToggleBlock",
1860+
N_("Toggle Active Block"),
1861+
N_("Toggle Active Block on/off. Active Block sets block chance to zero."),
1862+
SDLK_UNKNOWN,
1863+
ToggleActiveBlock,
1864+
nullptr,
1865+
CanUseToggleActiveBlock);
18531866

18541867
options.Keymapper.AddAction(
18551868
"Inventory",
@@ -2423,6 +2436,14 @@ void InitPadmapActions()
24232436
},
24242437
nullptr,
24252438
CanPlayerTakeAction);
2439+
options.Padmapper.AddAction(
2440+
"ToggleBlock",
2441+
N_("Toggle Active Block"),
2442+
N_("Toggle Active Block on/off. Active Block sets block chance to zero."),
2443+
ControllerButton_NONE,
2444+
ToggleActiveBlock,
2445+
nullptr,
2446+
CanUseToggleActiveBlock);
24262447
options.Padmapper.AddAction(
24272448
"Pause Game",
24282449
N_("Pause Game"),

Source/diablo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ void DisableInputEventHandler(const SDL_Event &event, uint16_t modState);
100100
tl::expected<void, std::string> LoadGameLevel(bool firstflag, lvl_entry lvldir);
101101
bool IsDiabloAlive(bool playSFX);
102102
void PrintScreen(SDL_Keycode vkey);
103+
void DrawActiveBlockIcon(const Surface &out);
103104

104105
/**
105106
* @param bStartup Process additional ticks before returning

Source/multi.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <fmt/format.h>
1515

1616
#include "DiabloUI/diabloui.h"
17+
#include "controls/keymapper.hpp"
18+
#include "controls/padmapper.hpp"
1719
#include "diablo.h"
1820
#include "engine/demomode.h"
1921
#include "engine/point.hpp"
@@ -506,10 +508,22 @@ bool InitMulti(GameData *gameData)
506508

507509
} // namespace
508510

511+
bool CheckActiveBlock()
512+
{
513+
auto &options = GetOptions();
514+
const bool toggleBlockMapped = options.Keymapper.KeyForAction("ToggleBlock") != SDLK_UNKNOWN
515+
|| options.Padmapper.ButtonComboForAction("ToggleBlock").button != ControllerButton_NONE;
516+
517+
options.Gameplay.activeBlock.SetValue(toggleBlockMapped);
518+
519+
return toggleBlockMapped;
520+
}
521+
509522
void InitGameInfo()
510523
{
511524
xoshiro128plusplus gameGenerator = ReserveSeedSequence();
512525
gameGenerator.save(sgGameInitInfo.gameSeed);
526+
const bool toggleBlockMapped = CheckActiveBlock();
513527

514528
sgGameInitInfo.size = sizeof(sgGameInitInfo);
515529
sgGameInitInfo.programid = GetGameId();
@@ -526,6 +540,7 @@ void InitGameInfo()
526540
sgGameInitInfo.bSharedExperience = *options.Gameplay.sharedXP ? 1 : 0;
527541
sgGameInitInfo.bRemoveCripplingEffects = *options.Gameplay.removeCripplingEffects ? 1 : 0;
528542
sgGameInitInfo.bDisableSearch = *options.Gameplay.disableSearch ? 1 : 0;
543+
sgGameInitInfo.bActiveBlock = toggleBlockMapped ? 1 : 0;
529544
}
530545

531546
void NetSendLoPri(uint8_t playerId, const std::byte *data, size_t size)

Source/multi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct GameData {
3737
uint8_t bSharedExperience;
3838
uint8_t bRemoveCripplingEffects;
3939
uint8_t bDisableSearch;
40+
uint8_t bActiveBlock;
4041
/** Used to initialise the seed table for dungeon levels so players in multiplayer games generate the same layout */
4142
uint32_t gameSeed[4];
4243

Source/options.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ void DisableEnhancedOptionsIfShareware()
7575

7676
gameplay.removeCripplingEffects.SetValue(false);
7777
gameplay.removeCripplingEffects.flags |= OptionEntryFlags::Invisible;
78+
79+
gameplay.activeBlock.SetValue(false);
80+
gameplay.activeBlock.flags |= OptionEntryFlags::Invisible;
7881
}
7982

8083
namespace {
@@ -822,6 +825,7 @@ GameplayOptions::GameplayOptions()
822825
, autoRefillBelt("Auto Refill Belt", OptionEntryFlags::None, N_("Auto Refill Belt"), N_("Refill belt from inventory when belt item is consumed."), false)
823826
, removeCripplingEffects("Remove Crippling Effects", OptionEntryFlags::CantChangeInMultiPlayer, N_("Remove Crippling Effects"), N_("Fascinating, Ornate, Sacred, Murphy's and Tainted will not spawn and will not be selected for Cauldrons and Goat Shrines. Black Deaths will not reduce max life."), true)
824827
, disableCripplingShrines("Disable Crippling Shrines", OptionEntryFlags::None, N_("Disable Crippling Shrines"), N_("When enabled Cauldrons, Fascinating Shrines, Goat Shrines, Ornate Shrines, Sacred Shrines and Murphy's Shrines are not able to be clicked on and labeled as disabled."), false)
828+
, activeBlock("Toggle Active Block", OptionEntryFlags::Invisible, N_("Toggle Active Block"), N_("Allows players to manually set their shield block chance to 0."), false)
825829
, quickCast("Quick Cast", OptionEntryFlags::None, N_("Quick Cast"), N_("Spell hotkeys instantly cast the spell, rather than switching the readied spell."), false)
826830
, numHealPotionPickup("Heal Potion Pickup", OptionEntryFlags::None, N_("Heal Potion Pickup"), N_("Number of Healing potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 })
827831
, numFullHealPotionPickup("Full Heal Potion Pickup", OptionEntryFlags::None, N_("Full Heal Potion Pickup"), N_("Number of Full Healing potions to pick up automatically."), 0, { 0, 1, 2, 4, 8, 16 })
@@ -850,6 +854,7 @@ std::vector<OptionEntryBase *> GameplayOptions::GetEntries()
850854
&cowQuest,
851855
&runInTown,
852856
&quickCast,
857+
&activeBlock,
853858
&testBard,
854859
&testBarbarian,
855860
&experienceBar,

Source/options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,8 @@ struct GameplayOptions : OptionCategoryBase {
618618
OptionEntryBoolean removeCripplingEffects;
619619
/** @brief Locally disable clicking on shrines which permanently cripple character. */
620620
OptionEntryBoolean disableCripplingShrines;
621+
/** @brief Allows players to manually set their shield block chance to 0. */
622+
OptionEntryBoolean activeBlock;
621623
/** @brief Spell hotkeys instantly cast the spell. */
622624
OptionEntryBoolean quickCast;
623625
/** @brief Number of Healing potions to pick up automatically */

Source/player.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ struct Player {
274274
int destParam3;
275275
int destParam4;
276276
int _pGold;
277+
bool disableBlock = false;
277278

278279
/**
279280
* @brief Contains Information for current Animation
@@ -626,6 +627,9 @@ struct Player {
626627
*/
627628
int GetBlockChance(bool useLevel = true) const
628629
{
630+
if (sgGameInitInfo.bActiveBlock == 1 && disableBlock)
631+
return 0;
632+
629633
int blkper = _pDexterity + getBaseToBlock();
630634
if (useLevel)
631635
blkper += getCharacterLevel() * 2;

0 commit comments

Comments
 (0)