This mostly change player to be by reference instead of by index. But additionally it does stript checks for gold in the belt, move some value types to the initialization and short circute a few functiongs.
2253 lines
64 KiB
C++
2253 lines
64 KiB
C++
/**
|
|
* @file inv.cpp
|
|
*
|
|
* Implementation of player inventory.
|
|
*/
|
|
#include <utility>
|
|
|
|
#include <algorithm>
|
|
#include <fmt/format.h>
|
|
|
|
#include "cursor.h"
|
|
#include "engine/render/cel_render.hpp"
|
|
#include "engine/render/text_render.hpp"
|
|
#include "minitext.h"
|
|
#include "options.h"
|
|
#include "plrmsg.h"
|
|
#include "stores.h"
|
|
#include "towners.h"
|
|
#include "utils/language.h"
|
|
#include "utils/stdcompat/optional.hpp"
|
|
|
|
namespace devilution {
|
|
namespace {
|
|
std::optional<CelSprite> pInvCels;
|
|
} // namespace
|
|
|
|
bool invflag;
|
|
bool drawsbarflag;
|
|
int sgdwLastTime; // check name
|
|
|
|
/**
|
|
* Maps from inventory slot to screen position. The inventory slots are
|
|
* arranged as follows:
|
|
* 00 01
|
|
* 02 03 06
|
|
*
|
|
* 07 08 19 20 13 14
|
|
* 09 10 21 22 15 16
|
|
* 11 12 23 24 17 18
|
|
*
|
|
* 04 05
|
|
*
|
|
* 25 26 27 28 29 30 31 32 33 34
|
|
* 35 36 37 38 39 40 41 42 43 44
|
|
* 45 46 47 48 49 50 51 52 53 54
|
|
* 55 56 57 58 59 60 61 62 63 64
|
|
*
|
|
* 65 66 67 68 69 70 71 72
|
|
* @see graphics/inv/inventory.png
|
|
*/
|
|
const InvXY InvRect[] = {
|
|
// clang-format off
|
|
// X, Y
|
|
{ 132, 31 }, // helmet
|
|
{ 160, 31 }, // helmet
|
|
{ 132, 59 }, // helmet
|
|
{ 160, 59 }, // helmet
|
|
{ 45, 205 }, // left ring
|
|
{ 247, 205 }, // right ring
|
|
{ 204, 59 }, // amulet
|
|
{ 17, 104 }, // left hand
|
|
{ 46, 104 }, // left hand
|
|
{ 17, 132 }, // left hand
|
|
{ 46, 132 }, // left hand
|
|
{ 17, 160 }, // left hand
|
|
{ 46, 160 }, // left hand
|
|
{ 247, 104 }, // right hand
|
|
{ 276, 104 }, // right hand
|
|
{ 247, 132 }, // right hand
|
|
{ 276, 132 }, // right hand
|
|
{ 247, 160 }, // right hand
|
|
{ 276, 160 }, // right hand
|
|
{ 132, 104 }, // chest
|
|
{ 160, 104 }, // chest
|
|
{ 132, 132 }, // chest
|
|
{ 160, 132 }, // chest
|
|
{ 132, 160 }, // chest
|
|
{ 160, 160 }, // chest
|
|
{ 17, 250 }, // inv row 1
|
|
{ 46, 250 }, // inv row 1
|
|
{ 75, 250 }, // inv row 1
|
|
{ 104, 250 }, // inv row 1
|
|
{ 133, 250 }, // inv row 1
|
|
{ 162, 250 }, // inv row 1
|
|
{ 191, 250 }, // inv row 1
|
|
{ 220, 250 }, // inv row 1
|
|
{ 249, 250 }, // inv row 1
|
|
{ 278, 250 }, // inv row 1
|
|
{ 17, 279 }, // inv row 2
|
|
{ 46, 279 }, // inv row 2
|
|
{ 75, 279 }, // inv row 2
|
|
{ 104, 279 }, // inv row 2
|
|
{ 133, 279 }, // inv row 2
|
|
{ 162, 279 }, // inv row 2
|
|
{ 191, 279 }, // inv row 2
|
|
{ 220, 279 }, // inv row 2
|
|
{ 249, 279 }, // inv row 2
|
|
{ 278, 279 }, // inv row 2
|
|
{ 17, 308 }, // inv row 3
|
|
{ 46, 308 }, // inv row 3
|
|
{ 75, 308 }, // inv row 3
|
|
{ 104, 308 }, // inv row 3
|
|
{ 133, 308 }, // inv row 3
|
|
{ 162, 308 }, // inv row 3
|
|
{ 191, 308 }, // inv row 3
|
|
{ 220, 308 }, // inv row 3
|
|
{ 249, 308 }, // inv row 3
|
|
{ 278, 308 }, // inv row 3
|
|
{ 17, 337 }, // inv row 4
|
|
{ 46, 337 }, // inv row 4
|
|
{ 75, 337 }, // inv row 4
|
|
{ 104, 337 }, // inv row 4
|
|
{ 133, 337 }, // inv row 4
|
|
{ 162, 337 }, // inv row 4
|
|
{ 191, 337 }, // inv row 4
|
|
{ 220, 337 }, // inv row 4
|
|
{ 249, 337 }, // inv row 4
|
|
{ 278, 337 }, // inv row 4
|
|
{ 205, 33 }, // belt
|
|
{ 234, 33 }, // belt
|
|
{ 263, 33 }, // belt
|
|
{ 292, 33 }, // belt
|
|
{ 321, 33 }, // belt
|
|
{ 350, 33 }, // belt
|
|
{ 379, 33 }, // belt
|
|
{ 408, 33 } // belt
|
|
// clang-format on
|
|
};
|
|
|
|
/* data */
|
|
|
|
void FreeInvGFX()
|
|
{
|
|
pInvCels = std::nullopt;
|
|
}
|
|
|
|
void InitInv()
|
|
{
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
if (myPlayer._pClass == HeroClass::Warrior) {
|
|
pInvCels = LoadCel("Data\\Inv\\Inv.CEL", SPANEL_WIDTH);
|
|
} else if (myPlayer._pClass == HeroClass::Rogue) {
|
|
pInvCels = LoadCel("Data\\Inv\\Inv_rog.CEL", SPANEL_WIDTH);
|
|
} else if (myPlayer._pClass == HeroClass::Sorcerer) {
|
|
pInvCels = LoadCel("Data\\Inv\\Inv_Sor.CEL", SPANEL_WIDTH);
|
|
} else if (myPlayer._pClass == HeroClass::Monk) {
|
|
if (!gbIsSpawn)
|
|
pInvCels = LoadCel("Data\\Inv\\Inv_Sor.CEL", SPANEL_WIDTH);
|
|
else
|
|
pInvCels = LoadCel("Data\\Inv\\Inv.CEL", SPANEL_WIDTH);
|
|
} else if (myPlayer._pClass == HeroClass::Bard) {
|
|
pInvCels = LoadCel("Data\\Inv\\Inv_rog.CEL", SPANEL_WIDTH);
|
|
} else if (myPlayer._pClass == HeroClass::Barbarian) {
|
|
pInvCels = LoadCel("Data\\Inv\\Inv.CEL", SPANEL_WIDTH);
|
|
}
|
|
|
|
invflag = false;
|
|
drawsbarflag = false;
|
|
}
|
|
|
|
static void InvDrawSlotBack(const CelOutputBuffer &out, int x, int y, int w, int h)
|
|
{
|
|
BYTE *dst = out.at(x, y);
|
|
|
|
for (int hgt = h; hgt != 0; hgt--, dst -= out.pitch() + w) {
|
|
for (int wdt = w; wdt != 0; wdt--) {
|
|
BYTE pix = *dst;
|
|
if (pix >= PAL16_BLUE) {
|
|
if (pix <= PAL16_BLUE + 15)
|
|
pix -= PAL16_BLUE - PAL16_BEIGE;
|
|
else if (pix >= PAL16_GRAY)
|
|
pix -= PAL16_GRAY - PAL16_BEIGE;
|
|
}
|
|
*dst++ = pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawInv(const CelOutputBuffer &out)
|
|
{
|
|
CelDrawTo(out, RIGHT_PANEL_X, 351, *pInvCels, 1);
|
|
|
|
InvXY slotSize[] = {
|
|
{ 2, 2 }, //head
|
|
{ 1, 1 }, //left ring
|
|
{ 1, 1 }, //right ring
|
|
{ 1, 1 }, //amulet
|
|
{ 2, 3 }, //left hand
|
|
{ 2, 3 }, //right hand
|
|
{ 2, 3 }, // chest
|
|
};
|
|
|
|
InvXY slotPos[] = {
|
|
{ 133, 59 }, //head
|
|
{ 48, 205 }, //left ring
|
|
{ 249, 205 }, //right ring
|
|
{ 205, 60 }, //amulet
|
|
{ 17, 160 }, //left hand
|
|
{ 248, 160 }, //right hand
|
|
{ 133, 160 }, // chest
|
|
};
|
|
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
for (int slot = INVLOC_HEAD; slot < NUM_INVLOC; slot++) {
|
|
if (!myPlayer.InvBody[slot].isEmpty()) {
|
|
int screenX = slotPos[slot].X;
|
|
int screenY = slotPos[slot].Y;
|
|
InvDrawSlotBack(out, RIGHT_PANEL_X + screenX, screenY, slotSize[slot].X * INV_SLOT_SIZE_PX, slotSize[slot].Y * INV_SLOT_SIZE_PX);
|
|
|
|
int frame = myPlayer.InvBody[slot]._iCurs + CURSOR_FIRSTITEM;
|
|
|
|
int frameW;
|
|
int frameH;
|
|
std::tie(frameW, frameH) = GetInvItemSize(frame);
|
|
|
|
// calc item offsets for weapons smaller than 2x3 slots
|
|
if (slot == INVLOC_HAND_LEFT) {
|
|
screenX += frameW == INV_SLOT_SIZE_PX ? 14 : 0;
|
|
screenY += frameH == (3 * INV_SLOT_SIZE_PX) ? 0 : -14;
|
|
} else if (slot == INVLOC_HAND_RIGHT) {
|
|
screenX += frameW == INV_SLOT_SIZE_PX ? 13 : 1;
|
|
screenY += frameH == 3 * INV_SLOT_SIZE_PX ? 0 : -14;
|
|
}
|
|
|
|
const auto &cel = GetInvItemSprite(frame);
|
|
const int celFrame = GetInvItemFrame(frame);
|
|
|
|
if (pcursinvitem == slot) {
|
|
CelBlitOutlineTo(out, GetOutlineColor(myPlayer.InvBody[slot], true), RIGHT_PANEL_X + screenX, screenY, cel, celFrame, false);
|
|
}
|
|
|
|
CelDrawItem(myPlayer.InvBody[slot]._iStatFlag, out, RIGHT_PANEL_X + screenX, screenY, cel, celFrame);
|
|
|
|
if (slot == INVLOC_HAND_LEFT) {
|
|
if (myPlayer.InvBody[slot]._iLoc == ILOC_TWOHAND) {
|
|
if (myPlayer._pClass != HeroClass::Barbarian
|
|
|| (myPlayer.InvBody[slot]._itype != ITYPE_SWORD
|
|
&& myPlayer.InvBody[slot]._itype != ITYPE_MACE)) {
|
|
InvDrawSlotBack(out, RIGHT_PANEL_X + slotPos[INVLOC_HAND_RIGHT].X, slotPos[INVLOC_HAND_RIGHT].Y, slotSize[INVLOC_HAND_RIGHT].X * INV_SLOT_SIZE_PX, slotSize[INVLOC_HAND_RIGHT].Y * INV_SLOT_SIZE_PX);
|
|
light_table_index = 0;
|
|
cel_transparency_active = true;
|
|
|
|
const int dstX = RIGHT_PANEL_X + slotPos[INVLOC_HAND_RIGHT].X + (frameW == INV_SLOT_SIZE_PX ? 13 : -1);
|
|
const int dstY = slotPos[INVLOC_HAND_RIGHT].Y;
|
|
CelClippedBlitLightTransTo(out, dstX, dstY, cel, celFrame);
|
|
|
|
cel_transparency_active = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < NUM_INV_GRID_ELEM; i++) {
|
|
if (myPlayer.InvGrid[i] != 0) {
|
|
InvDrawSlotBack(
|
|
out,
|
|
InvRect[i + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
|
|
InvRect[i + SLOTXY_INV_FIRST].Y - 1,
|
|
INV_SLOT_SIZE_PX,
|
|
INV_SLOT_SIZE_PX);
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < NUM_INV_GRID_ELEM; j++) {
|
|
if (myPlayer.InvGrid[j] > 0) { // first slot of an item
|
|
int ii = myPlayer.InvGrid[j] - 1;
|
|
int frame = myPlayer.InvList[ii]._iCurs + CURSOR_FIRSTITEM;
|
|
|
|
const auto &cel = GetInvItemSprite(frame);
|
|
const int celFrame = GetInvItemFrame(frame);
|
|
|
|
if (pcursinvitem == ii + INVITEM_INV_FIRST) {
|
|
CelBlitOutlineTo(
|
|
out,
|
|
GetOutlineColor(myPlayer.InvList[ii], true),
|
|
InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
|
|
InvRect[j + SLOTXY_INV_FIRST].Y - 1,
|
|
cel, celFrame, false);
|
|
}
|
|
|
|
CelDrawItem(
|
|
myPlayer.InvList[ii]._iStatFlag,
|
|
out,
|
|
InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
|
|
InvRect[j + SLOTXY_INV_FIRST].Y - 1,
|
|
cel, celFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DrawInvBelt(const CelOutputBuffer &out)
|
|
{
|
|
if (talkflag) {
|
|
return;
|
|
}
|
|
|
|
DrawPanelBox(out, 205, 21, 232, 28, PANEL_X + 205, PANEL_Y + 5);
|
|
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
for (int i = 0; i < MAXBELTITEMS; i++) {
|
|
if (myPlayer.SpdList[i].isEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
InvDrawSlotBack(out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, INV_SLOT_SIZE_PX, INV_SLOT_SIZE_PX);
|
|
int frame = myPlayer.SpdList[i]._iCurs + CURSOR_FIRSTITEM;
|
|
|
|
const auto &cel = GetInvItemSprite(frame);
|
|
const int celFrame = GetInvItemFrame(frame);
|
|
|
|
if (pcursinvitem == i + INVITEM_BELT_FIRST) {
|
|
if (!sgbControllerActive || invflag) {
|
|
CelBlitOutlineTo(out, GetOutlineColor(myPlayer.SpdList[i], true), InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, cel, celFrame, false);
|
|
}
|
|
}
|
|
|
|
CelDrawItem(myPlayer.SpdList[i]._iStatFlag, out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, cel, celFrame);
|
|
|
|
if (AllItemsList[myPlayer.SpdList[i].IDidx].iUsable
|
|
&& myPlayer.SpdList[i]._iStatFlag
|
|
&& myPlayer.SpdList[i]._itype != ITYPE_GOLD) {
|
|
sprintf(tempstr, "%i", i + 1);
|
|
SDL_Rect rect {
|
|
InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X + INV_SLOT_SIZE_PX - GetLineWidth(tempstr),
|
|
InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1,
|
|
0,
|
|
0
|
|
};
|
|
DrawString(out, tempstr, rect, UIS_SILVER);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Adds an item to a player's InvGrid array
|
|
* @param invGridIndex Item's position in InvGrid (this should be the item's topleft grid tile)
|
|
* @param invListIndex The item's InvList index (it's expected this already has +1 added to it since InvGrid can't store a 0 index)
|
|
* @param sizeX Horizontal size of item
|
|
* @param sizeY Vertical size of item
|
|
*/
|
|
static void AddItemToInvGrid(PlayerStruct &player, int invGridIndex, int invListIndex, int sizeX, int sizeY)
|
|
{
|
|
const int pitch = 10;
|
|
for (int y = 0; y < sizeY; y++) {
|
|
for (int x = 0; x < sizeX; x++) {
|
|
if (x == 0 && y == sizeY - 1)
|
|
player.InvGrid[invGridIndex + x] = invListIndex;
|
|
else
|
|
player.InvGrid[invGridIndex + x] = -invListIndex;
|
|
}
|
|
invGridIndex += pitch;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the size, in inventory cells, of the given item.
|
|
* @param item The item whose size is to be determined.
|
|
* @return The size, in inventory cells, of the item.
|
|
*/
|
|
InvXY GetInventorySize(const ItemStruct &item)
|
|
{
|
|
int itemSizeIndex = item._iCurs + CURSOR_FIRSTITEM;
|
|
int w;
|
|
int h;
|
|
std::tie(w, h) = GetInvItemSize(itemSizeIndex);
|
|
return { w / INV_SLOT_SIZE_PX, h / INV_SLOT_SIZE_PX };
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the given item can fit in a belt slot (i.e. the item's size in inventory cells is 1x1).
|
|
* @param item The item to be checked.
|
|
* @return 'True' in case the item can fit a belt slot and 'False' otherwise.
|
|
*/
|
|
bool FitsInBeltSlot(const ItemStruct &item)
|
|
{
|
|
InvXY size = GetInventorySize(item);
|
|
|
|
return size.X == 1 && size.Y == 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the given item can be placed on the belt. Takes item size as well as characteristics into account. Items
|
|
* that cannot be placed on the belt have to be placed in the inventory instead.
|
|
* @param item The item to be checked.
|
|
* @return 'True' in case the item can be placed on the belt and 'False' otherwise.
|
|
*/
|
|
bool CanBePlacedOnBelt(const ItemStruct &item)
|
|
{
|
|
return FitsInBeltSlot(item)
|
|
&& item._itype != ITYPE_GOLD
|
|
&& item._iStatFlag
|
|
&& AllItemsList[item.IDidx].iUsable;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the given item can be placed on the specified player's belt. Returns 'True' when the item can be placed
|
|
* on belt slots and the player has at least one empty slot in his belt.
|
|
* If 'persistItem' is 'True', the item is also placed in the belt.
|
|
* @param player The player on whose belt will be checked.
|
|
* @param item The item to be checked.
|
|
* @param persistItem Pass 'True' to actually place the item in the belt. The default is 'False'.
|
|
* @return 'True' in case the item can be placed on the player's belt and 'False' otherwise.
|
|
*/
|
|
bool AutoPlaceItemInBelt(PlayerStruct &player, const ItemStruct &item, bool persistItem)
|
|
{
|
|
if (!CanBePlacedOnBelt(item)) {
|
|
return false;
|
|
}
|
|
|
|
for (auto &beltItem : player.SpdList) {
|
|
if (beltItem.isEmpty()) {
|
|
if (persistItem) {
|
|
beltItem = item;
|
|
player.CalcScrolls();
|
|
drawsbarflag = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the given item can be equipped. Since this overload doesn't take player information, it only considers
|
|
* general aspects about the item, like if its requirements are met and if the item's target location is valid for the body.
|
|
* @param item The item to check.
|
|
* @return 'True' in case the item could be equipped in a player, and 'False' otherwise.
|
|
*/
|
|
bool CanEquip(const ItemStruct &item)
|
|
{
|
|
return item.isEquipment()
|
|
&& item._iStatFlag;
|
|
}
|
|
|
|
/**
|
|
* @brief A specialized version of 'CanEquip(int, ItemStruct&, int)' that specifically checks whether the item can be equipped
|
|
* in one/both of the player's hands.
|
|
* @param player The player whose inventory will be checked for compatibility with the item.
|
|
* @param item The item to check.
|
|
* @return 'True' if the player can currently equip the item in either one of his hands (i.e. the required hands are empty and
|
|
* allow the item), and 'False' otherwise.
|
|
*/
|
|
bool CanWield(PlayerStruct &player, const ItemStruct &item)
|
|
{
|
|
if (!CanEquip(item) || (item._iLoc != ILOC_ONEHAND && item._iLoc != ILOC_TWOHAND))
|
|
return false;
|
|
|
|
ItemStruct &leftHandItem = player.InvBody[INVLOC_HAND_LEFT];
|
|
ItemStruct &rightHandItem = player.InvBody[INVLOC_HAND_RIGHT];
|
|
|
|
if (leftHandItem.isEmpty() && rightHandItem.isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
if (!leftHandItem.isEmpty() && !rightHandItem.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
ItemStruct &occupiedHand = !leftHandItem.isEmpty() ? leftHandItem : rightHandItem;
|
|
|
|
// Barbarian can wield two handed swords and maces in one hand, so we allow equiping any sword/mace as long as his occupied
|
|
// hand has a shield (i.e. no dual wielding allowed)
|
|
if (player._pClass == HeroClass::Barbarian) {
|
|
if (occupiedHand._itype == ITYPE_SHIELD && (item._itype == ITYPE_SWORD || item._itype == ITYPE_MACE))
|
|
return true;
|
|
}
|
|
|
|
// Bard can dual wield swords and maces, so we allow equiping one-handed weapons in her free slot as long as her occupied
|
|
// slot is another one-handed weapon.
|
|
if (player._pClass == HeroClass::Bard) {
|
|
bool occupiedHandIsOneHandedSwordOrMace = occupiedHand._iLoc == ILOC_ONEHAND
|
|
&& (occupiedHand._itype == ITYPE_SWORD || occupiedHand._itype == ITYPE_MACE);
|
|
|
|
bool weaponToEquipIsOneHandedSwordOrMace = item._iLoc == ILOC_ONEHAND
|
|
&& (item._itype == ITYPE_SWORD || item._itype == ITYPE_MACE);
|
|
|
|
if (occupiedHandIsOneHandedSwordOrMace && weaponToEquipIsOneHandedSwordOrMace) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return item._iLoc == ILOC_ONEHAND
|
|
&& occupiedHand._iLoc == ILOC_ONEHAND
|
|
&& item._iClass != occupiedHand._iClass;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the specified item can be equipped in the desired body location on the player.
|
|
* @param player The player whose inventory will be checked for compatibility with the item.
|
|
* @param item The item to check.
|
|
* @param bodyLocation The location in the inventory to be checked against.
|
|
* @return 'True' if the player can currently equip the item in the specified body location (i.e. the body location is empty and
|
|
* allows the item), and 'False' otherwise.
|
|
*/
|
|
bool CanEquip(PlayerStruct &player, const ItemStruct &item, inv_body_loc bodyLocation)
|
|
{
|
|
if (!CanEquip(item) || player._pmode > PM_WALK3 || !player.InvBody[bodyLocation].isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
switch (bodyLocation) {
|
|
case INVLOC_AMULET:
|
|
return item._iLoc == ILOC_AMULET;
|
|
|
|
case INVLOC_CHEST:
|
|
return item._iLoc == ILOC_ARMOR;
|
|
|
|
case INVLOC_HAND_LEFT:
|
|
case INVLOC_HAND_RIGHT:
|
|
return CanWield(player, item);
|
|
|
|
case INVLOC_HEAD:
|
|
return item._iLoc == ILOC_HELM;
|
|
|
|
case INVLOC_RING_LEFT:
|
|
case INVLOC_RING_RIGHT:
|
|
return item._iLoc == ILOC_RING;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Automatically attempts to equip the specified item in a specific location in the player's body.
|
|
* @note On success, this will broadcast an equipment_change event to let other players know about the equipment change.
|
|
* @param playerId The player number whose inventory will be checked for compatibility with the item.
|
|
* @param item The item to equip.
|
|
* @param bodyLocation The location in the inventory where the item should be equipped.
|
|
* @param persistItem Indicates whether or not the item should be persisted in the player's body. Pass 'False' to check
|
|
* whether the player can equip the item but you don't want the item to actually be equipped. 'True' by default.
|
|
* @return 'True' if the item was equipped and 'False' otherwise.
|
|
*/
|
|
bool AutoEquip(int playerId, const ItemStruct &item, inv_body_loc bodyLocation, bool persistItem)
|
|
{
|
|
auto &player = plr[playerId];
|
|
|
|
if (!CanEquip(player, item, bodyLocation)) {
|
|
return false;
|
|
}
|
|
|
|
if (persistItem) {
|
|
player.InvBody[bodyLocation] = item;
|
|
|
|
if (sgOptions.Audio.bAutoEquipSound && playerId == myplr) {
|
|
PlaySFX(ItemInvSnds[ItemCAnimTbl[item._iCurs]]);
|
|
}
|
|
|
|
NetSendCmdChItem(false, bodyLocation);
|
|
CalcPlrInv(playerId, true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Automatically attempts to equip the specified item in the most appropriate location in the player's body.
|
|
* @note On success, this will broadcast an equipment_change event to let other players know about the equipment change.
|
|
* @param playerNumber The player number whose inventory will be checked for compatibility with the item.
|
|
* @param item The item to equip.
|
|
* @param persistItem Indicates whether or not the item should be persisted in the player's body. Pass 'False' to check
|
|
* whether the player can equip the item but you don't want the item to actually be equipped. 'True' by default.
|
|
* @return 'True' if the item was equipped and 'False' otherwise.
|
|
*/
|
|
bool AutoEquip(int playerNumber, const ItemStruct &item, bool persistItem)
|
|
{
|
|
if (!CanEquip(item)) {
|
|
return false;
|
|
}
|
|
|
|
for (int bodyLocation = INVLOC_HEAD; bodyLocation < NUM_INVLOC; bodyLocation++) {
|
|
if (AutoEquip(playerNumber, item, (inv_body_loc)bodyLocation, persistItem)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether or not auto-equipping behavior is enabled for the given player and item.
|
|
* @param player The player to check.
|
|
* @param item The item to check.
|
|
* @return 'True' if auto-equipping behavior is enabled for the player and item and 'False' otherwise.
|
|
*/
|
|
bool AutoEquipEnabled(const PlayerStruct &player, const ItemStruct &item)
|
|
{
|
|
if (item.isWeapon()) {
|
|
// Monk can use unarmed attack as an encouraged option, thus we do not automatically equip weapons on him so as to not
|
|
// annoy players who prefer that playstyle.
|
|
return player._pClass != HeroClass::Monk && sgOptions.Gameplay.bAutoEquipWeapons;
|
|
}
|
|
|
|
if (item.isArmor()) {
|
|
return sgOptions.Gameplay.bAutoEquipArmor;
|
|
}
|
|
|
|
if (item.isHelm()) {
|
|
return sgOptions.Gameplay.bAutoEquipHelms;
|
|
}
|
|
|
|
if (item.isShield()) {
|
|
return sgOptions.Gameplay.bAutoEquipShields;
|
|
}
|
|
|
|
if (item.isJewelry()) {
|
|
return sgOptions.Gameplay.bAutoEquipJewelry;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the given item can be placed on the specified player's inventory.
|
|
* If 'persistItem' is 'True', the item is also placed in the inventory.
|
|
* @param item The item to be checked.
|
|
* @param persistItem Pass 'True' to actually place the item in the inventory. The default is 'False'.
|
|
* @return 'True' in case the item can be placed on the player's inventory and 'False' otherwise.
|
|
*/
|
|
bool AutoPlaceItemInInventory(PlayerStruct &player, const ItemStruct &item, bool persistItem)
|
|
{
|
|
InvXY itemSize = GetInventorySize(item);
|
|
|
|
if (itemSize.Y == 1) {
|
|
for (int i = 30; i <= 39; i++) {
|
|
if (AutoPlaceItemInInventorySlot(player, i, item, persistItem))
|
|
return true;
|
|
}
|
|
for (int x = 9; x >= 0; x--) {
|
|
for (int y = 2; y >= 0; y--) {
|
|
if (AutoPlaceItemInInventorySlot(player, 10 * y + x, item, persistItem))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (itemSize.Y == 2) {
|
|
for (int x = 10 - itemSize.X; x >= 0; x -= itemSize.X) {
|
|
for (int y = 0; y < 3; y++) {
|
|
if (AutoPlaceItemInInventorySlot(player, 10 * y + x, item, persistItem))
|
|
return true;
|
|
}
|
|
}
|
|
if (itemSize.X == 2) {
|
|
for (int x = 7; x >= 0; x -= 2) {
|
|
for (int y = 0; y < 3; y++) {
|
|
if (AutoPlaceItemInInventorySlot(player, 10 * y + x, item, persistItem))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (itemSize.X == 1 && itemSize.Y == 3) {
|
|
for (int i = 0; i < 20; i++) {
|
|
if (AutoPlaceItemInInventorySlot(player, i, item, persistItem))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (itemSize.X == 2 && itemSize.Y == 3) {
|
|
for (int i = 0; i < 9; i++) {
|
|
if (AutoPlaceItemInInventorySlot(player, i, item, persistItem))
|
|
return true;
|
|
}
|
|
|
|
for (int i = 10; i < 19; i++) {
|
|
if (AutoPlaceItemInInventorySlot(player, i, item, persistItem))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
app_fatal("Unknown item size: %ix%i", itemSize.X, itemSize.Y);
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the given item can be placed on the specified player's inventory slot.
|
|
* If 'persistItem' is 'True', the item is also placed in the inventory slot.
|
|
* @param slotIndex The 0-based index of the slot to put the item on.
|
|
* @param item The item to be checked.
|
|
* @param persistItem Pass 'True' to actually place the item in the inventory slot. The default is 'False'.
|
|
* @return 'True' in case the item can be placed on the specified player's inventory slot and 'False' otherwise.
|
|
*/
|
|
bool AutoPlaceItemInInventorySlot(PlayerStruct &player, int slotIndex, const ItemStruct &item, bool persistItem)
|
|
{
|
|
int yy = (slotIndex > 0) ? (10 * (slotIndex / 10)) : 0;
|
|
|
|
InvXY itemSize = GetInventorySize(item);
|
|
for (int j = 0; j < itemSize.Y; j++) {
|
|
if (yy >= NUM_INV_GRID_ELEM) {
|
|
return false;
|
|
}
|
|
int xx = (slotIndex > 0) ? (slotIndex % 10) : 0;
|
|
for (int i = 0; i < itemSize.X; i++) {
|
|
if (xx >= 10 || player.InvGrid[xx + yy] != 0) {
|
|
return false;
|
|
}
|
|
xx++;
|
|
}
|
|
yy += 10;
|
|
}
|
|
|
|
if (persistItem) {
|
|
player.InvList[player._pNumInv] = player.HoldItem;
|
|
player._pNumInv++;
|
|
|
|
AddItemToInvGrid(player, slotIndex, player._pNumInv, itemSize.X, itemSize.Y);
|
|
player.CalcScrolls();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GoldAutoPlace(PlayerStruct &player)
|
|
{
|
|
bool done = false;
|
|
|
|
for (int i = 0; i < player._pNumInv && !done; i++) {
|
|
if (player.InvList[i]._itype != ITYPE_GOLD)
|
|
continue;
|
|
if (player.InvList[i]._ivalue >= MaxGold)
|
|
continue;
|
|
|
|
player.InvList[i]._ivalue += player.HoldItem._ivalue;
|
|
if (player.InvList[i]._ivalue > MaxGold) {
|
|
player.HoldItem._ivalue = player.InvList[i]._ivalue - MaxGold;
|
|
SetPlrHandGoldCurs(&player.HoldItem);
|
|
player.InvList[i]._ivalue = MaxGold;
|
|
if (gbIsHellfire)
|
|
GetPlrHandSeed(&player.HoldItem);
|
|
} else {
|
|
player.HoldItem._ivalue = 0;
|
|
done = true;
|
|
}
|
|
|
|
SetPlrHandGoldCurs(&player.InvList[i]);
|
|
player._pGold = CalculateGold(player);
|
|
}
|
|
|
|
for (int i = 39; i >= 30 && !done; i--) {
|
|
done = GoldAutoPlaceInInventorySlot(player, i);
|
|
}
|
|
for (int x = 9; x >= 0 && !done; x--) {
|
|
for (int y = 2; y >= 0 && !done; y--) {
|
|
done = GoldAutoPlaceInInventorySlot(player, 10 * y + x);
|
|
}
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
bool GoldAutoPlaceInInventorySlot(PlayerStruct &player, int slotIndex)
|
|
{
|
|
int yy = 10 * (slotIndex / 10);
|
|
int xx = slotIndex % 10;
|
|
|
|
if (player.InvGrid[xx + yy] != 0) {
|
|
return false;
|
|
}
|
|
|
|
int ii = player._pNumInv;
|
|
player.InvList[ii] = player.HoldItem;
|
|
player._pNumInv = player._pNumInv + 1;
|
|
player.InvGrid[xx + yy] = player._pNumInv;
|
|
GetPlrHandSeed(&player.InvList[ii]);
|
|
|
|
int gold = player.HoldItem._ivalue;
|
|
if (gold > MaxGold) {
|
|
gold -= MaxGold;
|
|
player.HoldItem._ivalue = gold;
|
|
GetPlrHandSeed(&player.HoldItem);
|
|
player.InvList[ii]._ivalue = MaxGold;
|
|
return false;
|
|
}
|
|
|
|
player.HoldItem._ivalue = 0;
|
|
player._pGold = CalculateGold(player);
|
|
NewCursor(CURSOR_HAND);
|
|
|
|
return true;
|
|
}
|
|
|
|
int SwapItem(ItemStruct *a, ItemStruct *b)
|
|
{
|
|
std::swap(*a, *b);
|
|
|
|
return b->_iCurs + CURSOR_FIRSTITEM;
|
|
}
|
|
|
|
void CheckInvPaste(int pnum, int mx, int my)
|
|
{
|
|
auto &player = plr[pnum];
|
|
|
|
SetICursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
int i = mx + (icursW / 2);
|
|
int j = my + (icursH / 2);
|
|
int sx = icursW28;
|
|
int sy = icursH28;
|
|
bool done = false;
|
|
int r = 0;
|
|
for (; r < NUM_XY_SLOTS && !done; r++) {
|
|
int xo = RIGHT_PANEL;
|
|
int yo = 0;
|
|
if (r >= SLOTXY_BELT_FIRST) {
|
|
xo = PANEL_LEFT;
|
|
yo = PANEL_TOP;
|
|
}
|
|
|
|
if (i >= InvRect[r].X + xo && i <= InvRect[r].X + xo + INV_SLOT_SIZE_PX) {
|
|
if (j >= InvRect[r].Y + yo - INV_SLOT_SIZE_PX - 1 && j < InvRect[r].Y + yo) {
|
|
done = true;
|
|
r--;
|
|
}
|
|
}
|
|
if (r == SLOTXY_CHEST_LAST) {
|
|
if ((sx & 1) == 0)
|
|
i -= 14;
|
|
if ((sy & 1) == 0)
|
|
j -= 14;
|
|
}
|
|
if (r == SLOTXY_INV_LAST && (sy & 1) == 0)
|
|
j += 14;
|
|
}
|
|
if (!done)
|
|
return;
|
|
|
|
item_equip_type il = ILOC_UNEQUIPABLE;
|
|
if (r >= SLOTXY_HEAD_FIRST && r <= SLOTXY_HEAD_LAST)
|
|
il = ILOC_HELM;
|
|
if (r >= SLOTXY_RING_LEFT && r <= SLOTXY_RING_RIGHT)
|
|
il = ILOC_RING;
|
|
if (r == SLOTXY_AMULET)
|
|
il = ILOC_AMULET;
|
|
if (r >= SLOTXY_HAND_LEFT_FIRST && r <= SLOTXY_HAND_RIGHT_LAST)
|
|
il = ILOC_ONEHAND;
|
|
if (r >= SLOTXY_CHEST_FIRST && r <= SLOTXY_CHEST_LAST)
|
|
il = ILOC_ARMOR;
|
|
if (r >= SLOTXY_BELT_FIRST && r <= SLOTXY_BELT_LAST)
|
|
il = ILOC_BELT;
|
|
|
|
done = player.HoldItem._iLoc == il;
|
|
|
|
if (il == ILOC_ONEHAND && player.HoldItem._iLoc == ILOC_TWOHAND) {
|
|
if (player._pClass == HeroClass::Barbarian
|
|
&& (player.HoldItem._itype == ITYPE_SWORD || player.HoldItem._itype == ITYPE_MACE))
|
|
il = ILOC_ONEHAND;
|
|
else
|
|
il = ILOC_TWOHAND;
|
|
done = true;
|
|
}
|
|
if (player.HoldItem._iLoc == ILOC_UNEQUIPABLE && il == ILOC_BELT) {
|
|
if (sx == 1 && sy == 1) {
|
|
done = true;
|
|
if (!AllItemsList[player.HoldItem.IDidx].iUsable)
|
|
done = false;
|
|
if (!player.HoldItem._iStatFlag)
|
|
done = false;
|
|
if (player.HoldItem._itype == ITYPE_GOLD)
|
|
done = false;
|
|
}
|
|
}
|
|
|
|
int it = 0;
|
|
if (il == ILOC_UNEQUIPABLE) {
|
|
done = true;
|
|
int ii = r - SLOTXY_INV_FIRST;
|
|
if (player.HoldItem._itype == ITYPE_GOLD) {
|
|
int yy = 10 * (ii / 10);
|
|
int xx = ii % 10;
|
|
if (player.InvGrid[xx + yy] != 0) {
|
|
int iv = player.InvGrid[xx + yy];
|
|
if (iv > 0) {
|
|
if (player.InvList[iv - 1]._itype != ITYPE_GOLD) {
|
|
it = iv;
|
|
}
|
|
} else {
|
|
it = -iv;
|
|
}
|
|
}
|
|
} else {
|
|
int yy = 10 * ((ii / 10) - ((sy - 1) / 2));
|
|
if (yy < 0)
|
|
yy = 0;
|
|
for (j = 0; j < sy && done; j++) {
|
|
if (yy >= NUM_INV_GRID_ELEM)
|
|
done = false;
|
|
int xx = (ii % 10) - ((sx - 1) / 2);
|
|
if (xx < 0)
|
|
xx = 0;
|
|
for (i = 0; i < sx && done; i++) {
|
|
if (xx >= 10) {
|
|
done = false;
|
|
} else {
|
|
if (player.InvGrid[xx + yy] != 0) {
|
|
int iv = player.InvGrid[xx + yy];
|
|
if (iv < 0)
|
|
iv = -iv;
|
|
if (it != 0) {
|
|
if (it != iv)
|
|
done = false;
|
|
} else {
|
|
it = iv;
|
|
}
|
|
}
|
|
}
|
|
xx++;
|
|
}
|
|
yy += 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!done)
|
|
return;
|
|
|
|
if (il != ILOC_UNEQUIPABLE && il != ILOC_BELT && !player.HoldItem._iStatFlag) {
|
|
done = false;
|
|
player.Say(HeroSpeech::ICantUseThisYet);
|
|
}
|
|
|
|
if (!done)
|
|
return;
|
|
|
|
if (pnum == myplr)
|
|
PlaySFX(ItemInvSnds[ItemCAnimTbl[player.HoldItem._iCurs]]);
|
|
|
|
int cn = CURSOR_HAND;
|
|
switch (il) {
|
|
case ILOC_HELM:
|
|
NetSendCmdChItem(false, INVLOC_HEAD);
|
|
if (player.InvBody[INVLOC_HEAD].isEmpty())
|
|
player.InvBody[INVLOC_HEAD] = player.HoldItem;
|
|
else
|
|
cn = SwapItem(&player.InvBody[INVLOC_HEAD], &player.HoldItem);
|
|
break;
|
|
case ILOC_RING:
|
|
if (r == SLOTXY_RING_LEFT) {
|
|
NetSendCmdChItem(false, INVLOC_RING_LEFT);
|
|
if (player.InvBody[INVLOC_RING_LEFT].isEmpty())
|
|
player.InvBody[INVLOC_RING_LEFT] = player.HoldItem;
|
|
else
|
|
cn = SwapItem(&player.InvBody[INVLOC_RING_LEFT], &player.HoldItem);
|
|
} else {
|
|
NetSendCmdChItem(false, INVLOC_RING_RIGHT);
|
|
if (player.InvBody[INVLOC_RING_RIGHT].isEmpty())
|
|
player.InvBody[INVLOC_RING_RIGHT] = player.HoldItem;
|
|
else
|
|
cn = SwapItem(&player.InvBody[INVLOC_RING_RIGHT], &player.HoldItem);
|
|
}
|
|
break;
|
|
case ILOC_AMULET:
|
|
NetSendCmdChItem(false, INVLOC_AMULET);
|
|
if (player.InvBody[INVLOC_AMULET].isEmpty())
|
|
player.InvBody[INVLOC_AMULET] = player.HoldItem;
|
|
else
|
|
cn = SwapItem(&player.InvBody[INVLOC_AMULET], &player.HoldItem);
|
|
break;
|
|
case ILOC_ONEHAND:
|
|
if (r <= SLOTXY_HAND_LEFT_LAST) {
|
|
if (player.InvBody[INVLOC_HAND_LEFT].isEmpty()) {
|
|
if ((player.InvBody[INVLOC_HAND_RIGHT].isEmpty() || player.InvBody[INVLOC_HAND_RIGHT]._iClass != player.HoldItem._iClass)
|
|
|| (player._pClass == HeroClass::Bard && player.InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON && player.HoldItem._iClass == ICLASS_WEAPON)) {
|
|
NetSendCmdChItem(false, INVLOC_HAND_LEFT);
|
|
player.InvBody[INVLOC_HAND_LEFT] = player.HoldItem;
|
|
} else {
|
|
NetSendCmdChItem(false, INVLOC_HAND_RIGHT);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_RIGHT], &player.HoldItem);
|
|
}
|
|
break;
|
|
}
|
|
if ((player.InvBody[INVLOC_HAND_RIGHT].isEmpty() || player.InvBody[INVLOC_HAND_RIGHT]._iClass != player.HoldItem._iClass)
|
|
|| (player._pClass == HeroClass::Bard && player.InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON && player.HoldItem._iClass == ICLASS_WEAPON)) {
|
|
NetSendCmdChItem(false, INVLOC_HAND_LEFT);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_LEFT], &player.HoldItem);
|
|
break;
|
|
}
|
|
|
|
NetSendCmdChItem(false, INVLOC_HAND_RIGHT);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_RIGHT], &player.HoldItem);
|
|
break;
|
|
}
|
|
if (player.InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
|
|
if ((player.InvBody[INVLOC_HAND_LEFT].isEmpty() || player.InvBody[INVLOC_HAND_LEFT]._iLoc != ILOC_TWOHAND)
|
|
|| (player._pClass == HeroClass::Barbarian && (player.InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SWORD || player.InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_MACE))) {
|
|
if ((player.InvBody[INVLOC_HAND_LEFT].isEmpty() || player.InvBody[INVLOC_HAND_LEFT]._iClass != player.HoldItem._iClass)
|
|
|| (player._pClass == HeroClass::Bard && player.InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON && player.HoldItem._iClass == ICLASS_WEAPON)) {
|
|
NetSendCmdChItem(false, INVLOC_HAND_RIGHT);
|
|
player.InvBody[INVLOC_HAND_RIGHT] = player.HoldItem;
|
|
break;
|
|
}
|
|
NetSendCmdChItem(false, INVLOC_HAND_LEFT);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_LEFT], &player.HoldItem);
|
|
break;
|
|
}
|
|
NetSendCmdDelItem(false, INVLOC_HAND_LEFT);
|
|
NetSendCmdChItem(false, INVLOC_HAND_RIGHT);
|
|
SwapItem(&player.InvBody[INVLOC_HAND_RIGHT], &player.InvBody[INVLOC_HAND_LEFT]);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_RIGHT], &player.HoldItem);
|
|
break;
|
|
}
|
|
|
|
if ((!player.InvBody[INVLOC_HAND_LEFT].isEmpty() && player.InvBody[INVLOC_HAND_LEFT]._iClass == player.HoldItem._iClass)
|
|
&& !(player._pClass == HeroClass::Bard && player.InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON && player.HoldItem._iClass == ICLASS_WEAPON)) {
|
|
NetSendCmdChItem(false, INVLOC_HAND_LEFT);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_LEFT], &player.HoldItem);
|
|
break;
|
|
}
|
|
NetSendCmdChItem(false, INVLOC_HAND_RIGHT);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_RIGHT], &player.HoldItem);
|
|
break;
|
|
case ILOC_TWOHAND:
|
|
if (!player.InvBody[INVLOC_HAND_LEFT].isEmpty() && !player.InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
|
|
ItemStruct tempitem = player.HoldItem;
|
|
if (player.InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD)
|
|
player.HoldItem = player.InvBody[INVLOC_HAND_RIGHT];
|
|
else
|
|
player.HoldItem = player.InvBody[INVLOC_HAND_LEFT];
|
|
if (pnum == myplr)
|
|
NewCursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
else
|
|
SetICursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
bool done2h = AutoPlaceItemInInventory(player, player.HoldItem, true);
|
|
player.HoldItem = tempitem;
|
|
if (pnum == myplr)
|
|
NewCursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
else
|
|
SetICursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
if (!done2h)
|
|
return;
|
|
|
|
if (player.InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD)
|
|
player.InvBody[INVLOC_HAND_RIGHT]._itype = ITYPE_NONE;
|
|
else
|
|
player.InvBody[INVLOC_HAND_LEFT]._itype = ITYPE_NONE;
|
|
}
|
|
|
|
NetSendCmdDelItem(false, INVLOC_HAND_RIGHT);
|
|
|
|
if (!player.InvBody[INVLOC_HAND_LEFT].isEmpty() || !player.InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
|
|
NetSendCmdChItem(false, INVLOC_HAND_LEFT);
|
|
if (player.InvBody[INVLOC_HAND_LEFT].isEmpty())
|
|
SwapItem(&player.InvBody[INVLOC_HAND_LEFT], &player.InvBody[INVLOC_HAND_RIGHT]);
|
|
cn = SwapItem(&player.InvBody[INVLOC_HAND_LEFT], &player.HoldItem);
|
|
} else {
|
|
NetSendCmdChItem(false, INVLOC_HAND_LEFT);
|
|
player.InvBody[INVLOC_HAND_LEFT] = player.HoldItem;
|
|
}
|
|
if (player.InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_STAFF && player.InvBody[INVLOC_HAND_LEFT]._iSpell != SPL_NULL && player.InvBody[INVLOC_HAND_LEFT]._iCharges > 0) {
|
|
player._pRSpell = player.InvBody[INVLOC_HAND_LEFT]._iSpell;
|
|
player._pRSplType = RSPLTYPE_CHARGES;
|
|
force_redraw = 255;
|
|
}
|
|
break;
|
|
case ILOC_ARMOR:
|
|
NetSendCmdChItem(false, INVLOC_CHEST);
|
|
if (player.InvBody[INVLOC_CHEST].isEmpty())
|
|
player.InvBody[INVLOC_CHEST] = player.HoldItem;
|
|
else
|
|
cn = SwapItem(&player.InvBody[INVLOC_CHEST], &player.HoldItem);
|
|
break;
|
|
case ILOC_UNEQUIPABLE:
|
|
if (player.HoldItem._itype == ITYPE_GOLD && it == 0) {
|
|
int ii = r - SLOTXY_INV_FIRST;
|
|
int yy = 10 * (ii / 10);
|
|
int xx = ii % 10;
|
|
if (player.InvGrid[yy + xx] > 0) {
|
|
int il = player.InvGrid[yy + xx] - 1;
|
|
int gt = player.InvList[il]._ivalue;
|
|
int ig = player.HoldItem._ivalue + gt;
|
|
if (ig <= MaxGold) {
|
|
player.InvList[il]._ivalue = ig;
|
|
player._pGold += player.HoldItem._ivalue;
|
|
SetPlrHandGoldCurs(&player.InvList[il]);
|
|
} else {
|
|
ig = MaxGold - gt;
|
|
player._pGold += ig;
|
|
player.HoldItem._ivalue -= ig;
|
|
player.InvList[il]._ivalue = MaxGold;
|
|
player.InvList[il]._iCurs = ICURS_GOLD_LARGE;
|
|
// BUGFIX: incorrect values here are leftover from beta (fixed)
|
|
cn = GetGoldCursor(player.HoldItem._ivalue);
|
|
cn += CURSOR_FIRSTITEM;
|
|
}
|
|
} else {
|
|
int il = player._pNumInv;
|
|
player.InvList[il] = player.HoldItem;
|
|
player._pNumInv++;
|
|
player.InvGrid[yy + xx] = player._pNumInv;
|
|
player._pGold += player.HoldItem._ivalue;
|
|
SetPlrHandGoldCurs(&player.InvList[il]);
|
|
}
|
|
} else {
|
|
if (it == 0) {
|
|
player.InvList[player._pNumInv] = player.HoldItem;
|
|
player._pNumInv++;
|
|
it = player._pNumInv;
|
|
} else {
|
|
int il = it - 1;
|
|
if (player.HoldItem._itype == ITYPE_GOLD)
|
|
player._pGold += player.HoldItem._ivalue;
|
|
cn = SwapItem(&player.InvList[il], &player.HoldItem);
|
|
if (player.HoldItem._itype == ITYPE_GOLD)
|
|
player._pGold = CalculateGold(player);
|
|
for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
|
|
if (player.InvGrid[i] == it)
|
|
player.InvGrid[i] = 0;
|
|
if (player.InvGrid[i] == -it)
|
|
player.InvGrid[i] = 0;
|
|
}
|
|
}
|
|
int ii = r - SLOTXY_INV_FIRST;
|
|
|
|
// Calculate top-left position of item for InvGrid and then add item to InvGrid
|
|
|
|
int xx = std::max(ii % 10 - ((sx - 1) / 2), 0);
|
|
int yy = std::max(10 * (ii / 10 - ((sy - 1) / 2)), 0);
|
|
AddItemToInvGrid(player, xx + yy, it, sx, sy);
|
|
}
|
|
break;
|
|
case ILOC_BELT: {
|
|
int ii = r - SLOTXY_BELT_FIRST;
|
|
if (player.HoldItem._itype == ITYPE_GOLD) {
|
|
if (!player.SpdList[ii].isEmpty()) {
|
|
if (player.SpdList[ii]._itype == ITYPE_GOLD) {
|
|
i = player.HoldItem._ivalue + player.SpdList[ii]._ivalue;
|
|
if (i <= MaxGold) {
|
|
player.SpdList[ii]._ivalue = i;
|
|
player._pGold += player.HoldItem._ivalue;
|
|
SetPlrHandGoldCurs(&player.SpdList[ii]);
|
|
} else {
|
|
i = MaxGold - player.SpdList[ii]._ivalue;
|
|
player._pGold += i;
|
|
player.HoldItem._ivalue -= i;
|
|
player.SpdList[ii]._ivalue = MaxGold;
|
|
player.SpdList[ii]._iCurs = ICURS_GOLD_LARGE;
|
|
|
|
// BUGFIX: incorrect values here are leftover from beta (fixed)
|
|
cn = GetGoldCursor(player.HoldItem._ivalue);
|
|
cn += CURSOR_FIRSTITEM;
|
|
}
|
|
} else {
|
|
player._pGold += player.HoldItem._ivalue;
|
|
cn = SwapItem(&player.SpdList[ii], &player.HoldItem);
|
|
}
|
|
} else {
|
|
player.SpdList[ii] = player.HoldItem;
|
|
player._pGold += player.HoldItem._ivalue;
|
|
}
|
|
} else if (player.SpdList[ii].isEmpty()) {
|
|
player.SpdList[ii] = player.HoldItem;
|
|
} else {
|
|
cn = SwapItem(&player.SpdList[ii], &player.HoldItem);
|
|
if (player.HoldItem._itype == ITYPE_GOLD)
|
|
player._pGold = CalculateGold(player);
|
|
}
|
|
drawsbarflag = true;
|
|
} break;
|
|
case ILOC_NONE:
|
|
case ILOC_INVALID:
|
|
break;
|
|
}
|
|
CalcPlrInv(pnum, true);
|
|
if (pnum == myplr) {
|
|
if (cn == CURSOR_HAND)
|
|
SetCursorPos(MouseX + (cursW / 2), MouseY + (cursH / 2));
|
|
NewCursor(cn);
|
|
}
|
|
}
|
|
|
|
void CheckInvSwap(int pnum, BYTE bLoc, int idx, uint16_t wCI, int seed, bool bId, uint32_t dwBuff)
|
|
{
|
|
memset(&items[MAXITEMS], 0, sizeof(*items));
|
|
RecreateItem(MAXITEMS, idx, wCI, seed, 0, (dwBuff & CF_HELLFIRE) != 0);
|
|
|
|
auto &player = plr[pnum];
|
|
|
|
player.HoldItem = items[MAXITEMS];
|
|
|
|
if (bId) {
|
|
player.HoldItem._iIdentified = true;
|
|
}
|
|
|
|
if (bLoc < NUM_INVLOC) {
|
|
player.InvBody[bLoc] = player.HoldItem;
|
|
|
|
if (bLoc == INVLOC_HAND_LEFT && player.HoldItem._iLoc == ILOC_TWOHAND) {
|
|
player.InvBody[INVLOC_HAND_RIGHT]._itype = ITYPE_NONE;
|
|
} else if (bLoc == INVLOC_HAND_RIGHT && player.HoldItem._iLoc == ILOC_TWOHAND) {
|
|
player.InvBody[INVLOC_HAND_LEFT]._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
CalcPlrInv(pnum, true);
|
|
}
|
|
|
|
void CheckInvCut(int pnum, int mx, int my, bool automaticMove)
|
|
{
|
|
auto &player = plr[pnum];
|
|
|
|
if (player._pmode > PM_WALK3) {
|
|
return;
|
|
}
|
|
|
|
if (dropGoldFlag) {
|
|
dropGoldFlag = false;
|
|
dropGoldValue = 0;
|
|
}
|
|
|
|
bool done = false;
|
|
|
|
int r = 0;
|
|
for (; (DWORD)r < NUM_XY_SLOTS && !done; r++) {
|
|
int xo = RIGHT_PANEL;
|
|
int yo = 0;
|
|
if (r >= SLOTXY_BELT_FIRST) {
|
|
xo = PANEL_LEFT;
|
|
yo = PANEL_TOP;
|
|
}
|
|
|
|
// check which inventory rectangle the mouse is in, if any
|
|
if (mx >= InvRect[r].X + xo
|
|
&& mx < InvRect[r].X + xo + (INV_SLOT_SIZE_PX + 1)
|
|
&& my >= InvRect[r].Y + yo - (INV_SLOT_SIZE_PX + 1)
|
|
&& my < InvRect[r].Y + yo) {
|
|
done = true;
|
|
r--;
|
|
}
|
|
}
|
|
|
|
if (!done) {
|
|
// not on an inventory slot rectangle
|
|
return;
|
|
}
|
|
|
|
ItemStruct &holdItem = player.HoldItem;
|
|
holdItem._itype = ITYPE_NONE;
|
|
|
|
bool automaticallyMoved = false;
|
|
bool automaticallyEquipped = false;
|
|
bool automaticallyUnequip = false;
|
|
|
|
ItemStruct &headItem = player.InvBody[INVLOC_HEAD];
|
|
if (r >= SLOTXY_HEAD_FIRST && r <= SLOTXY_HEAD_LAST && !headItem.isEmpty()) {
|
|
holdItem = headItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_HEAD);
|
|
headItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
ItemStruct &leftRingItem = player.InvBody[INVLOC_RING_LEFT];
|
|
if (r == SLOTXY_RING_LEFT && !leftRingItem.isEmpty()) {
|
|
holdItem = leftRingItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_RING_LEFT);
|
|
leftRingItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
ItemStruct &rightRingItem = player.InvBody[INVLOC_RING_RIGHT];
|
|
if (r == SLOTXY_RING_RIGHT && !rightRingItem.isEmpty()) {
|
|
holdItem = rightRingItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_RING_RIGHT);
|
|
rightRingItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
ItemStruct &amuletItem = player.InvBody[INVLOC_AMULET];
|
|
if (r == SLOTXY_AMULET && !amuletItem.isEmpty()) {
|
|
holdItem = amuletItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_AMULET);
|
|
amuletItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
ItemStruct &leftHandItem = player.InvBody[INVLOC_HAND_LEFT];
|
|
if (r >= SLOTXY_HAND_LEFT_FIRST && r <= SLOTXY_HAND_LEFT_LAST && !leftHandItem.isEmpty()) {
|
|
holdItem = leftHandItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_HAND_LEFT);
|
|
leftHandItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
ItemStruct &rightHandItem = player.InvBody[INVLOC_HAND_RIGHT];
|
|
if (r >= SLOTXY_HAND_RIGHT_FIRST && r <= SLOTXY_HAND_RIGHT_LAST && !rightHandItem.isEmpty()) {
|
|
holdItem = rightHandItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_HAND_RIGHT);
|
|
rightHandItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
ItemStruct &chestItem = player.InvBody[INVLOC_CHEST];
|
|
if (r >= SLOTXY_CHEST_FIRST && r <= SLOTXY_CHEST_LAST && !chestItem.isEmpty()) {
|
|
holdItem = chestItem;
|
|
if (automaticMove) {
|
|
automaticallyUnequip = true;
|
|
automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
NetSendCmdDelItem(false, INVLOC_CHEST);
|
|
chestItem._itype = ITYPE_NONE;
|
|
}
|
|
}
|
|
|
|
if (r >= SLOTXY_INV_FIRST && r <= SLOTXY_INV_LAST) {
|
|
int ig = r - SLOTXY_INV_FIRST;
|
|
int ii = player.InvGrid[ig];
|
|
if (ii != 0) {
|
|
int iv = (ii < 0) ? -ii : ii;
|
|
|
|
holdItem = player.InvList[iv - 1];
|
|
if (automaticMove) {
|
|
if (CanBePlacedOnBelt(holdItem)) {
|
|
automaticallyMoved = AutoPlaceItemInBelt(player, holdItem, true);
|
|
} else {
|
|
automaticallyMoved = automaticallyEquipped = AutoEquip(pnum, holdItem);
|
|
}
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
player.RemoveInvItem(iv - 1, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r >= SLOTXY_BELT_FIRST) {
|
|
ItemStruct &beltItem = player.SpdList[r - SLOTXY_BELT_FIRST];
|
|
if (!beltItem.isEmpty()) {
|
|
holdItem = beltItem;
|
|
if (automaticMove) {
|
|
automaticallyMoved = AutoPlaceItemInInventory(player, holdItem, true);
|
|
}
|
|
|
|
if (!automaticMove || automaticallyMoved) {
|
|
beltItem._itype = ITYPE_NONE;
|
|
drawsbarflag = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!holdItem.isEmpty()) {
|
|
if (holdItem._itype == ITYPE_GOLD) {
|
|
player._pGold = CalculateGold(player);
|
|
}
|
|
|
|
CalcPlrInv(pnum, true);
|
|
CheckItemStats(player);
|
|
|
|
if (pnum == myplr) {
|
|
if (automaticallyEquipped) {
|
|
PlaySFX(ItemInvSnds[ItemCAnimTbl[holdItem._iCurs]]);
|
|
} else if (!automaticMove || automaticallyMoved) {
|
|
PlaySFX(IS_IGRAB);
|
|
}
|
|
|
|
if (automaticMove) {
|
|
if (!automaticallyMoved) {
|
|
if (CanBePlacedOnBelt(holdItem) || automaticallyUnequip) {
|
|
player.SaySpecific(HeroSpeech::IHaveNoRoom);
|
|
} else {
|
|
player.SaySpecific(HeroSpeech::ICantDoThat);
|
|
}
|
|
}
|
|
|
|
holdItem._itype = ITYPE_NONE;
|
|
} else {
|
|
NewCursor(holdItem._iCurs + CURSOR_FIRSTITEM);
|
|
SetCursorPos(mx - (cursW / 2), MouseY - (cursH / 2));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void inv_update_rem_item(int pnum, BYTE iv)
|
|
{
|
|
auto &player = plr[pnum];
|
|
|
|
if (iv < NUM_INVLOC) {
|
|
player.InvBody[iv]._itype = ITYPE_NONE;
|
|
}
|
|
|
|
CalcPlrInv(pnum, player._pmode != PM_DEATH);
|
|
}
|
|
|
|
void CheckInvItem(bool isShiftHeld)
|
|
{
|
|
if (pcurs >= CURSOR_FIRSTITEM) {
|
|
CheckInvPaste(myplr, MouseX, MouseY);
|
|
} else {
|
|
CheckInvCut(myplr, MouseX, MouseY, isShiftHeld);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for interactions with belt
|
|
*/
|
|
void CheckInvScrn(bool isShiftHeld)
|
|
{
|
|
if (MouseX > 190 + PANEL_LEFT && MouseX < 437 + PANEL_LEFT
|
|
&& MouseY > PANEL_TOP && MouseY < 33 + PANEL_TOP) {
|
|
CheckInvItem(isShiftHeld);
|
|
}
|
|
}
|
|
|
|
void CheckItemStats(PlayerStruct &player)
|
|
{
|
|
ItemStruct &item = player.HoldItem;
|
|
|
|
item._iStatFlag = false;
|
|
|
|
if (player._pStrength >= item._iMinStr
|
|
&& player._pMagic >= item._iMinMag
|
|
&& player._pDexterity >= item._iMinDex) {
|
|
item._iStatFlag = true;
|
|
}
|
|
}
|
|
|
|
static void CheckBookLevel(PlayerStruct &player)
|
|
{
|
|
if (player.HoldItem._iMiscId != IMISC_BOOK)
|
|
return;
|
|
|
|
player.HoldItem._iMinMag = spelldata[player.HoldItem._iSpell].sMinInt;
|
|
int slvl = player._pSplLvl[player.HoldItem._iSpell];
|
|
while (slvl != 0) {
|
|
player.HoldItem._iMinMag += 20 * player.HoldItem._iMinMag / 100;
|
|
slvl--;
|
|
if (player.HoldItem._iMinMag + 20 * player.HoldItem._iMinMag / 100 > 255) {
|
|
player.HoldItem._iMinMag = -1;
|
|
slvl = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CheckNaKrulNotes(PlayerStruct &player)
|
|
{
|
|
int idx = player.HoldItem.IDidx;
|
|
|
|
if (idx != IDI_NOTE1 && idx != IDI_NOTE2 && idx != IDI_NOTE3) {
|
|
return;
|
|
}
|
|
|
|
int n1;
|
|
if (idx != IDI_NOTE1 && !player.HasItem(IDI_NOTE1, &n1)) {
|
|
return;
|
|
}
|
|
|
|
int n2;
|
|
if (idx != IDI_NOTE2 && !player.HasItem(IDI_NOTE2, &n2)) {
|
|
return;
|
|
}
|
|
|
|
int n3;
|
|
if (idx != IDI_NOTE3 && !player.HasItem(IDI_NOTE3, &n3)) {
|
|
return;
|
|
}
|
|
|
|
plr[myplr].Say(HeroSpeech::JustWhatIWasLookingFor, 10);
|
|
|
|
if (idx != IDI_NOTE1) {
|
|
player.RemoveInvItem(n1);
|
|
}
|
|
if (idx != IDI_NOTE2) {
|
|
player.RemoveInvItem(n2);
|
|
}
|
|
if (idx != IDI_NOTE3) {
|
|
player.RemoveInvItem(n3);
|
|
}
|
|
|
|
int itemNum = itemactive[0];
|
|
ItemStruct tmp = items[itemNum];
|
|
memset(&items[itemNum], 0, sizeof(*items));
|
|
GetItemAttrs(itemNum, IDI_FULLNOTE, 16);
|
|
SetupItem(itemNum);
|
|
player.HoldItem = items[itemNum];
|
|
items[itemNum] = tmp;
|
|
}
|
|
|
|
static void CheckQuestItem(PlayerStruct &player)
|
|
{
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
if (player.HoldItem.IDidx == IDI_OPTAMULET && quests[Q_BLIND]._qactive == QUEST_ACTIVE)
|
|
quests[Q_BLIND]._qactive = QUEST_DONE;
|
|
|
|
if (player.HoldItem.IDidx == IDI_MUSHROOM && quests[Q_MUSHROOM]._qactive == QUEST_ACTIVE && quests[Q_MUSHROOM]._qvar1 == QS_MUSHSPAWNED) {
|
|
player.Say(HeroSpeech::NowThatsOneBigMushroom, 10); // BUGFIX: Voice for this quest might be wrong in MP
|
|
quests[Q_MUSHROOM]._qvar1 = QS_MUSHPICKED;
|
|
}
|
|
|
|
if (player.HoldItem.IDidx == IDI_ANVIL && quests[Q_ANVIL]._qactive != QUEST_NOTAVAIL) {
|
|
if (quests[Q_ANVIL]._qactive == QUEST_INIT) {
|
|
quests[Q_ANVIL]._qactive = QUEST_ACTIVE;
|
|
}
|
|
if (quests[Q_ANVIL]._qlog) {
|
|
myPlayer.Say(HeroSpeech::INeedToGetThisToGriswold, 10);
|
|
}
|
|
}
|
|
|
|
if (player.HoldItem.IDidx == IDI_GLDNELIX && quests[Q_VEIL]._qactive != QUEST_NOTAVAIL) {
|
|
myPlayer.Say(HeroSpeech::INeedToGetThisToLachdanan, 30);
|
|
}
|
|
|
|
if (player.HoldItem.IDidx == IDI_ROCK && quests[Q_ROCK]._qactive != QUEST_NOTAVAIL) {
|
|
if (quests[Q_ROCK]._qactive == QUEST_INIT) {
|
|
quests[Q_ROCK]._qactive = QUEST_ACTIVE;
|
|
}
|
|
if (quests[Q_ROCK]._qlog) {
|
|
myPlayer.Say(HeroSpeech::ThisMustBeWhatGriswoldWanted, 10);
|
|
}
|
|
}
|
|
|
|
if (player.HoldItem.IDidx == IDI_ARMOFVAL && quests[Q_BLOOD]._qactive == QUEST_ACTIVE) {
|
|
quests[Q_BLOOD]._qactive = QUEST_DONE;
|
|
myPlayer.Say(HeroSpeech::MayTheSpiritOfArkaineProtectMe, 20);
|
|
}
|
|
|
|
if (player.HoldItem.IDidx == IDI_MAPOFDOOM) {
|
|
quests[Q_GRAVE]._qlog = false;
|
|
quests[Q_GRAVE]._qactive = QUEST_ACTIVE;
|
|
quests[Q_GRAVE]._qvar1 = 1;
|
|
plr[myplr].Say(HeroSpeech::UhHuh, 10);
|
|
}
|
|
|
|
CheckNaKrulNotes(player);
|
|
}
|
|
|
|
void CleanupItems(ItemStruct *item, int ii)
|
|
{
|
|
dItem[item->position.x][item->position.y] = 0;
|
|
|
|
if (currlevel == 21 && item->position == CornerStone.position) {
|
|
CornerStone.item._itype = ITYPE_NONE;
|
|
CornerStone.item._iSelFlag = 0;
|
|
CornerStone.item.position = { 0, 0 };
|
|
CornerStone.item._iAnimFlag = false;
|
|
CornerStone.item._iIdentified = false;
|
|
CornerStone.item._iPostDraw = false;
|
|
}
|
|
|
|
int i = 0;
|
|
while (i < numitems) {
|
|
if (itemactive[i] == ii) {
|
|
DeleteItem(itemactive[i], i);
|
|
i = 0;
|
|
continue;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void InvGetItem(int pnum, ItemStruct *item, int ii)
|
|
{
|
|
if (dropGoldFlag) {
|
|
dropGoldFlag = false;
|
|
dropGoldValue = 0;
|
|
}
|
|
|
|
if (dItem[item->position.x][item->position.y] == 0)
|
|
return;
|
|
|
|
auto &player = plr[pnum];
|
|
|
|
if (myplr == pnum && pcurs >= CURSOR_FIRSTITEM)
|
|
NetSendCmdPItem(true, CMD_SYNCPUTITEM, player.position.tile);
|
|
|
|
item->_iCreateInfo &= ~CF_PREGEN;
|
|
player.HoldItem = *item;
|
|
CheckQuestItem(player);
|
|
CheckBookLevel(player);
|
|
CheckItemStats(player);
|
|
bool cursorUpdated = false;
|
|
if (player.HoldItem._itype == ITYPE_GOLD && GoldAutoPlace(player))
|
|
cursorUpdated = true;
|
|
CleanupItems(item, ii);
|
|
pcursitem = -1;
|
|
if (!cursorUpdated)
|
|
NewCursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
}
|
|
|
|
void AutoGetItem(int pnum, ItemStruct *item, int ii)
|
|
{
|
|
bool done;
|
|
|
|
if (pcurs != CURSOR_HAND) {
|
|
return;
|
|
}
|
|
|
|
if (dropGoldFlag) {
|
|
dropGoldFlag = false;
|
|
dropGoldValue = 0;
|
|
}
|
|
|
|
if (dItem[item->position.x][item->position.y] == 0)
|
|
return;
|
|
|
|
auto &player = plr[pnum];
|
|
|
|
item->_iCreateInfo &= ~CF_PREGEN;
|
|
player.HoldItem = *item; /// BUGFIX: overwrites cursor item, allowing for belt dupe bug
|
|
CheckQuestItem(player);
|
|
CheckBookLevel(player);
|
|
CheckItemStats(player);
|
|
SetICursor(player.HoldItem._iCurs + CURSOR_FIRSTITEM);
|
|
if (player.HoldItem._itype == ITYPE_GOLD) {
|
|
done = GoldAutoPlace(player);
|
|
if (!done) {
|
|
item->_ivalue = player.HoldItem._ivalue;
|
|
SetPlrHandGoldCurs(item);
|
|
}
|
|
} else {
|
|
done = AutoEquipEnabled(player, player.HoldItem) && AutoEquip(pnum, player.HoldItem);
|
|
if (!done) {
|
|
done = AutoPlaceItemInBelt(player, player.HoldItem, true);
|
|
}
|
|
if (!done) {
|
|
done = AutoPlaceItemInInventory(player, player.HoldItem, true);
|
|
}
|
|
}
|
|
|
|
if (done) {
|
|
CleanupItems(&items[ii], ii);
|
|
return;
|
|
}
|
|
|
|
if (pnum == myplr) {
|
|
player.Say(HeroSpeech::ICantCarryAnymore);
|
|
}
|
|
player.HoldItem = *item;
|
|
RespawnItem(item, true);
|
|
NetSendCmdPItem(true, CMD_RESPAWNITEM, item->position);
|
|
player.HoldItem._itype = ITYPE_NONE;
|
|
}
|
|
|
|
int FindGetItem(int idx, uint16_t ci, int iseed)
|
|
{
|
|
if (numitems <= 0)
|
|
return -1;
|
|
|
|
int ii;
|
|
int i = 0;
|
|
while (true) {
|
|
ii = itemactive[i];
|
|
if (items[ii].IDidx == idx && items[ii]._iSeed == iseed && items[ii]._iCreateInfo == ci)
|
|
break;
|
|
|
|
i++;
|
|
|
|
if (i >= numitems)
|
|
return -1;
|
|
}
|
|
|
|
return ii;
|
|
}
|
|
|
|
void SyncGetItem(int x, int y, int idx, uint16_t ci, int iseed)
|
|
{
|
|
int ii;
|
|
|
|
if (dItem[x][y] != 0) {
|
|
ii = dItem[x][y] - 1;
|
|
if (items[ii].IDidx == idx
|
|
&& items[ii]._iSeed == iseed
|
|
&& items[ii]._iCreateInfo == ci) {
|
|
FindGetItem(idx, ci, iseed);
|
|
} else {
|
|
ii = FindGetItem(idx, ci, iseed);
|
|
}
|
|
} else {
|
|
ii = FindGetItem(idx, ci, iseed);
|
|
}
|
|
|
|
if (ii == -1)
|
|
return;
|
|
|
|
CleanupItems(&items[ii], ii);
|
|
assert(FindGetItem(idx, ci, iseed) == -1);
|
|
}
|
|
|
|
bool CanPut(int x, int y)
|
|
{
|
|
if (dItem[x][y] != 0)
|
|
return false;
|
|
if (nSolidTable[dPiece[x][y]])
|
|
return false;
|
|
|
|
if (dObject[x][y] != 0) {
|
|
if (object[dObject[x][y] > 0 ? dObject[x][y] - 1 : -(dObject[x][y] + 1)]._oSolidFlag)
|
|
return false;
|
|
}
|
|
|
|
int8_t oi = dObject[x + 1][y + 1];
|
|
if (oi > 0 && object[oi - 1]._oSelFlag != 0) {
|
|
return false;
|
|
}
|
|
if (oi < 0 && object[-(oi + 1)]._oSelFlag != 0) {
|
|
return false;
|
|
}
|
|
|
|
oi = dObject[x + 1][y];
|
|
if (oi > 0) {
|
|
int8_t oi2 = dObject[x][y + 1];
|
|
if (oi2 > 0 && object[oi - 1]._oSelFlag != 0 && object[oi2 - 1]._oSelFlag != 0)
|
|
return false;
|
|
}
|
|
|
|
if (currlevel == 0 && dMonster[x][y] != 0)
|
|
return false;
|
|
if (currlevel == 0 && dMonster[x + 1][y + 1] != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TryInvPut()
|
|
{
|
|
if (numitems >= MAXITEMS)
|
|
return false;
|
|
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
Direction dir = GetDirection(myPlayer.position.tile, { cursmx, cursmy });
|
|
Point position = myPlayer.position.tile + dir;
|
|
if (CanPut(position.x, position.y)) {
|
|
return true;
|
|
}
|
|
|
|
position = myPlayer.position.tile + left[dir];
|
|
if (CanPut(position.x, position.y)) {
|
|
return true;
|
|
}
|
|
|
|
position = myPlayer.position.tile + right[dir];
|
|
if (CanPut(position.x, position.y)) {
|
|
return true;
|
|
}
|
|
|
|
return CanPut(myPlayer.position.tile.x, myPlayer.position.tile.y);
|
|
}
|
|
|
|
void DrawInvMsg(const char *msg)
|
|
{
|
|
uint32_t dwTicks = SDL_GetTicks();
|
|
if (dwTicks - sgdwLastTime >= 5000) {
|
|
sgdwLastTime = dwTicks;
|
|
ErrorPlrMsg(msg);
|
|
}
|
|
}
|
|
|
|
static bool PutItem(PlayerStruct &player, Point &position)
|
|
{
|
|
if (numitems >= MAXITEMS)
|
|
return false;
|
|
|
|
auto relativePosition = position - player.position.tile;
|
|
|
|
Direction d = GetDirection(player.position.tile, position);
|
|
|
|
if (abs(relativePosition.x) > 1 || abs(relativePosition.y) > 1) {
|
|
position = player.position.tile + d;
|
|
}
|
|
if (CanPut(position.x, position.y))
|
|
return true;
|
|
|
|
position = player.position.tile + left[d];
|
|
if (CanPut(position.x, position.y))
|
|
return true;
|
|
|
|
position = player.position.tile + right[d];
|
|
if (CanPut(position.x, position.y))
|
|
return true;
|
|
|
|
for (int l = 1; l < 50; l++) {
|
|
for (int j = -l; j <= l; j++) {
|
|
int yp = j + player.position.tile.y;
|
|
for (int i = -l; i <= l; i++) {
|
|
int xp = i + player.position.tile.x;
|
|
if (!CanPut(xp, yp))
|
|
continue;
|
|
|
|
position = { xp, yp };
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int InvPutItem(PlayerStruct &player, Point position)
|
|
{
|
|
if (!PutItem(player, position))
|
|
return -1;
|
|
|
|
if (currlevel == 0) {
|
|
int yp = cursmy;
|
|
int xp = cursmx;
|
|
if (player.HoldItem._iCurs == ICURS_RUNE_BOMB && xp >= 79 && xp <= 82 && yp >= 61 && yp <= 64) {
|
|
Point relativePosition = position - player.position.tile;
|
|
NetSendCmdLocParam2(false, CMD_OPENHIVE, player.position.tile, relativePosition.x, relativePosition.y);
|
|
quests[Q_FARMER]._qactive = QUEST_DONE;
|
|
if (gbIsMultiplayer) {
|
|
NetSendCmdQuest(true, Q_FARMER);
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
if (player.HoldItem.IDidx == IDI_MAPOFDOOM && xp >= 35 && xp <= 38 && yp >= 20 && yp <= 24) {
|
|
NetSendCmd(false, CMD_OPENCRYPT);
|
|
quests[Q_GRAVE]._qactive = QUEST_DONE;
|
|
if (gbIsMultiplayer) {
|
|
NetSendCmdQuest(true, Q_GRAVE);
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
assert(CanPut(position.x, position.y));
|
|
|
|
int ii = AllocateItem();
|
|
|
|
dItem[position.x][position.y] = ii + 1;
|
|
items[ii] = player.HoldItem;
|
|
items[ii].position = position;
|
|
RespawnItem(&items[ii], true);
|
|
|
|
if (currlevel == 21 && position == CornerStone.position) {
|
|
CornerStone.item = items[ii];
|
|
InitQTextMsg(TEXT_CORNSTN);
|
|
quests[Q_CORNSTN]._qlog = false;
|
|
quests[Q_CORNSTN]._qactive = QUEST_DONE;
|
|
}
|
|
|
|
NewCursor(CURSOR_HAND);
|
|
return ii;
|
|
}
|
|
|
|
int SyncPutItem(PlayerStruct &player, Point position, int idx, uint16_t icreateinfo, int iseed, int id, int dur, int mdur, int ch, int mch, int ivalue, DWORD ibuff, int toHit, int maxDam, int minStr, int minMag, int minDex, int ac)
|
|
{
|
|
if (!PutItem(player, position))
|
|
return -1;
|
|
|
|
assert(CanPut(position.x, position.y));
|
|
|
|
int ii = AllocateItem();
|
|
|
|
dItem[position.x][position.y] = ii + 1;
|
|
|
|
if (idx == IDI_EAR) {
|
|
RecreateEar(ii, icreateinfo, iseed, id, dur, mdur, ch, mch, ivalue, ibuff);
|
|
} else {
|
|
RecreateItem(ii, idx, icreateinfo, iseed, ivalue, (ibuff & CF_HELLFIRE) != 0);
|
|
if (id != 0)
|
|
items[ii]._iIdentified = true;
|
|
items[ii]._iDurability = dur;
|
|
items[ii]._iMaxDur = mdur;
|
|
items[ii]._iCharges = ch;
|
|
items[ii]._iMaxCharges = mch;
|
|
items[ii]._iPLToHit = toHit;
|
|
items[ii]._iMaxDam = maxDam;
|
|
items[ii]._iMinStr = minStr;
|
|
items[ii]._iMinMag = minMag;
|
|
items[ii]._iMinDex = minDex;
|
|
items[ii]._iAC = ac;
|
|
items[ii].dwBuff = ibuff;
|
|
}
|
|
|
|
items[ii].position = position;
|
|
RespawnItem(&items[ii], true);
|
|
|
|
if (currlevel == 21 && position == CornerStone.position) {
|
|
CornerStone.item = items[ii];
|
|
InitQTextMsg(TEXT_CORNSTN);
|
|
quests[Q_CORNSTN]._qlog = false;
|
|
quests[Q_CORNSTN]._qactive = QUEST_DONE;
|
|
}
|
|
return ii;
|
|
}
|
|
|
|
char CheckInvHLight()
|
|
{
|
|
int r = 0;
|
|
for (; (DWORD)r < NUM_XY_SLOTS; r++) {
|
|
int xo = RIGHT_PANEL;
|
|
int yo = 0;
|
|
if (r >= SLOTXY_BELT_FIRST) {
|
|
xo = PANEL_LEFT;
|
|
yo = PANEL_TOP;
|
|
}
|
|
|
|
if (MouseX >= InvRect[r].X + xo
|
|
&& MouseX < InvRect[r].X + xo + (INV_SLOT_SIZE_PX + 1)
|
|
&& MouseY >= InvRect[r].Y + yo - (INV_SLOT_SIZE_PX + 1)
|
|
&& MouseY < InvRect[r].Y + yo) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((DWORD)r >= NUM_XY_SLOTS)
|
|
return -1;
|
|
|
|
int8_t rv = -1;
|
|
infoclr = UIS_SILVER;
|
|
ItemStruct *pi = nullptr;
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
ClearPanel();
|
|
if (r >= SLOTXY_HEAD_FIRST && r <= SLOTXY_HEAD_LAST) {
|
|
rv = INVLOC_HEAD;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else if (r == SLOTXY_RING_LEFT) {
|
|
rv = INVLOC_RING_LEFT;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else if (r == SLOTXY_RING_RIGHT) {
|
|
rv = INVLOC_RING_RIGHT;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else if (r == SLOTXY_AMULET) {
|
|
rv = INVLOC_AMULET;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else if (r >= SLOTXY_HAND_LEFT_FIRST && r <= SLOTXY_HAND_LEFT_LAST) {
|
|
rv = INVLOC_HAND_LEFT;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else if (r >= SLOTXY_HAND_RIGHT_FIRST && r <= SLOTXY_HAND_RIGHT_LAST) {
|
|
pi = &myPlayer.InvBody[INVLOC_HAND_LEFT];
|
|
if (pi->isEmpty() || pi->_iLoc != ILOC_TWOHAND
|
|
|| (myPlayer._pClass == HeroClass::Barbarian && (myPlayer.InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SWORD || myPlayer.InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_MACE))) {
|
|
rv = INVLOC_HAND_RIGHT;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else {
|
|
rv = INVLOC_HAND_LEFT;
|
|
}
|
|
} else if (r >= SLOTXY_CHEST_FIRST && r <= SLOTXY_CHEST_LAST) {
|
|
rv = INVLOC_CHEST;
|
|
pi = &myPlayer.InvBody[rv];
|
|
} else if (r >= SLOTXY_INV_FIRST && r <= SLOTXY_INV_LAST) {
|
|
r = abs(myPlayer.InvGrid[r - SLOTXY_INV_FIRST]);
|
|
if (r == 0)
|
|
return -1;
|
|
int ii = r - 1;
|
|
rv = ii + INVITEM_INV_FIRST;
|
|
pi = &myPlayer.InvList[ii];
|
|
} else if (r >= SLOTXY_BELT_FIRST) {
|
|
r -= SLOTXY_BELT_FIRST;
|
|
drawsbarflag = true;
|
|
pi = &myPlayer.SpdList[r];
|
|
if (pi->isEmpty())
|
|
return -1;
|
|
rv = r + INVITEM_BELT_FIRST;
|
|
}
|
|
|
|
if (pi->isEmpty())
|
|
return -1;
|
|
|
|
if (pi->_itype == ITYPE_GOLD) {
|
|
int nGold = pi->_ivalue;
|
|
strcpy(infostr, fmt::format(ngettext("{:d} gold piece", "{:d} gold pieces", nGold), nGold).c_str());
|
|
} else {
|
|
if (pi->_iMagical == ITEM_QUALITY_MAGIC) {
|
|
infoclr = UIS_BLUE;
|
|
} else if (pi->_iMagical == ITEM_QUALITY_UNIQUE) {
|
|
infoclr = UIS_GOLD;
|
|
}
|
|
if (pi->_iIdentified) {
|
|
strcpy(infostr, pi->_iIName);
|
|
PrintItemDetails(pi);
|
|
} else {
|
|
strcpy(infostr, pi->_iName);
|
|
PrintItemDur(pi);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void RemoveScroll(int pnum)
|
|
{
|
|
auto &player = plr[pnum];
|
|
|
|
for (int i = 0; i < player._pNumInv; i++) {
|
|
if (!player.InvList[i].isEmpty()
|
|
&& (player.InvList[i]._iMiscId == IMISC_SCROLL || player.InvList[i]._iMiscId == IMISC_SCROLLT)
|
|
&& player.InvList[i]._iSpell == player._pRSpell) {
|
|
player.RemoveInvItem(i);
|
|
player.CalcScrolls();
|
|
return;
|
|
}
|
|
}
|
|
for (int i = 0; i < MAXBELTITEMS; i++) {
|
|
if (!player.SpdList[i].isEmpty()
|
|
&& (player.SpdList[i]._iMiscId == IMISC_SCROLL || player.SpdList[i]._iMiscId == IMISC_SCROLLT)
|
|
&& player.SpdList[i]._iSpell == player._pSpell) {
|
|
player.RemoveSpdBarItem(i);
|
|
player.CalcScrolls();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UseScroll()
|
|
{
|
|
if (pcurs != CURSOR_HAND)
|
|
return false;
|
|
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
if (leveltype == DTYPE_TOWN && !spelldata[myPlayer._pRSpell].sTownSpell)
|
|
return false;
|
|
|
|
for (int i = 0; i < myPlayer._pNumInv; i++) {
|
|
if (!myPlayer.InvList[i].isEmpty()
|
|
&& (myPlayer.InvList[i]._iMiscId == IMISC_SCROLL || myPlayer.InvList[i]._iMiscId == IMISC_SCROLLT)
|
|
&& myPlayer.InvList[i]._iSpell == myPlayer._pRSpell) {
|
|
return true;
|
|
}
|
|
}
|
|
for (auto &item : myPlayer.SpdList) {
|
|
if (!item.isEmpty()
|
|
&& (item._iMiscId == IMISC_SCROLL || item._iMiscId == IMISC_SCROLLT)
|
|
&& item._iSpell == myPlayer._pRSpell) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool CanUseStaff(ItemStruct &staff, spell_id spell)
|
|
{
|
|
return !staff.isEmpty()
|
|
&& (staff._iMiscId == IMISC_STAFF || staff._iMiscId == IMISC_UNIQUE)
|
|
&& staff._iSpell == spell
|
|
&& staff._iCharges > 0;
|
|
}
|
|
|
|
void UseStaffCharge(int pnum)
|
|
{
|
|
auto &player = plr[pnum];
|
|
auto &staff = player.InvBody[INVLOC_HAND_LEFT];
|
|
|
|
if (!CanUseStaff(staff, player._pRSpell))
|
|
return;
|
|
|
|
staff._iCharges--;
|
|
CalcPlrStaff(pnum);
|
|
}
|
|
|
|
bool UseStaff()
|
|
{
|
|
if (pcurs != CURSOR_HAND) {
|
|
return false;
|
|
}
|
|
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
return CanUseStaff(myPlayer.InvBody[INVLOC_HAND_LEFT], myPlayer._pRSpell);
|
|
}
|
|
|
|
void StartGoldDrop()
|
|
{
|
|
initialDropGoldIndex = pcursinvitem;
|
|
|
|
auto &myPlayer = plr[myplr];
|
|
|
|
if (pcursinvitem <= INVITEM_INV_LAST)
|
|
initialDropGoldValue = myPlayer.InvList[pcursinvitem - INVITEM_INV_FIRST]._ivalue;
|
|
else
|
|
initialDropGoldValue = myPlayer.SpdList[pcursinvitem - INVITEM_BELT_FIRST]._ivalue;
|
|
|
|
dropGoldFlag = true;
|
|
dropGoldValue = 0;
|
|
|
|
if (talkflag)
|
|
control_reset_talk();
|
|
}
|
|
|
|
bool UseInvItem(int pnum, int cii)
|
|
{
|
|
int c;
|
|
ItemStruct *item;
|
|
|
|
auto &player = plr[pnum];
|
|
|
|
if (player._pInvincible && player._pHitPoints == 0 && pnum == myplr)
|
|
return true;
|
|
if (pcurs != CURSOR_HAND)
|
|
return true;
|
|
if (stextflag != STORE_NONE)
|
|
return true;
|
|
if (cii < INVITEM_INV_FIRST)
|
|
return false;
|
|
|
|
bool speedlist = false;
|
|
if (cii <= INVITEM_INV_LAST) {
|
|
c = cii - INVITEM_INV_FIRST;
|
|
item = &player.InvList[c];
|
|
} else {
|
|
if (talkflag)
|
|
return true;
|
|
c = cii - INVITEM_BELT_FIRST;
|
|
item = &player.SpdList[c];
|
|
speedlist = true;
|
|
}
|
|
|
|
switch (item->IDidx) {
|
|
case IDI_MUSHROOM:
|
|
player.Say(HeroSpeech::NowThatsOneBigMushroom, 10);
|
|
return true;
|
|
case IDI_FUNGALTM:
|
|
PlaySFX(IS_IBOOK);
|
|
player.Say(HeroSpeech::ThatDidntDoAnything, 10);
|
|
return true;
|
|
}
|
|
|
|
if (!AllItemsList[item->IDidx].iUsable)
|
|
return false;
|
|
|
|
if (!item->_iStatFlag) {
|
|
player.Say(HeroSpeech::ICantUseThisYet);
|
|
return true;
|
|
}
|
|
|
|
if (item->_iMiscId == IMISC_NONE && item->_itype == ITYPE_GOLD) {
|
|
StartGoldDrop();
|
|
return true;
|
|
}
|
|
|
|
if (dropGoldFlag) {
|
|
dropGoldFlag = false;
|
|
dropGoldValue = 0;
|
|
}
|
|
|
|
if (item->_iMiscId == IMISC_SCROLL && currlevel == 0 && !spelldata[item->_iSpell].sTownSpell) {
|
|
return true;
|
|
}
|
|
|
|
if (item->_iMiscId == IMISC_SCROLLT && currlevel == 0 && !spelldata[item->_iSpell].sTownSpell) {
|
|
return true;
|
|
}
|
|
|
|
if (item->_iMiscId > IMISC_RUNEFIRST && item->_iMiscId < IMISC_RUNELAST && currlevel == 0) {
|
|
return true;
|
|
}
|
|
|
|
int idata = ItemCAnimTbl[item->_iCurs];
|
|
if (item->_iMiscId == IMISC_BOOK)
|
|
PlaySFX(IS_RBOOK);
|
|
else if (pnum == myplr)
|
|
PlaySFX(ItemInvSnds[idata]);
|
|
|
|
UseItem(pnum, item->_iMiscId, item->_iSpell);
|
|
|
|
if (speedlist) {
|
|
if (player.SpdList[c]._iMiscId == IMISC_NOTE) {
|
|
InitQTextMsg(TEXT_BOOK9);
|
|
invflag = false;
|
|
return true;
|
|
}
|
|
player.RemoveSpdBarItem(c);
|
|
return true;
|
|
}
|
|
if (player.InvList[c]._iMiscId == IMISC_MAPOFDOOM)
|
|
return true;
|
|
if (player.InvList[c]._iMiscId == IMISC_NOTE) {
|
|
InitQTextMsg(TEXT_BOOK9);
|
|
invflag = false;
|
|
return true;
|
|
}
|
|
player.RemoveInvItem(c);
|
|
|
|
return true;
|
|
}
|
|
|
|
void DoTelekinesis()
|
|
{
|
|
if (pcursobj != -1)
|
|
NetSendCmdParam1(true, CMD_OPOBJT, pcursobj);
|
|
if (pcursitem != -1)
|
|
NetSendCmdGItem(true, CMD_REQUESTAGITEM, myplr, myplr, pcursitem);
|
|
if (pcursmonst != -1 && !M_Talker(pcursmonst) && monster[pcursmonst].mtalkmsg == TEXT_NONE)
|
|
NetSendCmdParam1(true, CMD_KNOCKBACK, pcursmonst);
|
|
NewCursor(CURSOR_HAND);
|
|
}
|
|
|
|
int CalculateGold(PlayerStruct &player)
|
|
{
|
|
int gold = 0;
|
|
|
|
for (int i = 0; i < player._pNumInv; i++) {
|
|
if (player.InvList[i]._itype == ITYPE_GOLD)
|
|
gold += player.InvList[i]._ivalue;
|
|
}
|
|
|
|
return gold;
|
|
}
|
|
|
|
bool DropItemBeforeTrig()
|
|
{
|
|
if (!TryInvPut()) {
|
|
return false;
|
|
}
|
|
|
|
NetSendCmdPItem(true, CMD_PUTITEM, { cursmx, cursmy });
|
|
NewCursor(CURSOR_HAND);
|
|
return true;
|
|
}
|
|
|
|
} // namespace devilution
|