|
22 | 22 | #include "controls/plrctrls.h" |
23 | 23 | #include "cursor.h" |
24 | 24 | #include "diablo_msg.hpp" |
| 25 | +#include "effects.h" |
25 | 26 | #include "engine/backbuffer_state.hpp" |
26 | 27 | #include "engine/clx_sprite.hpp" |
27 | 28 | #include "engine/load_cel.hpp" |
| 29 | +#include "engine/palette.h" |
28 | 30 | #include "engine/render/clx_render.hpp" |
29 | 31 | #include "engine/render/text_render.hpp" |
30 | 32 | #include "engine/trn.hpp" |
@@ -337,54 +339,82 @@ int DrawDurIcon4Item(const Surface &out, Item &pItem, int x, int c) |
337 | 339 | const int durabilityThresholdGold = 5; |
338 | 340 | const int durabilityThresholdRed = 2; |
339 | 341 |
|
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 | + |
344 | 349 | 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) |
359 | 353 | c = 7; |
360 | | - break; |
361 | | - case ItemType::Shield: |
362 | | - default: |
| 354 | + else if (isBareHandedMonk) |
363 | 355 | c = 0; |
364 | | - break; |
365 | | - } |
| 356 | + else |
| 357 | + return x; |
366 | 358 | } |
367 | 359 |
|
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(); |
370 | 362 | 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) { |
372 | 367 | int current = pItem._iDurability - durabilityThresholdRed; |
373 | 368 | partition = (height * current) / (durabilityThresholdGold - durabilityThresholdRed); |
374 | 369 | } |
375 | 370 |
|
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; |
381 | 387 | } |
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 | + } |
385 | 415 | } |
386 | 416 |
|
387 | | - return x - (*pDurIcons)[c].height() - 8; // Add in spacing for the next durability icon |
| 417 | + return x - height - 8; |
388 | 418 | } |
389 | 419 |
|
390 | 420 | struct TextCmdItem { |
@@ -1067,6 +1097,37 @@ void CycleAutomapType() |
1067 | 1097 | } |
1068 | 1098 | } |
1069 | 1099 |
|
| 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 | + |
1070 | 1131 | void CheckPanelInfo() |
1071 | 1132 | { |
1072 | 1133 | MainPanelFlag = false; |
@@ -1409,10 +1470,38 @@ void DrawDurIcon(const Surface &out) |
1409 | 1470 | } |
1410 | 1471 |
|
1411 | 1472 | 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 | + } |
1416 | 1505 | } |
1417 | 1506 |
|
1418 | 1507 | void RedBack(const Surface &out) |
|
0 commit comments