1 /**
2  * @file inv.cpp
3  *
4  * Implementation of player inventory.
5  */
6 #include "all.h"
7 #include "options.h"
8 
9 DEVILUTION_BEGIN_NAMESPACE
10 
11 BOOL invflag;
12 BYTE *pInvCels;
13 BOOL drawsbarflag;
14 int sgdwLastTime; // check name
15 
16 /**
17  * Maps from inventory slot to screen position. The inventory slots are
18  * arranged as follows:
19  *                          00 01
20  *                          02 03   06
21  *              07 08       19 20       13 14
22  *              09 10       21 22       15 16
23  *              11 12       23 24       17 18
24  *                 04                   05
25  *              25 26 27 28 29 30 31 32 33 34
26  *              35 36 37 38 39 40 41 42 43 44
27  *              45 46 47 48 49 50 51 52 53 54
28  *              55 56 57 58 59 60 61 62 63 64
29  * 65 66 67 68 69 70 71 72
30  * @see graphics/inv/inventory.png
31  */
32 const InvXY InvRect[] = {
33 	// clang-format off
34 	//  X,   Y
35 	{ 132,  31 }, // helmet
36 	{ 160,  31 }, // helmet
37 	{ 132,  59 }, // helmet
38 	{ 160,  59 }, // helmet
39 	{  45, 205 }, // left ring
40 	{ 247, 205 }, // right ring
41 	{ 204,  59 }, // amulet
42 	{  17, 104 }, // left hand
43 	{  46, 104 }, // left hand
44 	{  17, 132 }, // left hand
45 	{  46, 132 }, // left hand
46 	{  17, 160 }, // left hand
47 	{  46, 160 }, // left hand
48 	{ 247, 104 }, // right hand
49 	{ 276, 104 }, // right hand
50 	{ 247, 132 }, // right hand
51 	{ 276, 132 }, // right hand
52 	{ 247, 160 }, // right hand
53 	{ 276, 160 }, // right hand
54 	{ 132, 104 }, // chest
55 	{ 160, 104 }, // chest
56 	{ 132, 132 }, // chest
57 	{ 160, 132 }, // chest
58 	{ 132, 160 }, // chest
59 	{ 160, 160 }, // chest
60 	{  17, 250 }, // inv row 1
61 	{  46, 250 }, // inv row 1
62 	{  75, 250 }, // inv row 1
63 	{ 104, 250 }, // inv row 1
64 	{ 133, 250 }, // inv row 1
65 	{ 162, 250 }, // inv row 1
66 	{ 191, 250 }, // inv row 1
67 	{ 220, 250 }, // inv row 1
68 	{ 249, 250 }, // inv row 1
69 	{ 278, 250 }, // inv row 1
70 	{  17, 279 }, // inv row 2
71 	{  46, 279 }, // inv row 2
72 	{  75, 279 }, // inv row 2
73 	{ 104, 279 }, // inv row 2
74 	{ 133, 279 }, // inv row 2
75 	{ 162, 279 }, // inv row 2
76 	{ 191, 279 }, // inv row 2
77 	{ 220, 279 }, // inv row 2
78 	{ 249, 279 }, // inv row 2
79 	{ 278, 279 }, // inv row 2
80 	{  17, 308 }, // inv row 3
81 	{  46, 308 }, // inv row 3
82 	{  75, 308 }, // inv row 3
83 	{ 104, 308 }, // inv row 3
84 	{ 133, 308 }, // inv row 3
85 	{ 162, 308 }, // inv row 3
86 	{ 191, 308 }, // inv row 3
87 	{ 220, 308 }, // inv row 3
88 	{ 249, 308 }, // inv row 3
89 	{ 278, 308 }, // inv row 3
90 	{  17, 337 }, // inv row 4
91 	{  46, 337 }, // inv row 4
92 	{  75, 337 }, // inv row 4
93 	{ 104, 337 }, // inv row 4
94 	{ 133, 337 }, // inv row 4
95 	{ 162, 337 }, // inv row 4
96 	{ 191, 337 }, // inv row 4
97 	{ 220, 337 }, // inv row 4
98 	{ 249, 337 }, // inv row 4
99 	{ 278, 337 }, // inv row 4
100 	{ 205,  33 }, // belt
101 	{ 234,  33 }, // belt
102 	{ 263,  33 }, // belt
103 	{ 292,  33 }, // belt
104 	{ 321,  33 }, // belt
105 	{ 350,  33 }, // belt
106 	{ 379,  33 }, // belt
107 	{ 408,  33 }  // belt
108 	// clang-format on
109 };
110 
111 /* data */
112 /** Specifies the starting inventory slots for placement of 2x2 items. */
113 int AP2x2Tbl[10] = { 8, 28, 6, 26, 4, 24, 2, 22, 0, 20 };
114 
FreeInvGFX()115 void FreeInvGFX()
116 {
117 	MemFreeDbg(pInvCels);
118 }
119 
InitInv()120 void InitInv()
121 {
122 	if (plr[myplr]._pClass == PC_WARRIOR) {
123 		pInvCels = LoadFileInMem("Data\\Inv\\Inv.CEL", NULL);
124 	} else if (plr[myplr]._pClass == PC_ROGUE) {
125 		pInvCels = LoadFileInMem("Data\\Inv\\Inv_rog.CEL", NULL);
126 	} else if (plr[myplr]._pClass == PC_SORCERER) {
127 		pInvCels = LoadFileInMem("Data\\Inv\\Inv_Sor.CEL", NULL);
128 	} else if (plr[myplr]._pClass == PC_MONK) {
129 		if (!gbIsSpawn)
130 			pInvCels = LoadFileInMem("Data\\Inv\\Inv_Sor.CEL", NULL);
131 		else
132 			pInvCels = LoadFileInMem("Data\\Inv\\Inv.CEL", NULL);
133 	} else if (plr[myplr]._pClass == PC_BARD) {
134 		pInvCels = LoadFileInMem("Data\\Inv\\Inv_rog.CEL", NULL);
135 	} else if (plr[myplr]._pClass == PC_BARBARIAN) {
136 		pInvCels = LoadFileInMem("Data\\Inv\\Inv.CEL", NULL);
137 	}
138 
139 	invflag = FALSE;
140 	drawsbarflag = FALSE;
141 }
142 
InvDrawSlotBack(CelOutputBuffer out,int X,int Y,int W,int H)143 static void InvDrawSlotBack(CelOutputBuffer out, int X, int Y, int W, int H)
144 {
145 	BYTE *dst;
146 
147 	dst = out.at(X, Y);
148 
149 	int wdt, hgt;
150 	BYTE pix;
151 
152 	for (hgt = H; hgt; hgt--, dst -= out.pitch() + W) {
153 		for (wdt = W; wdt; wdt--) {
154 			pix = *dst;
155 			if (pix >= PAL16_BLUE) {
156 				if (pix <= PAL16_BLUE + 15)
157 					pix -= PAL16_BLUE - PAL16_BEIGE;
158 				else if (pix >= PAL16_GRAY)
159 					pix -= PAL16_GRAY - PAL16_BEIGE;
160 			}
161 			*dst++ = pix;
162 		}
163 	}
164 }
165 
DrawInv(CelOutputBuffer out)166 void DrawInv(CelOutputBuffer out)
167 {
168 	BOOL invtest[NUM_INV_GRID_ELEM];
169 	int frame, frame_width, color, screen_x, screen_y, i, j, ii;
170 
171 	CelDrawTo(out, RIGHT_PANEL_X, 351, pInvCels, 1, SPANEL_WIDTH);
172 
173 	if (!plr[myplr].InvBody[INVLOC_HEAD].isEmpty()) {
174 		InvDrawSlotBack(out, RIGHT_PANEL_X + 133, 59, 2 * INV_SLOT_SIZE_PX, 2 * INV_SLOT_SIZE_PX);
175 
176 		frame = plr[myplr].InvBody[INVLOC_HEAD]._iCurs + CURSOR_FIRSTITEM;
177 		frame_width = InvItemWidth[frame];
178 
179 		if (pcursinvitem == INVITEM_HEAD) {
180 			color = ICOL_WHITE;
181 			if (plr[myplr].InvBody[INVLOC_HEAD]._iMagical != ITEM_QUALITY_NORMAL) {
182 				color = ICOL_BLUE;
183 			}
184 			if (!plr[myplr].InvBody[INVLOC_HEAD]._iStatFlag) {
185 				color = ICOL_RED;
186 			}
187 			if (frame <= 179) {
188 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 133, 59, pCursCels, frame, frame_width, false);
189 			} else {
190 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 133, 59, pCursCels2, frame - 179, frame_width, false);
191 			}
192 		}
193 
194 		if (plr[myplr].InvBody[INVLOC_HEAD]._iStatFlag) {
195 			if (frame <= 179) {
196 				CelClippedDrawTo(out, RIGHT_PANEL_X + 133, 59, pCursCels, frame, frame_width);
197 			} else {
198 				CelClippedDrawTo(out, RIGHT_PANEL_X + 133, 59, pCursCels2, frame - 179, frame_width);
199 			}
200 		} else {
201 			if (frame <= 179) {
202 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 133, 59, pCursCels, frame, frame_width, 1);
203 			} else {
204 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 133, 59, pCursCels2, frame - 179, frame_width, 1);
205 			}
206 		}
207 	}
208 
209 	if (!plr[myplr].InvBody[INVLOC_RING_LEFT].isEmpty()) {
210 		InvDrawSlotBack(out, RIGHT_PANEL_X + 48, 205, INV_SLOT_SIZE_PX, INV_SLOT_SIZE_PX);
211 
212 		frame = plr[myplr].InvBody[INVLOC_RING_LEFT]._iCurs + CURSOR_FIRSTITEM;
213 		frame_width = InvItemWidth[frame];
214 
215 		if (pcursinvitem == INVITEM_RING_LEFT) {
216 			color = ICOL_WHITE;
217 			if (plr[myplr].InvBody[INVLOC_RING_LEFT]._iMagical != ITEM_QUALITY_NORMAL) {
218 				color = ICOL_BLUE;
219 			}
220 			if (!plr[myplr].InvBody[INVLOC_RING_LEFT]._iStatFlag) {
221 				color = ICOL_RED;
222 			}
223 			if (frame <= 179) {
224 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 48, 205, pCursCels, frame, frame_width, false);
225 			} else {
226 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 48, 205, pCursCels2, frame - 179, frame_width, false);
227 			}
228 		}
229 
230 		if (plr[myplr].InvBody[INVLOC_RING_LEFT]._iStatFlag) {
231 			if (frame <= 179) {
232 				CelClippedDrawTo(out, RIGHT_PANEL_X + 48, 205, pCursCels, frame, frame_width);
233 			} else {
234 				CelClippedDrawTo(out, RIGHT_PANEL_X + 48, 205, pCursCels2, frame - 179, frame_width);
235 			}
236 		} else {
237 			if (frame <= 179) {
238 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 48, 205, pCursCels, frame, frame_width, 1);
239 			} else {
240 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 48, 205, pCursCels2, frame - 179, frame_width, 1);
241 			}
242 		}
243 	}
244 
245 	if (!plr[myplr].InvBody[INVLOC_RING_RIGHT].isEmpty()) {
246 		InvDrawSlotBack(out, RIGHT_PANEL_X + 249, 205, INV_SLOT_SIZE_PX, INV_SLOT_SIZE_PX);
247 
248 		frame = plr[myplr].InvBody[INVLOC_RING_RIGHT]._iCurs + CURSOR_FIRSTITEM;
249 		frame_width = InvItemWidth[frame];
250 
251 		if (pcursinvitem == INVITEM_RING_RIGHT) {
252 			color = ICOL_WHITE;
253 			if (plr[myplr].InvBody[INVLOC_RING_RIGHT]._iMagical != ITEM_QUALITY_NORMAL) {
254 				color = ICOL_BLUE;
255 			}
256 			if (!plr[myplr].InvBody[INVLOC_RING_RIGHT]._iStatFlag) {
257 				color = ICOL_RED;
258 			}
259 			if (frame <= 179) {
260 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 249, 205, pCursCels, frame, frame_width, false);
261 			} else {
262 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 249, 205, pCursCels2, frame - 179, frame_width, false);
263 			}
264 		}
265 
266 		if (plr[myplr].InvBody[INVLOC_RING_RIGHT]._iStatFlag) {
267 			if (frame <= 179) {
268 				CelClippedDrawTo(out, RIGHT_PANEL_X + 249, 205, pCursCels, frame, frame_width);
269 			} else {
270 				CelClippedDrawTo(out, RIGHT_PANEL_X + 249, 205, pCursCels2, frame - 179, frame_width);
271 			}
272 		} else {
273 			if (frame <= 179) {
274 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 249, 205, pCursCels, frame, frame_width, 1);
275 			} else {
276 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 249, 205, pCursCels2, frame - 179, frame_width, 1);
277 			}
278 		}
279 	}
280 
281 	if (!plr[myplr].InvBody[INVLOC_AMULET].isEmpty()) {
282 		InvDrawSlotBack(out, RIGHT_PANEL_X + 205, 60, INV_SLOT_SIZE_PX, INV_SLOT_SIZE_PX);
283 
284 		frame = plr[myplr].InvBody[INVLOC_AMULET]._iCurs + CURSOR_FIRSTITEM;
285 		frame_width = InvItemWidth[frame];
286 
287 		if (pcursinvitem == INVITEM_AMULET) {
288 			color = ICOL_WHITE;
289 			if (plr[myplr].InvBody[INVLOC_AMULET]._iMagical != ITEM_QUALITY_NORMAL) {
290 				color = ICOL_BLUE;
291 			}
292 			if (!plr[myplr].InvBody[INVLOC_AMULET]._iStatFlag) {
293 				color = ICOL_RED;
294 			}
295 			if (frame <= 179) {
296 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 205, 60, pCursCels, frame, frame_width, false);
297 			} else {
298 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 205, 60, pCursCels2, frame - 179, frame_width, false);
299 			}
300 		}
301 
302 		if (plr[myplr].InvBody[INVLOC_AMULET]._iStatFlag) {
303 			if (frame <= 179) {
304 				CelClippedDrawTo(out, RIGHT_PANEL_X + 205, 60, pCursCels, frame, frame_width);
305 			} else {
306 				CelClippedDrawTo(out, RIGHT_PANEL_X + 205, 60, pCursCels2, frame - 179, frame_width);
307 			}
308 		} else {
309 			if (frame <= 179) {
310 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 205, 60, pCursCels, frame, frame_width, 1);
311 			} else {
312 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 205, 60, pCursCels2, frame - 179, frame_width, 1);
313 			}
314 		}
315 	}
316 
317 	if (!plr[myplr].InvBody[INVLOC_HAND_LEFT].isEmpty()) {
318 		InvDrawSlotBack(out, RIGHT_PANEL_X + 17, 160, 2 * INV_SLOT_SIZE_PX, 3 * INV_SLOT_SIZE_PX);
319 
320 		frame = plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCurs + CURSOR_FIRSTITEM;
321 		frame_width = InvItemWidth[frame];
322 		// calc item offsets for weapons smaller than 2x3 slots
323 		screen_x = frame_width == INV_SLOT_SIZE_PX ? (RIGHT_PANEL_X + 31) : (RIGHT_PANEL_X + 17);
324 		screen_y = InvItemHeight[frame] == (3 * INV_SLOT_SIZE_PX) ? (160) : (146);
325 
326 		if (pcursinvitem == INVITEM_HAND_LEFT) {
327 			color = ICOL_WHITE;
328 			if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._iMagical != ITEM_QUALITY_NORMAL) {
329 				color = ICOL_BLUE;
330 			}
331 			if (!plr[myplr].InvBody[INVLOC_HAND_LEFT]._iStatFlag) {
332 				color = ICOL_RED;
333 			}
334 			if (frame <= 179) {
335 				CelBlitOutlineTo(out, color, screen_x, screen_y, pCursCels, frame, frame_width, false);
336 			} else {
337 				CelBlitOutlineTo(out, color, screen_x, screen_y, pCursCels2, frame - 179, frame_width, false);
338 			}
339 		}
340 
341 		if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._iStatFlag) {
342 			if (frame <= 179) {
343 				CelClippedDrawTo(out, screen_x, screen_y, pCursCels, frame, frame_width);
344 			} else {
345 				CelClippedDrawTo(out, screen_x, screen_y, pCursCels2, frame - 179, frame_width);
346 			}
347 		} else {
348 			if (frame <= 179) {
349 				CelDrawLightRedTo(out, screen_x, screen_y, pCursCels, frame, frame_width, 1);
350 			} else {
351 				CelDrawLightRedTo(out, screen_x, screen_y, pCursCels2, frame - 179, frame_width, 1);
352 			}
353 		}
354 
355 		if (plr[myplr].InvBody[INVLOC_HAND_LEFT]._iLoc == ILOC_TWOHAND) {
356 			if (plr[myplr]._pClass != PC_BARBARIAN
357 			    || (plr[myplr].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_SWORD
358 			        && plr[myplr].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_MACE)) {
359 				InvDrawSlotBack(out, RIGHT_PANEL_X + 248, 160, 2 * INV_SLOT_SIZE_PX, 3 * INV_SLOT_SIZE_PX);
360 				light_table_index = 0;
361 				cel_transparency_active = TRUE;
362 
363 				const int dst_x = RIGHT_PANEL_X + (frame_width == INV_SLOT_SIZE_PX ? 261 : 247);
364 				const int dst_y = 160;
365 				if (frame <= 179) {
366 					CelClippedBlitLightTransTo(out, dst_x, dst_y, pCursCels, frame, frame_width);
367 				} else {
368 					CelClippedBlitLightTransTo(out, dst_x, dst_y, pCursCels2, frame - 179, frame_width);
369 				}
370 
371 				cel_transparency_active = FALSE;
372 			}
373 		}
374 	}
375 	if (!plr[myplr].InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
376 		InvDrawSlotBack(out, RIGHT_PANEL_X + 248, 160, 2 * INV_SLOT_SIZE_PX, 3 * INV_SLOT_SIZE_PX);
377 
378 		frame = plr[myplr].InvBody[INVLOC_HAND_RIGHT]._iCurs + CURSOR_FIRSTITEM;
379 		frame_width = InvItemWidth[frame];
380 		// calc item offsets for weapons smaller than 2x3 slots
381 		screen_x = frame_width == INV_SLOT_SIZE_PX ? (RIGHT_PANEL_X + 261) : (RIGHT_PANEL_X + 249);
382 		screen_y = InvItemHeight[frame] == 3 * INV_SLOT_SIZE_PX ? (160) : (146);
383 
384 		if (pcursinvitem == INVITEM_HAND_RIGHT) {
385 			color = ICOL_WHITE;
386 			if (plr[myplr].InvBody[INVLOC_HAND_RIGHT]._iMagical != ITEM_QUALITY_NORMAL) {
387 				color = ICOL_BLUE;
388 			}
389 			if (!plr[myplr].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) {
390 				color = ICOL_RED;
391 			}
392 			if (frame <= 179) {
393 				CelBlitOutlineTo(out, color, screen_x, screen_y, pCursCels, frame, frame_width, false);
394 			} else {
395 				CelBlitOutlineTo(out, color, screen_x, screen_y, pCursCels2, frame - 179, frame_width, false);
396 			}
397 		}
398 
399 		if (plr[myplr].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) {
400 			if (frame <= 179) {
401 				CelClippedDrawTo(out, screen_x, screen_y, pCursCels, frame, frame_width);
402 			} else {
403 				CelClippedDrawTo(out, screen_x, screen_y, pCursCels2, frame - 179, frame_width);
404 			}
405 		} else {
406 			if (frame <= 179) {
407 				CelDrawLightRedTo(out, screen_x, screen_y, pCursCels, frame, frame_width, 1);
408 			} else {
409 				CelDrawLightRedTo(out, screen_x, screen_y, pCursCels2, frame - 179, frame_width, 1);
410 			}
411 		}
412 	}
413 
414 	if (!plr[myplr].InvBody[INVLOC_CHEST].isEmpty()) {
415 		InvDrawSlotBack(out, RIGHT_PANEL_X + 133, 160, 2 * INV_SLOT_SIZE_PX, 3 * INV_SLOT_SIZE_PX);
416 
417 		frame = plr[myplr].InvBody[INVLOC_CHEST]._iCurs + CURSOR_FIRSTITEM;
418 		frame_width = InvItemWidth[frame];
419 
420 		if (pcursinvitem == INVITEM_CHEST) {
421 			color = ICOL_WHITE;
422 			if (plr[myplr].InvBody[INVLOC_CHEST]._iMagical != ITEM_QUALITY_NORMAL) {
423 				color = ICOL_BLUE;
424 			}
425 			if (!plr[myplr].InvBody[INVLOC_CHEST]._iStatFlag) {
426 				color = ICOL_RED;
427 			}
428 			if (frame <= 179) {
429 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 133, 160, pCursCels, frame, frame_width, false);
430 			} else {
431 				CelBlitOutlineTo(out, color, RIGHT_PANEL_X + 133, 160, pCursCels2, frame - 179, frame_width, false);
432 			}
433 		}
434 
435 		if (plr[myplr].InvBody[INVLOC_CHEST]._iStatFlag) {
436 			if (frame <= 179) {
437 				CelClippedDrawTo(out, RIGHT_PANEL_X + 133, 160, pCursCels, frame, frame_width);
438 			} else {
439 				CelClippedDrawTo(out, RIGHT_PANEL_X + 133, 160, pCursCels2, frame - 179, frame_width);
440 			}
441 		} else {
442 			if (frame <= 179) {
443 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 133, 160, pCursCels, frame, frame_width, 1);
444 			} else {
445 				CelDrawLightRedTo(out, RIGHT_PANEL_X + 133, 160, pCursCels2, frame - 179, frame_width, 1);
446 			}
447 		}
448 	}
449 
450 	for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
451 		invtest[i] = FALSE;
452 		if (plr[myplr].InvGrid[i] != 0) {
453 			InvDrawSlotBack(
454 			    out,
455 			    InvRect[i + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
456 			    InvRect[i + SLOTXY_INV_FIRST].Y - 1,
457 			    INV_SLOT_SIZE_PX,
458 			    INV_SLOT_SIZE_PX);
459 		}
460 	}
461 
462 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
463 		if (plr[myplr].InvGrid[j] > 0) // first slot of an item
464 		{
465 			ii = plr[myplr].InvGrid[j] - 1;
466 
467 			invtest[j] = TRUE;
468 
469 			frame = plr[myplr].InvList[ii]._iCurs + CURSOR_FIRSTITEM;
470 			frame_width = InvItemWidth[frame];
471 			if (pcursinvitem == ii + INVITEM_INV_FIRST) {
472 				color = ICOL_WHITE;
473 				if (plr[myplr].InvList[ii]._iMagical != ITEM_QUALITY_NORMAL) {
474 					color = ICOL_BLUE;
475 				}
476 				if (!plr[myplr].InvList[ii]._iStatFlag) {
477 					color = ICOL_RED;
478 				}
479 				if (frame <= 179) {
480 					CelBlitOutlineTo(
481 					    out,
482 					    color,
483 					    InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
484 					    InvRect[j + SLOTXY_INV_FIRST].Y - 1,
485 					    pCursCels, frame, frame_width,
486 						false);
487 				} else {
488 					CelBlitOutlineTo(
489 					    out,
490 					    color,
491 					    InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
492 					    InvRect[j + SLOTXY_INV_FIRST].Y - 1,
493 					    pCursCels2, frame - 179, frame_width,
494 						false);
495 				}
496 			}
497 
498 			if (plr[myplr].InvList[ii]._iStatFlag) {
499 				if (frame <= 179) {
500 					CelClippedDrawTo(
501 					    out,
502 					    InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
503 					    InvRect[j + SLOTXY_INV_FIRST].Y - 1,
504 					    pCursCels, frame, frame_width);
505 				} else {
506 					CelClippedDrawTo(
507 					    out,
508 					    InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
509 					    InvRect[j + SLOTXY_INV_FIRST].Y - 1,
510 					    pCursCels2, frame - 179, frame_width);
511 				}
512 			} else {
513 				if (frame <= 179) {
514 					CelDrawLightRedTo(
515 					    out,
516 					    InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
517 					    InvRect[j + SLOTXY_INV_FIRST].Y - 1,
518 					    pCursCels, frame, frame_width, 1);
519 				} else {
520 					CelDrawLightRedTo(
521 					    out,
522 					    InvRect[j + SLOTXY_INV_FIRST].X + RIGHT_PANEL_X,
523 					    InvRect[j + SLOTXY_INV_FIRST].Y - 1,
524 					    pCursCels2, frame - 179, frame_width, 1);
525 				}
526 			}
527 		}
528 	}
529 }
530 
DrawInvBelt(CelOutputBuffer out)531 void DrawInvBelt(CelOutputBuffer out)
532 {
533 	int i, frame, frame_width, color;
534 	BYTE fi, ff;
535 
536 	if (talkflag) {
537 		return;
538 	}
539 
540 	DrawPanelBox(out, 205, 21, 232, 28, PANEL_X + 205, PANEL_Y + 5);
541 
542 	for (i = 0; i < MAXBELTITEMS; i++) {
543 		if (plr[myplr].SpdList[i].isEmpty()) {
544 			continue;
545 		}
546 
547 		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);
548 		frame = plr[myplr].SpdList[i]._iCurs + CURSOR_FIRSTITEM;
549 		frame_width = InvItemWidth[frame];
550 
551 		if (pcursinvitem == i + INVITEM_BELT_FIRST) {
552 			color = ICOL_WHITE;
553 			if (plr[myplr].SpdList[i]._iMagical)
554 				color = ICOL_BLUE;
555 			if (!plr[myplr].SpdList[i]._iStatFlag)
556 				color = ICOL_RED;
557 			if (!sgbControllerActive || invflag) {
558 				if (frame <= 179)
559 					CelBlitOutlineTo(out, color, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, pCursCels, frame, frame_width, false);
560 				else
561 					CelBlitOutlineTo(out, color, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, pCursCels2, frame - 179, frame_width, false);
562 			}
563 		}
564 
565 		if (plr[myplr].SpdList[i]._iStatFlag) {
566 			if (frame <= 179)
567 				CelClippedDrawTo(out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, pCursCels, frame, frame_width);
568 			else
569 				CelClippedDrawTo(out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, pCursCels2, frame - 179, frame_width);
570 		} else {
571 			if (frame <= 179)
572 				CelDrawLightRedTo(out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, pCursCels, frame, frame_width, 1);
573 			else
574 				CelDrawLightRedTo(out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X, InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, pCursCels2, frame - 179, frame_width, 1);
575 		}
576 
577 		if (AllItemsList[plr[myplr].SpdList[i].IDidx].iUsable
578 		    && plr[myplr].SpdList[i]._iStatFlag
579 		    && plr[myplr].SpdList[i]._itype != ITYPE_GOLD) {
580 			fi = i + 49;
581 			ff = fontframe[gbFontTransTbl[fi]];
582 			PrintChar(out, InvRect[i + SLOTXY_BELT_FIRST].X + PANEL_X + INV_SLOT_SIZE_PX - fontkern[ff], InvRect[i + SLOTXY_BELT_FIRST].Y + PANEL_Y - 1, ff, COL_WHITE);
583 		}
584 	}
585 }
586 
587 /**
588  * @brief Gets the size, in inventory cells, of the given item.
589  * @param item The item whose size is to be determined.
590  * @return The size, in inventory cells, of the item.
591  */
GetInventorySize(const ItemStruct & item)592 InvXY GetInventorySize(const ItemStruct &item)
593 {
594 	int itemSizeIndex = item._iCurs + CURSOR_FIRSTITEM;
595 
596 	return {
597 		InvItemWidth[itemSizeIndex] / INV_SLOT_SIZE_PX,
598 		InvItemHeight[itemSizeIndex] / INV_SLOT_SIZE_PX,
599 	};
600 }
601 
602 /**
603  * @brief Checks whether the given item can fit in a belt slot (i.e. the item's size in inventory cells is 1x1).
604  * @param item The item to be checked.
605  * @return 'True' in case the item can fit a belt slot and 'False' otherwise.
606  */
FitsInBeltSlot(const ItemStruct & item)607 bool FitsInBeltSlot(const ItemStruct &item)
608 {
609 	InvXY size = GetInventorySize(item);
610 
611 	return size.X == 1 && size.Y == 1;
612 }
613 
614 /**
615  * @brief Checks whether the given item can be placed on the belt. Takes item size as well as characteristics into account. Items
616  * that cannot be placed on the belt have to be placed in the inventory instead.
617  * @param item The item to be checked.
618  * @return 'True' in case the item can be placed on the belt and 'False' otherwise.
619  */
CanBePlacedOnBelt(const ItemStruct & item)620 bool CanBePlacedOnBelt(const ItemStruct &item)
621 {
622 	return FitsInBeltSlot(item)
623 	    && item._itype != ITYPE_GOLD
624 	    && item._iStatFlag
625 	    && AllItemsList[item.IDidx].iUsable;
626 }
627 
628 /**
629  * @brief Checks whether the given item can be placed on the specified player's belt. Returns 'True' when the item can be placed
630  * on belt slots and the player has at least one empty slot in his belt.
631  * If 'persistItem' is 'True', the item is also placed in the belt.
632  * @param playerNumber The player number on whose belt will be checked.
633  * @param item The item to be checked.
634  * @param persistItem Pass 'True' to actually place the item in the belt. The default is 'False'.
635  * @return 'True' in case the item can be placed on the player's belt and 'False' otherwise.
636  */
AutoPlaceItemInBelt(int playerNumber,const ItemStruct & item,bool persistItem=false)637 bool AutoPlaceItemInBelt(int playerNumber, const ItemStruct &item, bool persistItem = false)
638 {
639 	if (!CanBePlacedOnBelt(item)) {
640 		return false;
641 	}
642 
643 	for (int i = 0; i < MAXBELTITEMS; i++) {
644 		if (plr[playerNumber].SpdList[i].isEmpty()) {
645 			if (persistItem) {
646 				plr[playerNumber].SpdList[i] = item;
647 				CalcPlrScrolls(playerNumber);
648 				drawsbarflag = TRUE;
649 			}
650 
651 			return true;
652 		}
653 	}
654 
655 	return false;
656 }
657 
658 /**
659  * @brief Checks whether the given item can be equipped. Since this overload doesn't take player information, it only considers
660  * general aspects about the item, like if its requirements are met and if the item's target location is valid for the body.
661  * @param item The item to check.
662  * @return 'True' in case the item could be equipped in a player, and 'False' otherwise.
663  */
CanEquip(const ItemStruct & item)664 bool CanEquip(const ItemStruct &item)
665 {
666 	return item.isEquipment()
667 	    && item._iStatFlag;
668 }
669 
670 /**
671  * @brief A specialized version of 'CanEquip(int, ItemStruct&, int)' that specifically checks whether the item can be equipped
672  * in one/both of the player's hands.
673  * @param playerNumber The player number whose inventory will be checked for compatibility with the item.
674  * @param item The item to check.
675  * @return 'True' if the player can currently equip the item in either one of his hands (i.e. the required hands are empty and
676  * allow the item), and 'False' otherwise.
677  */
CanWield(int playerNumber,const ItemStruct & item)678 bool CanWield(int playerNumber, const ItemStruct &item)
679 {
680 	if (!CanEquip(item) || (item._iLoc != ILOC_ONEHAND && item._iLoc != ILOC_TWOHAND))
681 		return false;
682 
683 	PlayerStruct &player = plr[playerNumber];
684 	ItemStruct &leftHandItem = player.InvBody[INVLOC_HAND_LEFT];
685 	ItemStruct &rightHandItem = player.InvBody[INVLOC_HAND_RIGHT];
686 
687 	if (leftHandItem.isEmpty() && rightHandItem.isEmpty()) {
688 		return true;
689 	}
690 
691 	if (!leftHandItem.isEmpty() && !rightHandItem.isEmpty()) {
692 		return false;
693 	}
694 
695 	ItemStruct &occupiedHand = !leftHandItem.isEmpty() ? leftHandItem : rightHandItem;
696 
697 	// Barbarian can wield two handed swords and maces in one hand, so we allow equiping any sword/mace as long as his occupied
698 	// hand has a shield (i.e. no dual wielding allowed)
699 	if (player._pClass == PC_BARBARIAN) {
700 		if (occupiedHand._itype == ITYPE_SHIELD && (item._itype == ITYPE_SWORD || item._itype == ITYPE_MACE))
701 			return true;
702 	}
703 
704 	// Bard can dual wield swords and maces, so we allow equiping one-handed weapons in her free slot as long as her occupied
705 	// slot is another one-handed weapon.
706 	if (player._pClass == PC_BARD) {
707 		bool occupiedHandIsOneHandedSwordOrMace = occupiedHand._iLoc == ILOC_ONEHAND
708 		    && (occupiedHand._itype == ITYPE_SWORD || occupiedHand._itype == ITYPE_MACE);
709 
710 		bool weaponToEquipIsOneHandedSwordOrMace = item._iLoc == ILOC_ONEHAND
711 		    && (item._itype == ITYPE_SWORD || item._itype == ITYPE_MACE);
712 
713 		if (occupiedHandIsOneHandedSwordOrMace && weaponToEquipIsOneHandedSwordOrMace) {
714 			return true;
715 		}
716 	}
717 
718 	return item._iLoc == ILOC_ONEHAND
719 	    && occupiedHand._iLoc == ILOC_ONEHAND
720 	    && item._iClass != occupiedHand._iClass;
721 }
722 
723 /**
724  * @brief Checks whether the specified item can be equipped in the desired body location on the player.
725  * @param playerNumber The player number whose inventory will be checked for compatibility with the item.
726  * @param item The item to check.
727  * @param bodyLocation The location in the inventory to be checked against. Can be one of 'inv_body_loc' members.
728  * @return 'True' if the player can currently equip the item in the specified body location (i.e. the body location is empty and
729  * allows the item), and 'False' otherwise.
730  */
CanEquip(int playerNumber,const ItemStruct & item,int bodyLocation)731 bool CanEquip(int playerNumber, const ItemStruct &item, int bodyLocation)
732 {
733 	PlayerStruct &player = plr[playerNumber];
734 	if (!CanEquip(item) || player._pmode > PM_WALK3 || !player.InvBody[bodyLocation].isEmpty()) {
735 		return false;
736 	}
737 
738 	switch (bodyLocation) {
739 	case INVLOC_AMULET:
740 		return item._iLoc == ILOC_AMULET;
741 
742 	case INVLOC_CHEST:
743 		return item._iLoc == ILOC_ARMOR;
744 
745 	case INVLOC_HAND_LEFT:
746 	case INVLOC_HAND_RIGHT:
747 		return CanWield(playerNumber, item);
748 
749 	case INVLOC_HEAD:
750 		return item._iLoc == ILOC_HELM;
751 
752 	case INVLOC_RING_LEFT:
753 	case INVLOC_RING_RIGHT:
754 		return item._iLoc == ILOC_RING;
755 
756 	default:
757 		return false;
758 	}
759 }
760 
761 /**
762  * @brief Automatically attempts to equip the specified item in a specific location in the player's body.
763  * @note On success, this will broadcast an equipment_change event to let other players know about the equipment change.
764  * @param playerNumber The player number whose inventory will be checked for compatibility with the item.
765  * @param item The item to equip.
766  * @param bodyLocation The location in the inventory where the item should be equipped. Can be one of 'inv_body_loc' members.
767  * @param persistItem Indicates whether or not the item should be persisted in the player's body. Pass 'False' to check
768  * whether the player can equip the item but you don't want the item to actually be equipped. 'True' by default.
769  * @return 'True' if the item was equipped and 'False' otherwise.
770  */
AutoEquip(int playerNumber,const ItemStruct & item,int bodyLocation,bool persistItem)771 bool AutoEquip(int playerNumber, const ItemStruct &item, int bodyLocation, bool persistItem)
772 {
773 	if (!CanEquip(playerNumber, item, bodyLocation)) {
774 		return false;
775 	}
776 
777 	if (persistItem) {
778 		plr[playerNumber].InvBody[bodyLocation] = item;
779 
780 		if (sgOptions.Audio.bAutoEquipSound && playerNumber == myplr) {
781 			PlaySFX(ItemInvSnds[ItemCAnimTbl[item._iCurs]]);
782 		}
783 
784 		NetSendCmdChItem(FALSE, bodyLocation);
785 		CalcPlrInv(playerNumber, TRUE);
786 	}
787 
788 	return true;
789 }
790 
791 /**
792  * @brief Automatically attempts to equip the specified item in the most appropriate location in the player's body.
793  * @note On success, this will broadcast an equipment_change event to let other players know about the equipment change.
794  * @param playerNumber The player number whose inventory will be checked for compatibility with the item.
795  * @param item The item to equip.
796  * @param persistItem Indicates whether or not the item should be persisted in the player's body. Pass 'False' to check
797  * whether the player can equip the item but you don't want the item to actually be equipped. 'True' by default.
798  * @return 'True' if the item was equipped and 'False' otherwise.
799  */
AutoEquip(int playerNumber,const ItemStruct & item,bool persistItem)800 bool AutoEquip(int playerNumber, const ItemStruct &item, bool persistItem)
801 {
802 	if (!CanEquip(item)) {
803 		return false;
804 	}
805 
806 	for (int bodyLocation = INVLOC_HEAD; bodyLocation < NUM_INVLOC; bodyLocation++) {
807 		if (AutoEquip(playerNumber, item, bodyLocation, persistItem)) {
808 			return true;
809 		}
810 	}
811 
812 	return false;
813 }
814 
815 /**
816  * @brief Checks whether or not auto-equipping behavior is enabled for the given player and item.
817  * @param player The player to check.
818  * @param item The item to check.
819  * @return 'True' if auto-equipping behavior is enabled for the player and item and 'False' otherwise.
820  */
AutoEquipEnabled(const PlayerStruct & player,const ItemStruct & item)821 bool AutoEquipEnabled(const PlayerStruct &player, const ItemStruct &item)
822 {
823 	if (item.isWeapon()) {
824 		// Monk can use unarmed attack as an encouraged option, thus we do not automatically equip weapons on him so as to not
825 		// annoy players who prefer that playstyle.
826 		return player._pClass != PC_MONK && sgOptions.Gameplay.bAutoEquipWeapons;
827 	}
828 
829 	if (item.isArmor()) {
830 		return sgOptions.Gameplay.bAutoEquipArmor;
831 	}
832 
833 	if (item.isHelm()) {
834 		return sgOptions.Gameplay.bAutoEquipHelms;
835 	}
836 
837 	if (item.isShield()) {
838 		return sgOptions.Gameplay.bAutoEquipShields;
839 	}
840 
841 	if (item.isJewelry()) {
842 		return sgOptions.Gameplay.bAutoEquipJewelry;
843 	}
844 
845 	return true;
846 }
847 
848 /**
849  * @brief Checks whether the given item can be placed on the specified player's inventory.
850  * If 'persistItem' is 'True', the item is also placed in the inventory.
851  * @param playerNumber The player number on whose inventory will be checked.
852  * @param item The item to be checked.
853  * @param persistItem Pass 'True' to actually place the item in the inventory. The default is 'False'.
854  * @return 'True' in case the item can be placed on the player's inventory and 'False' otherwise.
855  */
AutoPlaceItemInInventory(int playerNumber,const ItemStruct & item,bool persistItem=false)856 bool AutoPlaceItemInInventory(int playerNumber, const ItemStruct &item, bool persistItem = false)
857 {
858 	InvXY itemSize = GetInventorySize(item);
859 	bool done = false;
860 
861 	if (itemSize.X == 1 && itemSize.Y == 1) {
862 		for (int i = 30; i <= 39 && !done; i++) {
863 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
864 		}
865 
866 		for (int i = 20; i <= 29 && !done; i++) {
867 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
868 		}
869 
870 		for (int i = 10; i <= 19 && !done; i++) {
871 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
872 		}
873 
874 		for (int i = 0; i <= 9 && !done; i++) {
875 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
876 		}
877 	}
878 
879 	if (itemSize.X == 1 && itemSize.Y == 2) {
880 		for (int i = 29; i >= 20 && !done; i--) {
881 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
882 		}
883 
884 		for (int i = 9; i >= 0 && !done; i--) {
885 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
886 		}
887 
888 		for (int i = 19; i >= 10 && !done; i--) {
889 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
890 		}
891 	}
892 
893 	if (itemSize.X == 1 && itemSize.Y == 3) {
894 		for (int i = 0; i < 20 && !done; i++) {
895 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
896 		}
897 	}
898 
899 	if (itemSize.X == 2 && itemSize.Y == 2) {
900 		for (int i = 0; i < 10 && !done; i++) {
901 			done = AutoPlace(playerNumber, AP2x2Tbl[i], itemSize.X, itemSize.Y, persistItem);
902 		}
903 
904 		for (int i = 21; i < 29 && !done; i += 2) {
905 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
906 		}
907 
908 		for (int i = 1; i < 9 && !done; i += 2) {
909 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
910 		}
911 
912 		for (int i = 10; i < 19 && !done; i++) {
913 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
914 		}
915 	}
916 
917 	if (itemSize.X == 2 && itemSize.Y == 3) {
918 		for (int i = 0; i < 9 && !done; i++) {
919 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
920 		}
921 
922 		for (int i = 10; i < 19 && !done; i++) {
923 			done = AutoPlace(playerNumber, i, itemSize.X, itemSize.Y, persistItem);
924 		}
925 	}
926 
927 	return done;
928 }
929 
AutoPlace(int pnum,int ii,int sx,int sy,BOOL saveflag)930 BOOL AutoPlace(int pnum, int ii, int sx, int sy, BOOL saveflag)
931 {
932 	int i, j, xx, yy;
933 	BOOL done;
934 
935 	done = TRUE;
936 	yy = 10 * (ii / 10);
937 	if (yy < 0) {
938 		yy = 0;
939 	}
940 	for (j = 0; j < sy && done; j++) {
941 		if (yy >= NUM_INV_GRID_ELEM) {
942 			done = FALSE;
943 		}
944 		xx = ii % 10;
945 		if (xx < 0) {
946 			xx = 0;
947 		}
948 		for (i = 0; i < sx && done; i++) {
949 			if (xx >= 10) {
950 				done = FALSE;
951 			} else {
952 				done = plr[pnum].InvGrid[xx + yy] == 0;
953 			}
954 			xx++;
955 		}
956 		yy += 10;
957 	}
958 	if (done && saveflag) {
959 		plr[pnum].InvList[plr[pnum]._pNumInv] = plr[pnum].HoldItem;
960 		plr[pnum]._pNumInv++;
961 		yy = 10 * (ii / 10);
962 		if (yy < 0) {
963 			yy = 0;
964 		}
965 		for (j = 0; j < sy; j++) {
966 			xx = ii % 10;
967 			if (xx < 0) {
968 				xx = 0;
969 			}
970 			for (i = 0; i < sx; i++) {
971 				if (i != 0 || j != sy - 1) {
972 					plr[pnum].InvGrid[xx + yy] = -plr[pnum]._pNumInv;
973 				} else {
974 					plr[pnum].InvGrid[xx + yy] = plr[pnum]._pNumInv;
975 				}
976 				xx++;
977 			}
978 			yy += 10;
979 		}
980 		CalcPlrScrolls(pnum);
981 	}
982 	return done;
983 }
984 
SpecialAutoPlace(int pnum,int ii,const ItemStruct & item)985 BOOL SpecialAutoPlace(int pnum, int ii, const ItemStruct &item)
986 {
987 	int i, j, xx, yy;
988 	BOOL done;
989 
990 	done = TRUE;
991 	yy = 10 * (ii / 10);
992 	if (yy < 0) {
993 		yy = 0;
994 	}
995 
996 	InvXY itemSize = GetInventorySize(item);
997 	for (j = 0; j < itemSize.Y && done; j++) {
998 		if (yy >= NUM_INV_GRID_ELEM) {
999 			done = FALSE;
1000 		}
1001 		xx = ii % 10;
1002 		if (xx < 0) {
1003 			xx = 0;
1004 		}
1005 		for (i = 0; i < itemSize.X && done; i++) {
1006 			if (xx >= 10) {
1007 				done = FALSE;
1008 			} else {
1009 				done = plr[pnum].InvGrid[xx + yy] == 0;
1010 			}
1011 			xx++;
1012 		}
1013 		yy += 10;
1014 	}
1015 	if (!done) {
1016 		done = AutoPlaceItemInBelt(pnum, item);
1017 	}
1018 
1019 	return done;
1020 }
1021 
GoldAutoPlace(int pnum)1022 BOOL GoldAutoPlace(int pnum)
1023 {
1024 	bool done = false;
1025 
1026 	for (int i = 0; i < plr[pnum]._pNumInv && !done; i++) {
1027 		if (plr[pnum].InvList[i]._itype != ITYPE_GOLD)
1028 			continue;
1029 		if (plr[pnum].InvList[i]._ivalue >= MaxGold)
1030 			continue;
1031 
1032 		plr[pnum].InvList[i]._ivalue += plr[pnum].HoldItem._ivalue;
1033 		if (plr[pnum].InvList[i]._ivalue > MaxGold) {
1034 			plr[pnum].HoldItem._ivalue = plr[pnum].InvList[i]._ivalue - MaxGold;
1035 			SetPlrHandGoldCurs(&plr[pnum].HoldItem);
1036 			plr[pnum].InvList[i]._ivalue = MaxGold;
1037 			if (gbIsHellfire)
1038 				GetPlrHandSeed(&plr[pnum].HoldItem);
1039 		} else {
1040 			plr[pnum].HoldItem._ivalue = 0;
1041 			done = true;
1042 		}
1043 
1044 		SetPlrHandGoldCurs(&plr[pnum].InvList[i]);
1045 		plr[pnum]._pGold = CalculateGold(pnum);
1046 	}
1047 
1048 	for (int i = 39; i >= 0 && !done; i--) {
1049 		int yy = 10 * (i / 10);
1050 		int xx = i % 10;
1051 		if (plr[pnum].InvGrid[xx + yy] == 0) {
1052 			int ii = plr[pnum]._pNumInv;
1053 			plr[pnum].InvList[ii] = plr[pnum].HoldItem;
1054 			plr[pnum]._pNumInv = plr[pnum]._pNumInv + 1;
1055 			plr[pnum].InvGrid[xx + yy] = plr[pnum]._pNumInv;
1056 			GetPlrHandSeed(&plr[pnum].InvList[ii]);
1057 			int gold = plr[pnum].HoldItem._ivalue;
1058 			if (gold > MaxGold) {
1059 				gold -= MaxGold;
1060 				plr[pnum].HoldItem._ivalue = gold;
1061 				GetPlrHandSeed(&plr[pnum].HoldItem);
1062 				plr[pnum].InvList[ii]._ivalue = MaxGold;
1063 			} else {
1064 				plr[pnum].HoldItem._ivalue = 0;
1065 				done = true;
1066 				plr[pnum]._pGold = CalculateGold(pnum);
1067 				SetCursor_(CURSOR_HAND);
1068 			}
1069 		}
1070 	}
1071 
1072 	return done;
1073 }
1074 
WeaponAutoPlace(int pnum)1075 BOOL WeaponAutoPlace(int pnum)
1076 {
1077 	if (plr[pnum]._pClass == PC_MONK)
1078 		return FALSE;
1079 	if (plr[pnum].HoldItem._iLoc != ILOC_TWOHAND
1080 	    || (plr[pnum]._pClass == PC_BARBARIAN && (plr[pnum].HoldItem._itype == ITYPE_SWORD || plr[pnum].HoldItem._itype == ITYPE_MACE))) {
1081 		if (plr[pnum]._pClass != PC_BARD) {
1082 			if (!plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON)
1083 				return FALSE;
1084 			if (!plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty() && plr[pnum].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON)
1085 				return FALSE;
1086 		}
1087 
1088 		if (plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty()) {
1089 			NetSendCmdChItem(TRUE, INVLOC_HAND_LEFT);
1090 			plr[pnum].InvBody[INVLOC_HAND_LEFT] = plr[pnum].HoldItem;
1091 			return TRUE;
1092 		}
1093 		if (plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty() && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iLoc != ILOC_TWOHAND) {
1094 			NetSendCmdChItem(TRUE, INVLOC_HAND_RIGHT);
1095 			plr[pnum].InvBody[INVLOC_HAND_RIGHT] = plr[pnum].HoldItem;
1096 			return TRUE;
1097 		}
1098 	} else if (plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() && plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
1099 		NetSendCmdChItem(TRUE, INVLOC_HAND_LEFT);
1100 		plr[pnum].InvBody[INVLOC_HAND_LEFT] = plr[pnum].HoldItem;
1101 		return TRUE;
1102 	}
1103 
1104 	return FALSE;
1105 }
1106 
SwapItem(ItemStruct * a,ItemStruct * b)1107 int SwapItem(ItemStruct *a, ItemStruct *b)
1108 {
1109 	ItemStruct h;
1110 
1111 	h = *a;
1112 	*a = *b;
1113 	*b = h;
1114 
1115 	return h._iCurs + CURSOR_FIRSTITEM;
1116 }
1117 
CheckInvPaste(int pnum,int mx,int my)1118 void CheckInvPaste(int pnum, int mx, int my)
1119 {
1120 	int r, sx, sy;
1121 	int i, j, xx, yy, ii;
1122 	BOOL done, done2h;
1123 	int il, cn, it, iv, ig, gt;
1124 	ItemStruct tempitem;
1125 
1126 	SetICursor(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
1127 	i = mx + (icursW >> 1);
1128 	j = my + (icursH >> 1);
1129 	sx = icursW28;
1130 	sy = icursH28;
1131 	done = FALSE;
1132 	for (r = 0; (DWORD)r < NUM_XY_SLOTS && !done; r++) {
1133 		int xo = RIGHT_PANEL;
1134 		int yo = 0;
1135 		if (r >= SLOTXY_BELT_FIRST) {
1136 			xo = PANEL_LEFT;
1137 			yo = PANEL_TOP;
1138 		}
1139 
1140 		if (i >= InvRect[r].X + xo && i < InvRect[r].X + xo + INV_SLOT_SIZE_PX) {
1141 			if (j >= InvRect[r].Y + yo - INV_SLOT_SIZE_PX - 1 && j < InvRect[r].Y + yo) {
1142 				done = TRUE;
1143 				r--;
1144 			}
1145 		}
1146 		if (r == SLOTXY_CHEST_LAST) {
1147 			if ((sx & 1) == 0)
1148 				i -= 14;
1149 			if ((sy & 1) == 0)
1150 				j -= 14;
1151 		}
1152 		if (r == SLOTXY_INV_LAST && (sy & 1) == 0)
1153 			j += 14;
1154 	}
1155 	if (!done)
1156 		return;
1157 	il = ILOC_UNEQUIPABLE;
1158 	if (r >= SLOTXY_HEAD_FIRST && r <= SLOTXY_HEAD_LAST)
1159 		il = ILOC_HELM;
1160 	if (r >= SLOTXY_RING_LEFT && r <= SLOTXY_RING_RIGHT)
1161 		il = ILOC_RING;
1162 	if (r == SLOTXY_AMULET)
1163 		il = ILOC_AMULET;
1164 	if (r >= SLOTXY_HAND_LEFT_FIRST && r <= SLOTXY_HAND_RIGHT_LAST)
1165 		il = ILOC_ONEHAND;
1166 	if (r >= SLOTXY_CHEST_FIRST && r <= SLOTXY_CHEST_LAST)
1167 		il = ILOC_ARMOR;
1168 	if (r >= SLOTXY_BELT_FIRST && r <= SLOTXY_BELT_LAST)
1169 		il = ILOC_BELT;
1170 	done = FALSE;
1171 	if (plr[pnum].HoldItem._iLoc == il)
1172 		done = TRUE;
1173 	if (il == ILOC_ONEHAND && plr[pnum].HoldItem._iLoc == ILOC_TWOHAND) {
1174 		if (plr[pnum]._pClass == PC_BARBARIAN
1175 		    && (plr[pnum].HoldItem._itype == ITYPE_SWORD || plr[pnum].HoldItem._itype == ITYPE_MACE))
1176 			il = ILOC_ONEHAND;
1177 		else
1178 			il = ILOC_TWOHAND;
1179 		done = TRUE;
1180 	}
1181 	if (plr[pnum].HoldItem._iLoc == ILOC_UNEQUIPABLE && il == ILOC_BELT) {
1182 		if (sx == 1 && sy == 1) {
1183 			done = TRUE;
1184 			if (!AllItemsList[plr[pnum].HoldItem.IDidx].iUsable)
1185 				done = FALSE;
1186 			if (!plr[pnum].HoldItem._iStatFlag)
1187 				done = FALSE;
1188 			if (plr[pnum].HoldItem._itype == ITYPE_GOLD)
1189 				done = FALSE;
1190 		}
1191 	}
1192 
1193 	if (il == ILOC_UNEQUIPABLE) {
1194 		done = TRUE;
1195 		it = 0;
1196 		ii = r - SLOTXY_INV_FIRST;
1197 		if (plr[pnum].HoldItem._itype == ITYPE_GOLD) {
1198 			yy = 10 * (ii / 10);
1199 			xx = ii % 10;
1200 			if (plr[pnum].InvGrid[xx + yy] != 0) {
1201 				iv = plr[pnum].InvGrid[xx + yy];
1202 				if (iv > 0) {
1203 					if (plr[pnum].InvList[iv - 1]._itype != ITYPE_GOLD) {
1204 						it = iv;
1205 					}
1206 				} else {
1207 					it = -iv;
1208 				}
1209 			}
1210 		} else {
1211 			yy = 10 * ((ii / 10) - ((sy - 1) >> 1));
1212 			if (yy < 0)
1213 				yy = 0;
1214 			for (j = 0; j < sy && done; j++) {
1215 				if (yy >= NUM_INV_GRID_ELEM)
1216 					done = FALSE;
1217 				xx = (ii % 10) - ((sx - 1) >> 1);
1218 				if (xx < 0)
1219 					xx = 0;
1220 				for (i = 0; i < sx && done; i++) {
1221 					if (xx >= 10) {
1222 						done = FALSE;
1223 					} else {
1224 						if (plr[pnum].InvGrid[xx + yy] != 0) {
1225 							iv = plr[pnum].InvGrid[xx + yy];
1226 							if (iv < 0)
1227 								iv = -iv;
1228 							if (it != 0) {
1229 								if (it != iv)
1230 									done = FALSE;
1231 							} else
1232 								it = iv;
1233 						}
1234 					}
1235 					xx++;
1236 				}
1237 				yy += 10;
1238 			}
1239 		}
1240 	}
1241 
1242 	if (!done)
1243 		return;
1244 
1245 	if (il != ILOC_UNEQUIPABLE && il != ILOC_BELT && !plr[pnum].HoldItem._iStatFlag) {
1246 		done = FALSE;
1247 		if (plr[pnum]._pClass == PC_WARRIOR)
1248 			PlaySFX(PS_WARR13);
1249 		else if (plr[pnum]._pClass == PC_ROGUE)
1250 			PlaySFX(PS_ROGUE13);
1251 		else if (plr[pnum]._pClass == PC_SORCERER)
1252 			PlaySFX(PS_MAGE13);
1253 		else if (plr[pnum]._pClass == PC_MONK)
1254 			PlaySFX(PS_MONK13);
1255 		else if (plr[pnum]._pClass == PC_BARD)
1256 			PlaySFX(PS_ROGUE13);
1257 		else if (plr[pnum]._pClass == PC_BARBARIAN)
1258 			PlaySFX(PS_MAGE13);
1259 	}
1260 
1261 	if (!done)
1262 		return;
1263 
1264 	if (pnum == myplr)
1265 		PlaySFX(ItemInvSnds[ItemCAnimTbl[plr[pnum].HoldItem._iCurs]]);
1266 
1267 	cn = CURSOR_HAND;
1268 	switch (il) {
1269 	case ILOC_HELM:
1270 		NetSendCmdChItem(FALSE, INVLOC_HEAD);
1271 		if (plr[pnum].InvBody[INVLOC_HEAD].isEmpty())
1272 			plr[pnum].InvBody[INVLOC_HEAD] = plr[pnum].HoldItem;
1273 		else
1274 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_HEAD], &plr[pnum].HoldItem);
1275 		break;
1276 	case ILOC_RING:
1277 		if (r == SLOTXY_RING_LEFT) {
1278 			NetSendCmdChItem(FALSE, INVLOC_RING_LEFT);
1279 			if (plr[pnum].InvBody[INVLOC_RING_LEFT].isEmpty())
1280 				plr[pnum].InvBody[INVLOC_RING_LEFT] = plr[pnum].HoldItem;
1281 			else
1282 				cn = SwapItem(&plr[pnum].InvBody[INVLOC_RING_LEFT], &plr[pnum].HoldItem);
1283 		} else {
1284 			NetSendCmdChItem(FALSE, INVLOC_RING_RIGHT);
1285 			if (plr[pnum].InvBody[INVLOC_RING_RIGHT].isEmpty())
1286 				plr[pnum].InvBody[INVLOC_RING_RIGHT] = plr[pnum].HoldItem;
1287 			else
1288 				cn = SwapItem(&plr[pnum].InvBody[INVLOC_RING_RIGHT], &plr[pnum].HoldItem);
1289 		}
1290 		break;
1291 	case ILOC_AMULET:
1292 		NetSendCmdChItem(FALSE, INVLOC_AMULET);
1293 		if (plr[pnum].InvBody[INVLOC_AMULET].isEmpty())
1294 			plr[pnum].InvBody[INVLOC_AMULET] = plr[pnum].HoldItem;
1295 		else
1296 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_AMULET], &plr[pnum].HoldItem);
1297 		break;
1298 	case ILOC_ONEHAND:
1299 		if (r <= SLOTXY_HAND_LEFT_LAST) {
1300 			if (plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty()) {
1301 				if ((plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty() || plr[pnum].InvBody[INVLOC_HAND_RIGHT]._iClass != plr[pnum].HoldItem._iClass)
1302 				    || (plr[pnum]._pClass == PC_BARD && plr[pnum].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON && plr[pnum].HoldItem._iClass == ICLASS_WEAPON)) {
1303 					NetSendCmdChItem(FALSE, INVLOC_HAND_LEFT);
1304 					plr[pnum].InvBody[INVLOC_HAND_LEFT] = plr[pnum].HoldItem;
1305 				} else {
1306 					NetSendCmdChItem(FALSE, INVLOC_HAND_RIGHT);
1307 					cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_RIGHT], &plr[pnum].HoldItem);
1308 				}
1309 				break;
1310 			}
1311 			if ((plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty() || plr[pnum].InvBody[INVLOC_HAND_RIGHT]._iClass != plr[pnum].HoldItem._iClass)
1312 			    || (plr[pnum]._pClass == PC_BARD && plr[pnum].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON && plr[pnum].HoldItem._iClass == ICLASS_WEAPON)) {
1313 				NetSendCmdChItem(FALSE, INVLOC_HAND_LEFT);
1314 				cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_LEFT], &plr[pnum].HoldItem);
1315 				break;
1316 			}
1317 
1318 			NetSendCmdChItem(FALSE, INVLOC_HAND_RIGHT);
1319 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_RIGHT], &plr[pnum].HoldItem);
1320 			break;
1321 		}
1322 		if (plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
1323 			if ((plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() || plr[pnum].InvBody[INVLOC_HAND_LEFT]._iLoc != ILOC_TWOHAND)
1324 			    || (plr[pnum]._pClass == PC_BARBARIAN && (plr[pnum].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SWORD || plr[pnum].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_MACE))) {
1325 				if ((plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() || plr[pnum].InvBody[INVLOC_HAND_LEFT]._iClass != plr[pnum].HoldItem._iClass)
1326 				    || (plr[pnum]._pClass == PC_BARD && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON && plr[pnum].HoldItem._iClass == ICLASS_WEAPON)) {
1327 					NetSendCmdChItem(FALSE, INVLOC_HAND_RIGHT);
1328 					plr[pnum].InvBody[INVLOC_HAND_RIGHT] = plr[pnum].HoldItem;
1329 					break;
1330 				}
1331 				NetSendCmdChItem(FALSE, INVLOC_HAND_LEFT);
1332 				cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_LEFT], &plr[pnum].HoldItem);
1333 				break;
1334 			}
1335 			NetSendCmdDelItem(FALSE, INVLOC_HAND_LEFT);
1336 			NetSendCmdChItem(FALSE, INVLOC_HAND_RIGHT);
1337 			SwapItem(&plr[pnum].InvBody[INVLOC_HAND_RIGHT], &plr[pnum].InvBody[INVLOC_HAND_LEFT]);
1338 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_RIGHT], &plr[pnum].HoldItem);
1339 			break;
1340 		}
1341 
1342 		if ((!plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iClass == plr[pnum].HoldItem._iClass)
1343 		    && !(plr[pnum]._pClass == PC_BARD && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON && plr[pnum].HoldItem._iClass == ICLASS_WEAPON)) {
1344 			NetSendCmdChItem(FALSE, INVLOC_HAND_LEFT);
1345 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_LEFT], &plr[pnum].HoldItem);
1346 			break;
1347 		}
1348 		NetSendCmdChItem(FALSE, INVLOC_HAND_RIGHT);
1349 		cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_RIGHT], &plr[pnum].HoldItem);
1350 		break;
1351 	case ILOC_TWOHAND:
1352 		NetSendCmdDelItem(FALSE, INVLOC_HAND_RIGHT);
1353 		if (!plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() && !plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
1354 			tempitem = plr[pnum].HoldItem;
1355 			if (plr[pnum].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD)
1356 				plr[pnum].HoldItem = plr[pnum].InvBody[INVLOC_HAND_RIGHT];
1357 			else
1358 				plr[pnum].HoldItem = plr[pnum].InvBody[INVLOC_HAND_LEFT];
1359 			if (pnum == myplr)
1360 				SetCursor_(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
1361 			else
1362 				SetICursor(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
1363 			done2h = FALSE;
1364 			for (i = 0; i < NUM_INV_GRID_ELEM && !done2h; i++)
1365 				done2h = AutoPlace(pnum, i, icursW28, icursH28, TRUE);
1366 			plr[pnum].HoldItem = tempitem;
1367 			if (pnum == myplr)
1368 				SetCursor_(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
1369 			else
1370 				SetICursor(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
1371 			if (!done2h)
1372 				return;
1373 
1374 			if (plr[pnum].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD)
1375 				plr[pnum].InvBody[INVLOC_HAND_RIGHT]._itype = ITYPE_NONE;
1376 			else
1377 				plr[pnum].InvBody[INVLOC_HAND_LEFT]._itype = ITYPE_NONE;
1378 		}
1379 
1380 		if (!plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty() || !plr[pnum].InvBody[INVLOC_HAND_RIGHT].isEmpty()) {
1381 			NetSendCmdChItem(FALSE, INVLOC_HAND_LEFT);
1382 			if (plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty())
1383 				SwapItem(&plr[pnum].InvBody[INVLOC_HAND_LEFT], &plr[pnum].InvBody[INVLOC_HAND_RIGHT]);
1384 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_HAND_LEFT], &plr[pnum].HoldItem);
1385 		} else {
1386 			NetSendCmdChItem(FALSE, INVLOC_HAND_LEFT);
1387 			plr[pnum].InvBody[INVLOC_HAND_LEFT] = plr[pnum].HoldItem;
1388 		}
1389 		if (plr[pnum].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_STAFF && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iSpell != SPL_NULL && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iCharges > 0) {
1390 			plr[pnum]._pRSpell = plr[pnum].InvBody[INVLOC_HAND_LEFT]._iSpell;
1391 			plr[pnum]._pRSplType = RSPLTYPE_CHARGES;
1392 			force_redraw = 255;
1393 		}
1394 		break;
1395 	case ILOC_ARMOR:
1396 		NetSendCmdChItem(FALSE, INVLOC_CHEST);
1397 		if (plr[pnum].InvBody[INVLOC_CHEST].isEmpty())
1398 			plr[pnum].InvBody[INVLOC_CHEST] = plr[pnum].HoldItem;
1399 		else
1400 			cn = SwapItem(&plr[pnum].InvBody[INVLOC_CHEST], &plr[pnum].HoldItem);
1401 		break;
1402 	case ILOC_UNEQUIPABLE:
1403 		if (plr[pnum].HoldItem._itype == ITYPE_GOLD && it == 0) {
1404 			ii = r - SLOTXY_INV_FIRST;
1405 			yy = 10 * (ii / 10);
1406 			xx = ii % 10;
1407 			if (plr[pnum].InvGrid[yy + xx] > 0) {
1408 				il = plr[pnum].InvGrid[yy + xx];
1409 				il--;
1410 				gt = plr[pnum].InvList[il]._ivalue;
1411 				ig = plr[pnum].HoldItem._ivalue + gt;
1412 				if (ig <= MaxGold) {
1413 					plr[pnum].InvList[il]._ivalue = ig;
1414 					plr[pnum]._pGold += plr[pnum].HoldItem._ivalue;
1415 					SetPlrHandGoldCurs(&plr[pnum].InvList[il]);
1416 				} else {
1417 					ig = MaxGold - gt;
1418 					plr[pnum]._pGold += ig;
1419 					plr[pnum].HoldItem._ivalue -= ig;
1420 					plr[pnum].InvList[il]._ivalue = MaxGold;
1421 					plr[pnum].InvList[il]._iCurs = ICURS_GOLD_LARGE;
1422 					// BUGFIX: incorrect values here are leftover from beta (fixed)
1423 					cn = GetGoldCursor(plr[pnum].HoldItem._ivalue);
1424 					cn += CURSOR_FIRSTITEM;
1425 				}
1426 			} else {
1427 				il = plr[pnum]._pNumInv;
1428 				plr[pnum].InvList[il] = plr[pnum].HoldItem;
1429 				plr[pnum]._pNumInv++;
1430 				plr[pnum].InvGrid[yy + xx] = plr[pnum]._pNumInv;
1431 				plr[pnum]._pGold += plr[pnum].HoldItem._ivalue;
1432 				SetPlrHandGoldCurs(&plr[pnum].InvList[il]);
1433 			}
1434 		} else {
1435 			if (it == 0) {
1436 				plr[pnum].InvList[plr[pnum]._pNumInv] = plr[pnum].HoldItem;
1437 				plr[pnum]._pNumInv++;
1438 				it = plr[pnum]._pNumInv;
1439 			} else {
1440 				il = it - 1;
1441 				if (plr[pnum].HoldItem._itype == ITYPE_GOLD)
1442 					plr[pnum]._pGold += plr[pnum].HoldItem._ivalue;
1443 				cn = SwapItem(&plr[pnum].InvList[il], &plr[pnum].HoldItem);
1444 				if (plr[pnum].HoldItem._itype == ITYPE_GOLD)
1445 					plr[pnum]._pGold = CalculateGold(pnum);
1446 				for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
1447 					if (plr[pnum].InvGrid[i] == it)
1448 						plr[pnum].InvGrid[i] = 0;
1449 					if (plr[pnum].InvGrid[i] == -it)
1450 						plr[pnum].InvGrid[i] = 0;
1451 				}
1452 			}
1453 			ii = r - SLOTXY_INV_FIRST;
1454 			yy = 10 * (ii / 10 - ((sy - 1) >> 1));
1455 			if (yy < 0)
1456 				yy = 0;
1457 			for (j = 0; j < sy; j++) {
1458 				xx = (ii % 10 - ((sx - 1) >> 1));
1459 				if (xx < 0)
1460 					xx = 0;
1461 				for (i = 0; i < sx; i++) {
1462 					if (i != 0 || j != sy - 1)
1463 						plr[pnum].InvGrid[xx + yy] = -it;
1464 					else
1465 						plr[pnum].InvGrid[xx + yy] = it;
1466 					xx++;
1467 				}
1468 				yy += 10;
1469 			}
1470 		}
1471 		break;
1472 	case ILOC_BELT:
1473 		ii = r - SLOTXY_BELT_FIRST;
1474 		if (plr[pnum].HoldItem._itype == ITYPE_GOLD) {
1475 			if (!plr[pnum].SpdList[ii].isEmpty()) {
1476 				if (plr[pnum].SpdList[ii]._itype == ITYPE_GOLD) {
1477 					i = plr[pnum].HoldItem._ivalue + plr[pnum].SpdList[ii]._ivalue;
1478 					if (i <= MaxGold) {
1479 						plr[pnum].SpdList[ii]._ivalue = i;
1480 						plr[pnum]._pGold += plr[pnum].HoldItem._ivalue;
1481 						SetPlrHandGoldCurs(&plr[pnum].SpdList[ii]);
1482 					} else {
1483 						i = MaxGold - plr[pnum].SpdList[ii]._ivalue;
1484 						plr[pnum]._pGold += i;
1485 						plr[pnum].HoldItem._ivalue -= i;
1486 						plr[pnum].SpdList[ii]._ivalue = MaxGold;
1487 						plr[pnum].SpdList[ii]._iCurs = ICURS_GOLD_LARGE;
1488 
1489 						// BUGFIX: incorrect values here are leftover from beta (fixed)
1490 						cn = GetGoldCursor(plr[pnum].HoldItem._ivalue);
1491 						cn += CURSOR_FIRSTITEM;
1492 					}
1493 				} else {
1494 					plr[pnum]._pGold += plr[pnum].HoldItem._ivalue;
1495 					cn = SwapItem(&plr[pnum].SpdList[ii], &plr[pnum].HoldItem);
1496 				}
1497 			} else {
1498 				plr[pnum].SpdList[ii] = plr[pnum].HoldItem;
1499 				plr[pnum]._pGold += plr[pnum].HoldItem._ivalue;
1500 			}
1501 		} else if (plr[pnum].SpdList[ii].isEmpty()) {
1502 			plr[pnum].SpdList[ii] = plr[pnum].HoldItem;
1503 		} else {
1504 			cn = SwapItem(&plr[pnum].SpdList[ii], &plr[pnum].HoldItem);
1505 			if (plr[pnum].HoldItem._itype == ITYPE_GOLD)
1506 				plr[pnum]._pGold = CalculateGold(pnum);
1507 		}
1508 		drawsbarflag = TRUE;
1509 		break;
1510 	}
1511 	CalcPlrInv(pnum, TRUE);
1512 	if (pnum == myplr) {
1513 		if (cn == CURSOR_HAND)
1514 			SetCursorPos(MouseX + (cursW >> 1), MouseY + (cursH >> 1));
1515 		SetCursor_(cn);
1516 	}
1517 }
1518 
CheckInvSwap(int pnum,BYTE bLoc,int idx,WORD wCI,int seed,BOOL bId,uint32_t dwBuff)1519 void CheckInvSwap(int pnum, BYTE bLoc, int idx, WORD wCI, int seed, BOOL bId, uint32_t dwBuff)
1520 {
1521 	PlayerStruct *p;
1522 
1523 	memset(&item[MAXITEMS], 0, sizeof(*item));
1524 	RecreateItem(MAXITEMS, idx, wCI, seed, 0, (dwBuff & CF_HELLFIRE) != 0);
1525 
1526 	p = &plr[pnum];
1527 	p->HoldItem = item[MAXITEMS];
1528 
1529 	if (bId) {
1530 		p->HoldItem._iIdentified = TRUE;
1531 	}
1532 
1533 	if (bLoc < NUM_INVLOC) {
1534 		p->InvBody[bLoc] = p->HoldItem;
1535 
1536 		if (bLoc == INVLOC_HAND_LEFT && p->HoldItem._iLoc == ILOC_TWOHAND) {
1537 			p->InvBody[INVLOC_HAND_RIGHT]._itype = ITYPE_NONE;
1538 		} else if (bLoc == INVLOC_HAND_RIGHT && p->HoldItem._iLoc == ILOC_TWOHAND) {
1539 			p->InvBody[INVLOC_HAND_LEFT]._itype = ITYPE_NONE;
1540 		}
1541 	}
1542 
1543 	CalcPlrInv(pnum, TRUE);
1544 }
1545 
CheckInvCut(int pnum,int mx,int my,bool automaticMove)1546 void CheckInvCut(int pnum, int mx, int my, bool automaticMove)
1547 {
1548 	int r;
1549 	BOOL done;
1550 	char ii;
1551 	int iv, i, j, offs, ig;
1552 	PlayerStruct &player = plr[pnum];
1553 
1554 	if (player._pmode > PM_WALK3) {
1555 		return;
1556 	}
1557 
1558 	if (dropGoldFlag) {
1559 		dropGoldFlag = FALSE;
1560 		dropGoldValue = 0;
1561 	}
1562 
1563 	done = FALSE;
1564 
1565 	for (r = 0; (DWORD)r < NUM_XY_SLOTS && !done; r++) {
1566 		int xo = RIGHT_PANEL;
1567 		int yo = 0;
1568 		if (r >= SLOTXY_BELT_FIRST) {
1569 			xo = PANEL_LEFT;
1570 			yo = PANEL_TOP;
1571 		}
1572 
1573 		// check which inventory rectangle the mouse is in, if any
1574 		if (mx >= InvRect[r].X + xo
1575 		    && mx < InvRect[r].X + xo + (INV_SLOT_SIZE_PX + 1)
1576 		    && my >= InvRect[r].Y + yo - (INV_SLOT_SIZE_PX + 1)
1577 		    && my < InvRect[r].Y + yo) {
1578 			done = TRUE;
1579 			r--;
1580 		}
1581 	}
1582 
1583 	if (!done) {
1584 		// not on an inventory slot rectangle
1585 		return;
1586 	}
1587 
1588 	ItemStruct &holdItem = player.HoldItem;
1589 	holdItem._itype = ITYPE_NONE;
1590 
1591 	bool automaticallyMoved = false;
1592 	bool automaticallyEquipped = false;
1593 	bool automaticallyUnequip = false;
1594 
1595 	ItemStruct &headItem = player.InvBody[INVLOC_HEAD];
1596 	if (r >= SLOTXY_HEAD_FIRST && r <= SLOTXY_HEAD_LAST && !headItem.isEmpty()) {
1597 		holdItem = headItem;
1598 		if (automaticMove) {
1599 			automaticallyUnequip = true;
1600 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1601 		}
1602 
1603 		if (!automaticMove || automaticallyMoved) {
1604 			NetSendCmdDelItem(FALSE, INVLOC_HEAD);
1605 			headItem._itype = ITYPE_NONE;
1606 		}
1607 	}
1608 
1609 	ItemStruct &leftRingItem = player.InvBody[INVLOC_RING_LEFT];
1610 	if (r == SLOTXY_RING_LEFT && !leftRingItem.isEmpty()) {
1611 		holdItem = leftRingItem;
1612 		if (automaticMove) {
1613 			automaticallyUnequip = true;
1614 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1615 		}
1616 
1617 		if (!automaticMove || automaticallyMoved) {
1618 			NetSendCmdDelItem(FALSE, INVLOC_RING_LEFT);
1619 			leftRingItem._itype = ITYPE_NONE;
1620 		}
1621 	}
1622 
1623 	ItemStruct &rightRingItem = player.InvBody[INVLOC_RING_RIGHT];
1624 	if (r == SLOTXY_RING_RIGHT && !rightRingItem.isEmpty()) {
1625 		holdItem = rightRingItem;
1626 		if (automaticMove) {
1627 			automaticallyUnequip = true;
1628 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1629 		}
1630 
1631 		if (!automaticMove || automaticallyMoved) {
1632 			NetSendCmdDelItem(FALSE, INVLOC_RING_RIGHT);
1633 			rightRingItem._itype = ITYPE_NONE;
1634 		}
1635 	}
1636 
1637 	ItemStruct &amuletItem = player.InvBody[INVLOC_AMULET];
1638 	if (r == SLOTXY_AMULET && !amuletItem.isEmpty()) {
1639 		holdItem = amuletItem;
1640 		if (automaticMove) {
1641 			automaticallyUnequip = true;
1642 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1643 		}
1644 
1645 		if (!automaticMove || automaticallyMoved) {
1646 			NetSendCmdDelItem(FALSE, INVLOC_AMULET);
1647 			amuletItem._itype = ITYPE_NONE;
1648 		}
1649 	}
1650 
1651 	ItemStruct &leftHandItem = player.InvBody[INVLOC_HAND_LEFT];
1652 	if (r >= SLOTXY_HAND_LEFT_FIRST && r <= SLOTXY_HAND_LEFT_LAST && !leftHandItem.isEmpty()) {
1653 		holdItem = leftHandItem;
1654 		if (automaticMove) {
1655 			automaticallyUnequip = true;
1656 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1657 		}
1658 
1659 		if (!automaticMove || automaticallyMoved) {
1660 			NetSendCmdDelItem(FALSE, INVLOC_HAND_LEFT);
1661 			leftHandItem._itype = ITYPE_NONE;
1662 		}
1663 	}
1664 
1665 	ItemStruct &rightHandItem = player.InvBody[INVLOC_HAND_RIGHT];
1666 	if (r >= SLOTXY_HAND_RIGHT_FIRST && r <= SLOTXY_HAND_RIGHT_LAST && !rightHandItem.isEmpty()) {
1667 		holdItem = rightHandItem;
1668 		if (automaticMove) {
1669 			automaticallyUnequip = true;
1670 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1671 		}
1672 
1673 		if (!automaticMove || automaticallyMoved) {
1674 			NetSendCmdDelItem(FALSE, INVLOC_HAND_RIGHT);
1675 			rightHandItem._itype = ITYPE_NONE;
1676 		}
1677 	}
1678 
1679 	ItemStruct &chestItem = player.InvBody[INVLOC_CHEST];
1680 	if (r >= SLOTXY_CHEST_FIRST && r <= SLOTXY_CHEST_LAST && !chestItem.isEmpty()) {
1681 		holdItem = chestItem;
1682 		if (automaticMove) {
1683 			automaticallyUnequip = true;
1684 			automaticallyMoved = automaticallyEquipped = AutoPlaceItemInInventory(pnum, holdItem, true);
1685 		}
1686 
1687 		if (!automaticMove || automaticallyMoved) {
1688 			NetSendCmdDelItem(FALSE, INVLOC_CHEST);
1689 			chestItem._itype = ITYPE_NONE;
1690 		}
1691 	}
1692 
1693 	if (r >= SLOTXY_INV_FIRST && r <= SLOTXY_INV_LAST) {
1694 		ig = r - SLOTXY_INV_FIRST;
1695 		ii = player.InvGrid[ig];
1696 		if (ii != 0) {
1697 			iv = ii;
1698 			if (ii <= 0) {
1699 				iv = -ii;
1700 			}
1701 
1702 			holdItem = player.InvList[iv - 1];
1703 			if (automaticMove) {
1704 				if (CanBePlacedOnBelt(holdItem)) {
1705 					automaticallyMoved = AutoPlaceItemInBelt(pnum, holdItem, true);
1706 				} else {
1707 					automaticallyMoved = automaticallyEquipped = AutoEquip(pnum, holdItem);
1708 				}
1709 			}
1710 
1711 			if (!automaticMove || automaticallyMoved) {
1712 				for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
1713 					if (player.InvGrid[i] == iv || player.InvGrid[i] == -iv) {
1714 						player.InvGrid[i] = 0;
1715 					}
1716 				}
1717 
1718 				iv--;
1719 
1720 				player._pNumInv--;
1721 
1722 				if (player._pNumInv > 0 && player._pNumInv != iv) {
1723 					player.InvList[iv] = player.InvList[player._pNumInv];
1724 
1725 					for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
1726 						if (player.InvGrid[j] == player._pNumInv + 1) {
1727 							player.InvGrid[j] = iv + 1;
1728 						}
1729 						if (player.InvGrid[j] == -(player._pNumInv + 1)) {
1730 							player.InvGrid[j] = -iv - 1;
1731 						}
1732 					}
1733 				}
1734 			}
1735 		}
1736 	}
1737 
1738 	if (r >= SLOTXY_BELT_FIRST) {
1739 		ItemStruct &beltItem = player.SpdList[r - SLOTXY_BELT_FIRST];
1740 		if (!beltItem.isEmpty()) {
1741 			holdItem = beltItem;
1742 			if (automaticMove) {
1743 				automaticallyMoved = AutoPlaceItemInInventory(pnum, holdItem, true);
1744 			}
1745 
1746 			if (!automaticMove || automaticallyMoved) {
1747 				beltItem._itype = ITYPE_NONE;
1748 				drawsbarflag = TRUE;
1749 			}
1750 		}
1751 	}
1752 
1753 	if (!holdItem.isEmpty()) {
1754 		if (holdItem._itype == ITYPE_GOLD) {
1755 			player._pGold = CalculateGold(pnum);
1756 		}
1757 
1758 		CalcPlrInv(pnum, TRUE);
1759 		CheckItemStats(pnum);
1760 
1761 		if (pnum == myplr) {
1762 			if (automaticallyEquipped) {
1763 				PlaySFX(ItemInvSnds[ItemCAnimTbl[holdItem._iCurs]]);
1764 			} else if (!automaticMove || automaticallyMoved) {
1765 				PlaySFX(IS_IGRAB);
1766 			}
1767 
1768 			if (automaticMove) {
1769 				if (!automaticallyMoved) {
1770 					if (CanBePlacedOnBelt(holdItem) || automaticallyUnequip) {
1771 						switch (player._pClass) {
1772 						case PC_WARRIOR:
1773 						case PC_BARBARIAN:
1774 							PlaySFX(PS_WARR15, false);
1775 							break;
1776 						case PC_ROGUE:
1777 						case PC_BARD:
1778 							PlaySFX(PS_ROGUE15, false);
1779 							break;
1780 						case PC_SORCERER:
1781 							PlaySFX(PS_MAGE15, false);
1782 							break;
1783 						case PC_MONK:
1784 							PlaySFX(PS_MONK15, false);
1785 							break;
1786 						case NUM_CLASSES:
1787 							break;
1788 						}
1789 					} else {
1790 						switch (player._pClass) {
1791 						case PC_WARRIOR:
1792 						case PC_BARBARIAN:
1793 							PlaySFX(PS_WARR37, false);
1794 							break;
1795 						case PC_ROGUE:
1796 						case PC_BARD:
1797 							PlaySFX(PS_ROGUE37, false);
1798 							break;
1799 						case PC_SORCERER:
1800 							PlaySFX(PS_MAGE37, false);
1801 							break;
1802 						case PC_MONK:
1803 							PlaySFX(PS_MONK37, false);
1804 							break;
1805 						case NUM_CLASSES:
1806 							break;
1807 						}
1808 					}
1809 				}
1810 
1811 				holdItem._itype = ITYPE_NONE;
1812 			} else {
1813 				SetCursor_(holdItem._iCurs + CURSOR_FIRSTITEM);
1814 				SetCursorPos(mx - (cursW >> 1), MouseY - (cursH >> 1));
1815 			}
1816 		}
1817 	}
1818 }
1819 
inv_update_rem_item(int pnum,BYTE iv)1820 void inv_update_rem_item(int pnum, BYTE iv)
1821 {
1822 	if (iv < NUM_INVLOC) {
1823 		plr[pnum].InvBody[iv]._itype = ITYPE_NONE;
1824 	}
1825 
1826 	if (plr[pnum]._pmode != PM_DEATH) {
1827 		CalcPlrInv(pnum, TRUE);
1828 	} else {
1829 		CalcPlrInv(pnum, FALSE);
1830 	}
1831 }
1832 
RemoveInvItem(int pnum,int iv)1833 void RemoveInvItem(int pnum, int iv)
1834 {
1835 	int i, j;
1836 
1837 	iv++;
1838 
1839 	for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
1840 		if (plr[pnum].InvGrid[i] == iv || plr[pnum].InvGrid[i] == -iv) {
1841 			plr[pnum].InvGrid[i] = 0;
1842 		}
1843 	}
1844 
1845 	iv--;
1846 	plr[pnum]._pNumInv--;
1847 
1848 	if (plr[pnum]._pNumInv > 0 && plr[pnum]._pNumInv != iv) {
1849 		plr[pnum].InvList[iv] = plr[pnum].InvList[plr[pnum]._pNumInv];
1850 
1851 		for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
1852 			if (plr[pnum].InvGrid[j] == plr[pnum]._pNumInv + 1) {
1853 				plr[pnum].InvGrid[j] = iv + 1;
1854 			}
1855 			if (plr[pnum].InvGrid[j] == -(plr[pnum]._pNumInv + 1)) {
1856 				plr[pnum].InvGrid[j] = -(iv + 1);
1857 			}
1858 		}
1859 	}
1860 
1861 	CalcPlrScrolls(pnum);
1862 }
1863 
RemoveSpdBarItem(int pnum,int iv)1864 void RemoveSpdBarItem(int pnum, int iv)
1865 {
1866 	plr[pnum].SpdList[iv]._itype = ITYPE_NONE;
1867 
1868 	CalcPlrScrolls(pnum);
1869 	force_redraw = 255;
1870 }
1871 
CheckInvItem(bool isShiftHeld)1872 void CheckInvItem(bool isShiftHeld)
1873 {
1874 	if (pcurs >= CURSOR_FIRSTITEM) {
1875 		CheckInvPaste(myplr, MouseX, MouseY);
1876 	} else {
1877 		CheckInvCut(myplr, MouseX, MouseY, isShiftHeld);
1878 	}
1879 }
1880 
1881 /**
1882  * Check for interactions with belt
1883  */
CheckInvScrn(bool isShiftHeld)1884 void CheckInvScrn(bool isShiftHeld)
1885 {
1886 	if (MouseX > 190 + PANEL_LEFT && MouseX < 437 + PANEL_LEFT
1887 	    && MouseY > PANEL_TOP && MouseY < 33 + PANEL_TOP) {
1888 		CheckInvItem(isShiftHeld);
1889 	}
1890 }
1891 
CheckItemStats(int pnum)1892 void CheckItemStats(int pnum)
1893 {
1894 	PlayerStruct *p = &plr[pnum];
1895 
1896 	p->HoldItem._iStatFlag = FALSE;
1897 
1898 	if (p->_pStrength >= p->HoldItem._iMinStr
1899 	    && p->_pMagic >= p->HoldItem._iMinMag
1900 	    && p->_pDexterity >= p->HoldItem._iMinDex) {
1901 		p->HoldItem._iStatFlag = TRUE;
1902 	}
1903 }
1904 
CheckBookLevel(int pnum)1905 void CheckBookLevel(int pnum)
1906 {
1907 	int slvl;
1908 
1909 	if (plr[pnum].HoldItem._iMiscId == IMISC_BOOK) {
1910 		plr[pnum].HoldItem._iMinMag = spelldata[plr[pnum].HoldItem._iSpell].sMinInt;
1911 		slvl = plr[pnum]._pSplLvl[plr[pnum].HoldItem._iSpell];
1912 		while (slvl != 0) {
1913 			plr[pnum].HoldItem._iMinMag += 20 * plr[pnum].HoldItem._iMinMag / 100;
1914 			slvl--;
1915 			if (plr[pnum].HoldItem._iMinMag + 20 * plr[pnum].HoldItem._iMinMag / 100 > 255) {
1916 				plr[pnum].HoldItem._iMinMag = -1;
1917 				slvl = 0;
1918 			}
1919 		}
1920 	}
1921 }
1922 
CheckQuestItem(int pnum)1923 void CheckQuestItem(int pnum)
1924 {
1925 	if (plr[pnum].HoldItem.IDidx == IDI_OPTAMULET && quests[Q_BLIND]._qactive == QUEST_ACTIVE)
1926 		quests[Q_BLIND]._qactive = QUEST_DONE;
1927 	if (plr[pnum].HoldItem.IDidx == IDI_MUSHROOM && quests[Q_MUSHROOM]._qactive == QUEST_ACTIVE && quests[Q_MUSHROOM]._qvar1 == QS_MUSHSPAWNED) {
1928 		sfxdelay = 10;
1929 		if (plr[pnum]._pClass == PC_WARRIOR) { // BUGFIX: Voice for this quest might be wrong in MP
1930 			sfxdnum = PS_WARR95;
1931 		} else if (plr[pnum]._pClass == PC_ROGUE) {
1932 			sfxdnum = PS_ROGUE95;
1933 		} else if (plr[pnum]._pClass == PC_SORCERER) {
1934 			sfxdnum = PS_MAGE95;
1935 		} else if (plr[pnum]._pClass == PC_MONK) {
1936 			sfxdnum = PS_MONK95;
1937 		} else if (plr[pnum]._pClass == PC_BARD) {
1938 			sfxdnum = PS_ROGUE95;
1939 		} else if (plr[pnum]._pClass == PC_BARBARIAN) {
1940 			sfxdnum = PS_WARR95;
1941 		}
1942 		quests[Q_MUSHROOM]._qvar1 = QS_MUSHPICKED;
1943 	}
1944 	if (plr[pnum].HoldItem.IDidx == IDI_ANVIL && quests[Q_ANVIL]._qactive != QUEST_NOTAVAIL) {
1945 		if (quests[Q_ANVIL]._qactive == QUEST_INIT) {
1946 			quests[Q_ANVIL]._qactive = QUEST_ACTIVE;
1947 			quests[Q_ANVIL]._qvar1 = 1;
1948 		}
1949 		if (quests[Q_ANVIL]._qlog == TRUE) {
1950 			sfxdelay = 10;
1951 			if (plr[myplr]._pClass == PC_WARRIOR) {
1952 				sfxdnum = PS_WARR89;
1953 			} else if (plr[myplr]._pClass == PC_ROGUE) {
1954 				sfxdnum = PS_ROGUE89;
1955 			} else if (plr[myplr]._pClass == PC_SORCERER) {
1956 				sfxdnum = PS_MAGE89;
1957 			} else if (plr[myplr]._pClass == PC_MONK) {
1958 				sfxdnum = PS_MONK89;
1959 			} else if (plr[myplr]._pClass == PC_BARD) {
1960 				sfxdnum = PS_ROGUE89;
1961 			} else if (plr[myplr]._pClass == PC_BARBARIAN) {
1962 				sfxdnum = PS_WARR89;
1963 			}
1964 		}
1965 	}
1966 	if (plr[pnum].HoldItem.IDidx == IDI_GLDNELIX && quests[Q_VEIL]._qactive != QUEST_NOTAVAIL) {
1967 		sfxdelay = 30;
1968 		if (plr[myplr]._pClass == PC_WARRIOR) {
1969 			sfxdnum = PS_WARR88;
1970 		} else if (plr[myplr]._pClass == PC_ROGUE) {
1971 			sfxdnum = PS_ROGUE88;
1972 		} else if (plr[myplr]._pClass == PC_SORCERER) {
1973 			sfxdnum = PS_MAGE88;
1974 		} else if (plr[myplr]._pClass == PC_MONK) {
1975 			sfxdnum = PS_MONK88;
1976 		} else if (plr[myplr]._pClass == PC_BARD) {
1977 			sfxdnum = PS_ROGUE88;
1978 		} else if (plr[myplr]._pClass == PC_BARBARIAN) {
1979 			sfxdnum = PS_WARR88;
1980 		}
1981 	}
1982 	if (plr[pnum].HoldItem.IDidx == IDI_ROCK && quests[Q_ROCK]._qactive != QUEST_NOTAVAIL) {
1983 		if (quests[Q_ROCK]._qactive == QUEST_INIT) {
1984 			quests[Q_ROCK]._qactive = QUEST_ACTIVE;
1985 			quests[Q_ROCK]._qvar1 = 1;
1986 		}
1987 		if (quests[Q_ROCK]._qlog == TRUE) {
1988 			sfxdelay = 10;
1989 			if (plr[myplr]._pClass == PC_WARRIOR) {
1990 				sfxdnum = PS_WARR87;
1991 			} else if (plr[myplr]._pClass == PC_ROGUE) {
1992 				sfxdnum = PS_ROGUE87;
1993 			} else if (plr[myplr]._pClass == PC_SORCERER) {
1994 				sfxdnum = PS_MAGE87;
1995 			} else if (plr[myplr]._pClass == PC_MONK) {
1996 				sfxdnum = PS_MONK87;
1997 			} else if (plr[myplr]._pClass == PC_BARD) {
1998 				sfxdnum = PS_ROGUE87;
1999 			} else if (plr[myplr]._pClass == PC_BARBARIAN) {
2000 				sfxdnum = PS_WARR87;
2001 			}
2002 		}
2003 	}
2004 	if (plr[pnum].HoldItem.IDidx == IDI_ARMOFVAL && quests[Q_BLOOD]._qactive == QUEST_ACTIVE) {
2005 		quests[Q_BLOOD]._qactive = QUEST_DONE;
2006 		sfxdelay = 20;
2007 		if (plr[myplr]._pClass == PC_WARRIOR) {
2008 			sfxdnum = PS_WARR91;
2009 		} else if (plr[myplr]._pClass == PC_ROGUE) {
2010 			sfxdnum = PS_ROGUE91;
2011 		} else if (plr[myplr]._pClass == PC_SORCERER) {
2012 			sfxdnum = PS_MAGE91;
2013 		} else if (plr[myplr]._pClass == PC_MONK) {
2014 			sfxdnum = PS_MONK91;
2015 		} else if (plr[myplr]._pClass == PC_BARD) {
2016 			sfxdnum = PS_ROGUE91;
2017 		} else if (plr[myplr]._pClass == PC_BARBARIAN) {
2018 			sfxdnum = PS_WARR91;
2019 		}
2020 	}
2021 	if (plr[pnum].HoldItem.IDidx == IDI_MAPOFDOOM) {
2022 		quests[Q_GRAVE]._qlog = FALSE;
2023 		quests[Q_GRAVE]._qactive = QUEST_ACTIVE;
2024 		quests[Q_GRAVE]._qvar1 = 1;
2025 		sfxdelay = 10;
2026 		if (plr[myplr]._pClass == PC_WARRIOR) {
2027 			sfxdnum = PS_WARR79;
2028 		} else if (plr[myplr]._pClass == PC_ROGUE) {
2029 			sfxdnum = PS_ROGUE79;
2030 		} else if (plr[myplr]._pClass == PC_SORCERER) {
2031 			sfxdnum = PS_MAGE79;
2032 		} else if (plr[myplr]._pClass == PC_MONK) {
2033 			sfxdnum = PS_MONK79;
2034 		} else if (plr[myplr]._pClass == PC_BARD) {
2035 			sfxdnum = PS_ROGUE79;
2036 		} else if (plr[myplr]._pClass == PC_BARBARIAN) {
2037 			sfxdnum = PS_WARR79;
2038 		}
2039 	}
2040 	if (plr[pnum].HoldItem.IDidx == IDI_NOTE1 || plr[pnum].HoldItem.IDidx == IDI_NOTE2 || plr[pnum].HoldItem.IDidx == IDI_NOTE3) {
2041 		int mask, idx, item_num;
2042 		int n1, n2, n3;
2043 		ItemStruct tmp;
2044 		mask = 0;
2045 		idx = plr[pnum].HoldItem.IDidx;
2046 		if (PlrHasItem(pnum, IDI_NOTE1, &n1) || idx == IDI_NOTE1)
2047 			mask = 1;
2048 		if (PlrHasItem(pnum, IDI_NOTE2, &n2) || idx == IDI_NOTE2)
2049 			mask |= 2;
2050 		if (PlrHasItem(pnum, IDI_NOTE3, &n3) || idx == IDI_NOTE3)
2051 			mask |= 4;
2052 		if (mask == 7) {
2053 			sfxdelay = 10;
2054 			if (plr[myplr]._pClass == PC_WARRIOR) {
2055 				sfxdnum = PS_WARR46;
2056 			} else if (plr[myplr]._pClass == PC_ROGUE) {
2057 				sfxdnum = PS_ROGUE46;
2058 			} else if (plr[myplr]._pClass == PC_SORCERER) {
2059 				sfxdnum = PS_MAGE46;
2060 			} else if (plr[myplr]._pClass == PC_MONK) {
2061 				sfxdnum = PS_MONK46;
2062 			} else if (plr[myplr]._pClass == PC_BARD) {
2063 				sfxdnum = PS_ROGUE46;
2064 			} else if (plr[myplr]._pClass == PC_BARBARIAN) {
2065 				sfxdnum = PS_WARR46;
2066 			}
2067 			switch (idx) {
2068 			case IDI_NOTE1:
2069 				PlrHasItem(pnum, IDI_NOTE2, &n2);
2070 				RemoveInvItem(pnum, n2);
2071 				PlrHasItem(pnum, IDI_NOTE3, &n3);
2072 				RemoveInvItem(pnum, n3);
2073 				break;
2074 			case IDI_NOTE2:
2075 				PlrHasItem(pnum, IDI_NOTE1, &n1);
2076 				RemoveInvItem(pnum, n1);
2077 				PlrHasItem(pnum, IDI_NOTE3, &n3);
2078 				RemoveInvItem(pnum, n3);
2079 				break;
2080 			case IDI_NOTE3:
2081 				PlrHasItem(pnum, IDI_NOTE1, &n1);
2082 				RemoveInvItem(pnum, n1);
2083 				PlrHasItem(pnum, IDI_NOTE2, &n2);
2084 				RemoveInvItem(pnum, n2);
2085 				break;
2086 			}
2087 			item_num = itemactive[0];
2088 			tmp = item[item_num];
2089 			memset(&item[item_num], 0, sizeof(*item));
2090 			GetItemAttrs(item_num, IDI_FULLNOTE, 16);
2091 			SetupItem(item_num);
2092 			plr[pnum].HoldItem = item[item_num];
2093 			item[item_num] = tmp;
2094 		}
2095 	}
2096 }
2097 
CleanupItems(int ii)2098 void CleanupItems(int ii)
2099 {
2100 	dItem[item[ii]._ix][item[ii]._iy] = 0;
2101 
2102 	if (currlevel == 21 & item[ii]._ix == CornerStone.x && item[ii]._iy == CornerStone.y) {
2103 		CornerStone.item._itype = ITYPE_NONE;
2104 		CornerStone.item._iSelFlag = 0;
2105 		CornerStone.item._ix = 0;
2106 		CornerStone.item._iy = 0;
2107 		CornerStone.item._iAnimFlag = FALSE;
2108 		CornerStone.item._iIdentified = FALSE;
2109 		CornerStone.item._iPostDraw = FALSE;
2110 	}
2111 
2112 	int i = 0;
2113 	while (i < numitems) {
2114 		if (itemactive[i] == ii) {
2115 			DeleteItem(itemactive[i], i);
2116 			i = 0;
2117 			continue;
2118 		}
2119 
2120 		i++;
2121 	}
2122 }
2123 
InvGetItem(int pnum,int ii)2124 void InvGetItem(int pnum, int ii)
2125 {
2126 	if (dropGoldFlag) {
2127 		dropGoldFlag = FALSE;
2128 		dropGoldValue = 0;
2129 	}
2130 
2131 	if (dItem[item[ii]._ix][item[ii]._iy] == 0)
2132 		return;
2133 
2134 	if (myplr == pnum && pcurs >= CURSOR_FIRSTITEM)
2135 		NetSendCmdPItem(TRUE, CMD_SYNCPUTITEM, plr[myplr]._px, plr[myplr]._py);
2136 
2137 	item[ii]._iCreateInfo &= ~CF_PREGEN;
2138 	plr[pnum].HoldItem = item[ii];
2139 	CheckQuestItem(pnum);
2140 	CheckBookLevel(pnum);
2141 	CheckItemStats(pnum);
2142 	bool cursor_updated = false;
2143 	if (plr[pnum].HoldItem._itype == ITYPE_GOLD && GoldAutoPlace(pnum))
2144 		cursor_updated = true;
2145 	CleanupItems(ii);
2146 	pcursitem = -1;
2147 	if (!cursor_updated)
2148 		SetCursor_(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
2149 }
2150 
AutoGetItem(int pnum,int ii)2151 void AutoGetItem(int pnum, int ii)
2152 {
2153 	int i, idx;
2154 	int w, h;
2155 	BOOL done;
2156 
2157 	if (pcurs != CURSOR_HAND) {
2158 		return;
2159 	}
2160 
2161 	if (dropGoldFlag) {
2162 		dropGoldFlag = FALSE;
2163 		dropGoldValue = 0;
2164 	}
2165 
2166 	if (dItem[item[ii]._ix][item[ii]._iy] == 0)
2167 		return;
2168 
2169 	item[ii]._iCreateInfo &= ~CF_PREGEN;
2170 	plr[pnum].HoldItem = item[ii]; /// BUGFIX: overwrites cursor item, allowing for belt dupe bug
2171 	CheckQuestItem(pnum);
2172 	CheckBookLevel(pnum);
2173 	CheckItemStats(pnum);
2174 	SetICursor(plr[pnum].HoldItem._iCurs + CURSOR_FIRSTITEM);
2175 	if (plr[pnum].HoldItem._itype == ITYPE_GOLD) {
2176 		done = GoldAutoPlace(pnum);
2177 		if (!done) {
2178 			item[ii]._ivalue = plr[pnum].HoldItem._ivalue;
2179 			SetPlrHandGoldCurs(&item[ii]);
2180 		}
2181 	} else {
2182 		done = AutoEquipEnabled(plr[pnum], plr[pnum].HoldItem) && AutoEquip(pnum, plr[pnum].HoldItem);
2183 		if (!done) {
2184 			done = AutoPlaceItemInBelt(pnum, plr[pnum].HoldItem, true);
2185 		}
2186 		if (!done) {
2187 			done = AutoPlaceItemInInventory(pnum, plr[pnum].HoldItem, TRUE);
2188 		}
2189 	}
2190 
2191 	if (done) {
2192 		CleanupItems(ii);
2193 		return;
2194 	}
2195 
2196 	if (pnum == myplr) {
2197 		if (plr[pnum]._pClass == PC_WARRIOR) {
2198 			PlaySFX(random_(0, 3) + PS_WARR14);
2199 		} else if (plr[pnum]._pClass == PC_ROGUE) {
2200 			PlaySFX(random_(0, 3) + PS_ROGUE14);
2201 		} else if (plr[pnum]._pClass == PC_SORCERER) {
2202 			PlaySFX(random_(0, 3) + PS_MAGE14);
2203 		} else if (plr[pnum]._pClass == PC_MONK) {
2204 			PlaySFX(random_(0, 3) + PS_MONK14);
2205 		} else if (plr[pnum]._pClass == PC_BARD) {
2206 			PlaySFX(random_(0, 3) + PS_ROGUE14);
2207 		} else if (plr[pnum]._pClass == PC_BARBARIAN) {
2208 			PlaySFX(random_(0, 3) + PS_WARR14);
2209 		}
2210 	}
2211 	plr[pnum].HoldItem = item[ii];
2212 	RespawnItem(ii, TRUE);
2213 	NetSendCmdPItem(TRUE, CMD_RESPAWNITEM, item[ii]._ix, item[ii]._iy);
2214 	plr[pnum].HoldItem._itype = ITYPE_NONE;
2215 }
2216 
FindGetItem(int idx,WORD ci,int iseed)2217 int FindGetItem(int idx, WORD ci, int iseed)
2218 {
2219 	if (numitems <= 0)
2220 		return -1;
2221 
2222 	int ii;
2223 	int i = 0;
2224 	while (1) {
2225 		ii = itemactive[i];
2226 		if (item[ii].IDidx == idx && item[ii]._iSeed == iseed && item[ii]._iCreateInfo == ci)
2227 			break;
2228 
2229 		i++;
2230 
2231 		if (i >= numitems)
2232 			return -1;
2233 	}
2234 
2235 	return ii;
2236 }
2237 
SyncGetItem(int x,int y,int idx,WORD ci,int iseed)2238 void SyncGetItem(int x, int y, int idx, WORD ci, int iseed)
2239 {
2240 	int i, ii;
2241 
2242 	if (dItem[x][y]) {
2243 		ii = dItem[x][y] - 1;
2244 		if (item[ii].IDidx == idx
2245 		    && item[ii]._iSeed == iseed
2246 		    && item[ii]._iCreateInfo == ci) {
2247 			FindGetItem(idx, ci, iseed);
2248 		} else {
2249 			ii = FindGetItem(idx, ci, iseed);
2250 		}
2251 	} else {
2252 		ii = FindGetItem(idx, ci, iseed);
2253 	}
2254 
2255 	if (ii == -1)
2256 		return;
2257 
2258 	CleanupItems(ii);
2259 	assert(FindGetItem(idx, ci, iseed) == -1);
2260 }
2261 
CanPut(int x,int y)2262 BOOL CanPut(int x, int y)
2263 {
2264 	char oi, oi2;
2265 
2266 	if (dItem[x][y])
2267 		return FALSE;
2268 	if (nSolidTable[dPiece[x][y]])
2269 		return FALSE;
2270 
2271 	if (dObject[x][y] != 0) {
2272 		if (object[dObject[x][y] > 0 ? dObject[x][y] - 1 : -(dObject[x][y] + 1)]._oSolidFlag)
2273 			return FALSE;
2274 	}
2275 
2276 	oi = dObject[x + 1][y + 1];
2277 	if (oi > 0 && object[oi - 1]._oSelFlag != 0) {
2278 		return FALSE;
2279 	}
2280 	if (oi < 0 && object[-(oi + 1)]._oSelFlag != 0) {
2281 		return FALSE;
2282 	}
2283 
2284 	oi = dObject[x + 1][y];
2285 	if (oi > 0) {
2286 		oi2 = dObject[x][y + 1];
2287 		if (oi2 > 0 && object[oi - 1]._oSelFlag != 0 && object[oi2 - 1]._oSelFlag != 0)
2288 			return FALSE;
2289 	}
2290 
2291 	if (currlevel == 0 && dMonster[x][y] != 0)
2292 		return FALSE;
2293 	if (currlevel == 0 && dMonster[x + 1][y + 1] != 0)
2294 		return FALSE;
2295 
2296 	return TRUE;
2297 }
2298 
TryInvPut()2299 BOOL TryInvPut()
2300 {
2301 	int dir;
2302 
2303 	if (numitems >= MAXITEMS)
2304 		return FALSE;
2305 
2306 	dir = GetDirection(plr[myplr]._px, plr[myplr]._py, cursmx, cursmy);
2307 	if (CanPut(plr[myplr]._px + offset_x[dir], plr[myplr]._py + offset_y[dir])) {
2308 		return TRUE;
2309 	}
2310 
2311 	dir = (dir - 1) & 7;
2312 	if (CanPut(plr[myplr]._px + offset_x[dir], plr[myplr]._py + offset_y[dir])) {
2313 		return TRUE;
2314 	}
2315 
2316 	dir = (dir + 2) & 7;
2317 	if (CanPut(plr[myplr]._px + offset_x[dir], plr[myplr]._py + offset_y[dir])) {
2318 		return TRUE;
2319 	}
2320 
2321 	return CanPut(plr[myplr]._px, plr[myplr]._py);
2322 }
2323 
DrawInvMsg(const char * msg)2324 void DrawInvMsg(const char *msg)
2325 {
2326 	DWORD dwTicks;
2327 
2328 	dwTicks = SDL_GetTicks();
2329 	if (dwTicks - sgdwLastTime >= 5000) {
2330 		sgdwLastTime = dwTicks;
2331 		ErrorPlrMsg(msg);
2332 	}
2333 }
2334 
InvPutItem(int pnum,int x,int y)2335 int InvPutItem(int pnum, int x, int y)
2336 {
2337 	BOOL done;
2338 	int d;
2339 	int i, j, l;
2340 	int xx, yy;
2341 	int xp, yp;
2342 
2343 	if (numitems >= MAXITEMS)
2344 		return -1;
2345 
2346 	d = GetDirection(plr[pnum]._px, plr[pnum]._py, x, y);
2347 	xx = x - plr[pnum]._px;
2348 	yy = y - plr[pnum]._py;
2349 	if (abs(xx) > 1 || abs(yy) > 1) {
2350 		x = plr[pnum]._px + offset_x[d];
2351 		y = plr[pnum]._py + offset_y[d];
2352 	}
2353 	if (!CanPut(x, y)) {
2354 		d = (d - 1) & 7;
2355 		x = plr[pnum]._px + offset_x[d];
2356 		y = plr[pnum]._py + offset_y[d];
2357 		if (!CanPut(x, y)) {
2358 			d = (d + 2) & 7;
2359 			x = plr[pnum]._px + offset_x[d];
2360 			y = plr[pnum]._py + offset_y[d];
2361 			if (!CanPut(x, y)) {
2362 				done = FALSE;
2363 				for (l = 1; l < 50 && !done; l++) {
2364 					for (j = -l; j <= l && !done; j++) {
2365 						yp = j + plr[pnum]._py;
2366 						for (i = -l; i <= l && !done; i++) {
2367 							xp = i + plr[pnum]._px;
2368 							if (CanPut(xp, yp)) {
2369 								done = TRUE;
2370 								x = xp;
2371 								y = yp;
2372 							}
2373 						}
2374 					}
2375 				}
2376 				if (!done)
2377 					return -1;
2378 			}
2379 		}
2380 	}
2381 
2382 	if (currlevel == 0) {
2383 		yp = cursmy;
2384 		xp = cursmx;
2385 		if (plr[pnum].HoldItem._iCurs == ICURS_RUNE_BOMB && xp >= 79 && xp <= 82 && yp >= 61 && yp <= 64) {
2386 			NetSendCmdLocParam2(0, CMD_OPENHIVE, plr[pnum]._px, plr[pnum]._py, xx, yy);
2387 			quests[Q_FARMER]._qactive = 3;
2388 			if (gbIsMultiplayer) {
2389 				NetSendCmdQuest(TRUE, Q_FARMER);
2390 				return -1;
2391 			}
2392 			return -1;
2393 		}
2394 		if (plr[pnum].HoldItem.IDidx == IDI_MAPOFDOOM && xp >= 35 && xp <= 38 && yp >= 20 && yp <= 24) {
2395 			NetSendCmd(FALSE, CMD_OPENCRYPT);
2396 			quests[Q_GRAVE]._qactive = 3;
2397 			if (gbIsMultiplayer) {
2398 				NetSendCmdQuest(TRUE, Q_GRAVE);
2399 			}
2400 			return -1;
2401 		}
2402 	}
2403 
2404 	assert(CanPut(x, y));
2405 
2406 	int ii = AllocateItem();
2407 
2408 	dItem[x][y] = ii + 1;
2409 	item[ii] = plr[pnum].HoldItem;
2410 	item[ii]._ix = x;
2411 	item[ii]._iy = y;
2412 	RespawnItem(ii, TRUE);
2413 
2414 	if (currlevel == 21 && x == CornerStone.x && y == CornerStone.y) {
2415 		CornerStone.item = item[ii];
2416 		InitQTextMsg(TEXT_CORNSTN);
2417 		quests[Q_CORNSTN]._qlog = 0;
2418 		quests[Q_CORNSTN]._qactive = QUEST_DONE;
2419 	}
2420 
2421 	NewCursor(CURSOR_HAND);
2422 	return ii;
2423 }
2424 
SyncPutItem(int pnum,int x,int y,int idx,WORD icreateinfo,int iseed,int Id,int dur,int mdur,int ch,int mch,int ivalue,DWORD ibuff,int to_hit,int max_dam,int min_str,int min_mag,int min_dex,int ac)2425 int SyncPutItem(int pnum, int x, int y, int idx, WORD icreateinfo, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, DWORD ibuff, int to_hit, int max_dam, int min_str, int min_mag, int min_dex, int ac)
2426 {
2427 	BOOL done;
2428 	int d;
2429 	int i, j, l;
2430 	int xx, yy;
2431 	int xp, yp;
2432 
2433 	if (numitems >= MAXITEMS)
2434 		return -1;
2435 
2436 	d = GetDirection(plr[pnum]._px, plr[pnum]._py, x, y);
2437 	xx = x - plr[pnum]._px;
2438 	yy = y - plr[pnum]._py;
2439 	if (abs(xx) > 1 || abs(yy) > 1) {
2440 		x = plr[pnum]._px + offset_x[d];
2441 		y = plr[pnum]._py + offset_y[d];
2442 	}
2443 	if (!CanPut(x, y)) {
2444 		d = (d - 1) & 7;
2445 		x = plr[pnum]._px + offset_x[d];
2446 		y = plr[pnum]._py + offset_y[d];
2447 		if (!CanPut(x, y)) {
2448 			d = (d + 2) & 7;
2449 			x = plr[pnum]._px + offset_x[d];
2450 			y = plr[pnum]._py + offset_y[d];
2451 			if (!CanPut(x, y)) {
2452 				done = FALSE;
2453 				for (l = 1; l < 50 && !done; l++) {
2454 					for (j = -l; j <= l && !done; j++) {
2455 						yp = j + plr[pnum]._py;
2456 						for (i = -l; i <= l && !done; i++) {
2457 							xp = i + plr[pnum]._px;
2458 							if (CanPut(xp, yp)) {
2459 								done = TRUE;
2460 								x = xp;
2461 								y = yp;
2462 							}
2463 						}
2464 					}
2465 				}
2466 				if (!done)
2467 					return -1;
2468 			}
2469 		}
2470 	}
2471 
2472 	CanPut(x, y);
2473 
2474 	int ii = AllocateItem();
2475 
2476 	dItem[x][y] = ii + 1;
2477 
2478 	if (idx == IDI_EAR) {
2479 		RecreateEar(ii, icreateinfo, iseed, Id, dur, mdur, ch, mch, ivalue, ibuff);
2480 	} else {
2481 		RecreateItem(ii, idx, icreateinfo, iseed, ivalue, (ibuff & CF_HELLFIRE) != 0);
2482 		if (Id)
2483 			item[ii]._iIdentified = TRUE;
2484 		item[ii]._iDurability = dur;
2485 		item[ii]._iMaxDur = mdur;
2486 		item[ii]._iCharges = ch;
2487 		item[ii]._iMaxCharges = mch;
2488 		item[ii]._iPLToHit = to_hit;
2489 		item[ii]._iMaxDam = max_dam;
2490 		item[ii]._iMinStr = min_str;
2491 		item[ii]._iMinMag = min_mag;
2492 		item[ii]._iMinDex = min_dex;
2493 		item[ii]._iAC = ac;
2494 		item[ii].dwBuff = ibuff;
2495 	}
2496 
2497 	item[ii]._ix = x;
2498 	item[ii]._iy = y;
2499 	RespawnItem(ii, TRUE);
2500 
2501 	if (currlevel == 21 && x == CornerStone.x && y == CornerStone.y) {
2502 		CornerStone.item = item[ii];
2503 		InitQTextMsg(TEXT_CORNSTN);
2504 		quests[Q_CORNSTN]._qlog = 0;
2505 		quests[Q_CORNSTN]._qactive = QUEST_DONE;
2506 	}
2507 	return ii;
2508 }
2509 
CheckInvHLight()2510 char CheckInvHLight()
2511 {
2512 	int r, ii, nGold;
2513 	ItemStruct *pi;
2514 	PlayerStruct *p;
2515 	char rv;
2516 
2517 	for (r = 0; (DWORD)r < NUM_XY_SLOTS; r++) {
2518 		int xo = RIGHT_PANEL;
2519 		int yo = 0;
2520 		if (r >= SLOTXY_BELT_FIRST) {
2521 			xo = PANEL_LEFT;
2522 			yo = PANEL_TOP;
2523 		}
2524 
2525 		if (MouseX >= InvRect[r].X + xo
2526 		    && MouseX < InvRect[r].X + xo + (INV_SLOT_SIZE_PX + 1)
2527 		    && MouseY >= InvRect[r].Y + yo - (INV_SLOT_SIZE_PX + 1)
2528 		    && MouseY < InvRect[r].Y + yo) {
2529 			break;
2530 		}
2531 	}
2532 
2533 	if ((DWORD)r >= NUM_XY_SLOTS)
2534 		return -1;
2535 
2536 	rv = -1;
2537 	infoclr = COL_WHITE;
2538 	pi = NULL;
2539 	p = &plr[myplr];
2540 	ClearPanel();
2541 	if (r >= SLOTXY_HEAD_FIRST && r <= SLOTXY_HEAD_LAST) {
2542 		rv = INVLOC_HEAD;
2543 		pi = &p->InvBody[rv];
2544 	} else if (r == SLOTXY_RING_LEFT) {
2545 		rv = INVLOC_RING_LEFT;
2546 		pi = &p->InvBody[rv];
2547 	} else if (r == SLOTXY_RING_RIGHT) {
2548 		rv = INVLOC_RING_RIGHT;
2549 		pi = &p->InvBody[rv];
2550 	} else if (r == SLOTXY_AMULET) {
2551 		rv = INVLOC_AMULET;
2552 		pi = &p->InvBody[rv];
2553 	} else if (r >= SLOTXY_HAND_LEFT_FIRST && r <= SLOTXY_HAND_LEFT_LAST) {
2554 		rv = INVLOC_HAND_LEFT;
2555 		pi = &p->InvBody[rv];
2556 	} else if (r >= SLOTXY_HAND_RIGHT_FIRST && r <= SLOTXY_HAND_RIGHT_LAST) {
2557 		pi = &p->InvBody[INVLOC_HAND_LEFT];
2558 		if (pi->isEmpty() || pi->_iLoc != ILOC_TWOHAND
2559 		    || (p->_pClass == PC_BARBARIAN && (p->InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SWORD || p->InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_MACE))) {
2560 			rv = INVLOC_HAND_RIGHT;
2561 			pi = &p->InvBody[rv];
2562 		} else {
2563 			rv = INVLOC_HAND_LEFT;
2564 		}
2565 	} else if (r >= SLOTXY_CHEST_FIRST && r <= SLOTXY_CHEST_LAST) {
2566 		rv = INVLOC_CHEST;
2567 		pi = &p->InvBody[rv];
2568 	} else if (r >= SLOTXY_INV_FIRST && r <= SLOTXY_INV_LAST) {
2569 		r = abs(p->InvGrid[r - SLOTXY_INV_FIRST]);
2570 		if (r == 0)
2571 			return -1;
2572 		ii = r - 1;
2573 		rv = ii + INVITEM_INV_FIRST;
2574 		pi = &p->InvList[ii];
2575 	} else if (r >= SLOTXY_BELT_FIRST) {
2576 		r -= SLOTXY_BELT_FIRST;
2577 		drawsbarflag = TRUE;
2578 		pi = &p->SpdList[r];
2579 		if (pi->isEmpty())
2580 			return -1;
2581 		rv = r + INVITEM_BELT_FIRST;
2582 	}
2583 
2584 	if (pi->isEmpty())
2585 		return -1;
2586 
2587 	if (pi->_itype == ITYPE_GOLD) {
2588 		nGold = pi->_ivalue;
2589 		sprintf(infostr, "%i gold %s", nGold, get_pieces_str(nGold));
2590 	} else {
2591 		if (pi->_iMagical == ITEM_QUALITY_MAGIC) {
2592 			infoclr = COL_BLUE;
2593 		} else if (pi->_iMagical == ITEM_QUALITY_UNIQUE) {
2594 			infoclr = COL_GOLD;
2595 		}
2596 		strcpy(infostr, pi->_iName);
2597 		if (pi->_iIdentified) {
2598 			strcpy(infostr, pi->_iIName);
2599 			PrintItemDetails(pi);
2600 		} else {
2601 			PrintItemDur(pi);
2602 		}
2603 	}
2604 
2605 	return rv;
2606 }
2607 
RemoveScroll(int pnum)2608 void RemoveScroll(int pnum)
2609 {
2610 	int i;
2611 
2612 	for (i = 0; i < plr[pnum]._pNumInv; i++) {
2613 		if (!plr[pnum].InvList[i].isEmpty()
2614 		    && (plr[pnum].InvList[i]._iMiscId == IMISC_SCROLL || plr[pnum].InvList[i]._iMiscId == IMISC_SCROLLT)
2615 		    && plr[pnum].InvList[i]._iSpell == plr[pnum]._pRSpell) {
2616 			RemoveInvItem(pnum, i);
2617 			CalcPlrScrolls(pnum);
2618 			return;
2619 		}
2620 	}
2621 	for (i = 0; i < MAXBELTITEMS; i++) {
2622 		if (!plr[pnum].SpdList[i].isEmpty()
2623 		    && (plr[pnum].SpdList[i]._iMiscId == IMISC_SCROLL || plr[pnum].SpdList[i]._iMiscId == IMISC_SCROLLT)
2624 		    && plr[pnum].SpdList[i]._iSpell == plr[pnum]._pSpell) {
2625 			RemoveSpdBarItem(pnum, i);
2626 			CalcPlrScrolls(pnum);
2627 			return;
2628 		}
2629 	}
2630 }
2631 
UseScroll()2632 BOOL UseScroll()
2633 {
2634 	int i;
2635 
2636 	if (pcurs != CURSOR_HAND)
2637 		return FALSE;
2638 	if (leveltype == DTYPE_TOWN && !spelldata[plr[myplr]._pRSpell].sTownSpell)
2639 		return FALSE;
2640 
2641 	for (i = 0; i < plr[myplr]._pNumInv; i++) {
2642 		if (!plr[myplr].InvList[i].isEmpty()
2643 		    && (plr[myplr].InvList[i]._iMiscId == IMISC_SCROLL || plr[myplr].InvList[i]._iMiscId == IMISC_SCROLLT)
2644 		    && plr[myplr].InvList[i]._iSpell == plr[myplr]._pRSpell) {
2645 			return TRUE;
2646 		}
2647 	}
2648 	for (i = 0; i < MAXBELTITEMS; i++) {
2649 		if (!plr[myplr].SpdList[i].isEmpty()
2650 		    && (plr[myplr].SpdList[i]._iMiscId == IMISC_SCROLL || plr[myplr].SpdList[i]._iMiscId == IMISC_SCROLLT)
2651 		    && plr[myplr].SpdList[i]._iSpell == plr[myplr]._pRSpell) {
2652 			return TRUE;
2653 		}
2654 	}
2655 
2656 	return FALSE;
2657 }
2658 
UseStaffCharge(int pnum)2659 void UseStaffCharge(int pnum)
2660 {
2661 	if (!plr[pnum].InvBody[INVLOC_HAND_LEFT].isEmpty()
2662 	    && (plr[pnum].InvBody[INVLOC_HAND_LEFT]._iMiscId == IMISC_STAFF
2663 	        || plr[myplr].InvBody[INVLOC_HAND_LEFT]._iMiscId == IMISC_UNIQUE // BUGFIX: myplr->pnum
2664 	        )
2665 	    && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iSpell == plr[pnum]._pRSpell
2666 	    && plr[pnum].InvBody[INVLOC_HAND_LEFT]._iCharges > 0) {
2667 		plr[pnum].InvBody[INVLOC_HAND_LEFT]._iCharges--;
2668 		CalcPlrStaff(pnum);
2669 	}
2670 }
2671 
UseStaff()2672 BOOL UseStaff()
2673 {
2674 	if (pcurs == CURSOR_HAND) {
2675 		if (!plr[myplr].InvBody[INVLOC_HAND_LEFT].isEmpty()
2676 		    && (plr[myplr].InvBody[INVLOC_HAND_LEFT]._iMiscId == IMISC_STAFF || plr[myplr].InvBody[INVLOC_HAND_LEFT]._iMiscId == IMISC_UNIQUE)
2677 		    && plr[myplr].InvBody[INVLOC_HAND_LEFT]._iSpell == plr[myplr]._pRSpell
2678 		    && plr[myplr].InvBody[INVLOC_HAND_LEFT]._iCharges > 0) {
2679 			return TRUE;
2680 		}
2681 	}
2682 
2683 	return FALSE;
2684 }
2685 
StartGoldDrop()2686 void StartGoldDrop()
2687 {
2688 	initialDropGoldIndex = pcursinvitem;
2689 	if (pcursinvitem <= INVITEM_INV_LAST)
2690 		initialDropGoldValue = plr[myplr].InvList[pcursinvitem - INVITEM_INV_FIRST]._ivalue;
2691 	else
2692 		initialDropGoldValue = plr[myplr].SpdList[pcursinvitem - INVITEM_BELT_FIRST]._ivalue;
2693 	dropGoldFlag = TRUE;
2694 	dropGoldValue = 0;
2695 	if (talkflag)
2696 		control_reset_talk();
2697 }
2698 
UseInvItem(int pnum,int cii)2699 BOOL UseInvItem(int pnum, int cii)
2700 {
2701 	int c, idata;
2702 	ItemStruct *Item;
2703 	BOOL speedlist;
2704 
2705 	if (plr[pnum]._pInvincible && plr[pnum]._pHitPoints == 0 && pnum == myplr)
2706 		return TRUE;
2707 	if (pcurs != CURSOR_HAND)
2708 		return TRUE;
2709 	if (stextflag != STORE_NONE)
2710 		return TRUE;
2711 	if (cii < INVITEM_INV_FIRST)
2712 		return FALSE;
2713 
2714 	if (cii <= INVITEM_INV_LAST) {
2715 		c = cii - INVITEM_INV_FIRST;
2716 		Item = &plr[pnum].InvList[c];
2717 		speedlist = FALSE;
2718 	} else {
2719 		if (talkflag)
2720 			return TRUE;
2721 		c = cii - INVITEM_BELT_FIRST;
2722 		Item = &plr[pnum].SpdList[c];
2723 		speedlist = TRUE;
2724 	}
2725 
2726 	switch (Item->IDidx) {
2727 	case IDI_MUSHROOM:
2728 		sfxdelay = 10;
2729 		if (plr[pnum]._pClass == PC_WARRIOR) {
2730 			sfxdnum = PS_WARR95;
2731 		} else if (plr[pnum]._pClass == PC_ROGUE) {
2732 			sfxdnum = PS_ROGUE95;
2733 		} else if (plr[pnum]._pClass == PC_SORCERER) {
2734 			sfxdnum = PS_MAGE95;
2735 		} else if (plr[pnum]._pClass == PC_MONK) {
2736 			sfxdnum = PS_MONK95;
2737 		} else if (plr[pnum]._pClass == PC_BARD) {
2738 			sfxdnum = PS_ROGUE95;
2739 		} else if (plr[pnum]._pClass == PC_BARBARIAN) {
2740 			sfxdnum = PS_WARR95;
2741 		}
2742 		return TRUE;
2743 	case IDI_FUNGALTM:
2744 		PlaySFX(IS_IBOOK);
2745 		sfxdelay = 10;
2746 		if (plr[pnum]._pClass == PC_WARRIOR) {
2747 			sfxdnum = PS_WARR29;
2748 		} else if (plr[pnum]._pClass == PC_ROGUE) {
2749 			sfxdnum = PS_ROGUE29;
2750 		} else if (plr[pnum]._pClass == PC_SORCERER) {
2751 			sfxdnum = PS_MAGE29;
2752 		} else if (plr[pnum]._pClass == PC_MONK) {
2753 			sfxdnum = PS_MONK29;
2754 		} else if (plr[pnum]._pClass == PC_BARD) {
2755 			sfxdnum = PS_ROGUE29;
2756 		} else if (plr[pnum]._pClass == PC_BARBARIAN) {
2757 			sfxdnum = PS_WARR29;
2758 		}
2759 		return TRUE;
2760 	}
2761 
2762 	if (!AllItemsList[Item->IDidx].iUsable)
2763 		return FALSE;
2764 
2765 	if (!Item->_iStatFlag) {
2766 		if (plr[pnum]._pClass == PC_WARRIOR) {
2767 			PlaySFX(PS_WARR13);
2768 		} else if (plr[pnum]._pClass == PC_ROGUE) {
2769 			PlaySFX(PS_ROGUE13);
2770 		} else if (plr[pnum]._pClass == PC_SORCERER) {
2771 			PlaySFX(PS_MAGE13);
2772 		} else if (plr[pnum]._pClass == PC_MONK) {
2773 			PlaySFX(PS_MONK13);
2774 		} else if (plr[pnum]._pClass == PC_BARD) {
2775 			PlaySFX(PS_ROGUE13);
2776 		} else if (plr[pnum]._pClass == PC_BARBARIAN) {
2777 			PlaySFX(PS_WARR13);
2778 		}
2779 		return TRUE;
2780 	}
2781 
2782 	if (Item->_iMiscId == IMISC_NONE && Item->_itype == ITYPE_GOLD) {
2783 		StartGoldDrop();
2784 		return TRUE;
2785 	}
2786 
2787 	if (dropGoldFlag) {
2788 		dropGoldFlag = FALSE;
2789 		dropGoldValue = 0;
2790 	}
2791 
2792 	if (Item->_iMiscId == IMISC_SCROLL && currlevel == 0 && !spelldata[Item->_iSpell].sTownSpell) {
2793 		return TRUE;
2794 	}
2795 
2796 	if (Item->_iMiscId == IMISC_SCROLLT && currlevel == 0 && !spelldata[Item->_iSpell].sTownSpell) {
2797 		return TRUE;
2798 	}
2799 
2800 	if (Item->_iMiscId > IMISC_RUNEFIRST && Item->_iMiscId < IMISC_RUNELAST && currlevel == 0) {
2801 		return TRUE;
2802 	}
2803 
2804 	idata = ItemCAnimTbl[Item->_iCurs];
2805 	if (Item->_iMiscId == IMISC_BOOK)
2806 		PlaySFX(IS_RBOOK);
2807 	else if (pnum == myplr)
2808 		PlaySFX(ItemInvSnds[idata]);
2809 
2810 	UseItem(pnum, Item->_iMiscId, Item->_iSpell);
2811 
2812 	if (speedlist) {
2813 		if (plr[pnum].SpdList[c]._iMiscId == IMISC_NOTE) {
2814 			InitQTextMsg(TEXT_BOOK9);
2815 			invflag = FALSE;
2816 			return TRUE;
2817 		}
2818 		RemoveSpdBarItem(pnum, c);
2819 		return TRUE;
2820 	} else {
2821 		if (plr[pnum].InvList[c]._iMiscId == IMISC_MAPOFDOOM)
2822 			return TRUE;
2823 		if (plr[pnum].InvList[c]._iMiscId == IMISC_NOTE) {
2824 			InitQTextMsg(TEXT_BOOK9);
2825 			invflag = FALSE;
2826 			return TRUE;
2827 		}
2828 		RemoveInvItem(pnum, c);
2829 	}
2830 	return TRUE;
2831 }
2832 
DoTelekinesis()2833 void DoTelekinesis()
2834 {
2835 	if (pcursobj != -1)
2836 		NetSendCmdParam1(TRUE, CMD_OPOBJT, pcursobj);
2837 	if (pcursitem != -1)
2838 		NetSendCmdGItem(TRUE, CMD_REQUESTAGITEM, myplr, myplr, pcursitem);
2839 	if (pcursmonst != -1 && !M_Talker(pcursmonst) && monster[pcursmonst].mtalkmsg == 0)
2840 		NetSendCmdParam1(TRUE, CMD_KNOCKBACK, pcursmonst);
2841 	NewCursor(CURSOR_HAND);
2842 }
2843 
CalculateGold(int pnum)2844 int CalculateGold(int pnum)
2845 {
2846 	int i, gold;
2847 
2848 	gold = 0;
2849 	for (i = 0; i < MAXBELTITEMS; i++) {
2850 		if (plr[pnum].SpdList[i]._itype == ITYPE_GOLD) {
2851 			gold += plr[pnum].SpdList[i]._ivalue;
2852 			force_redraw = 255;
2853 		}
2854 	}
2855 	for (i = 0; i < plr[pnum]._pNumInv; i++) {
2856 		if (plr[pnum].InvList[i]._itype == ITYPE_GOLD)
2857 			gold += plr[pnum].InvList[i]._ivalue;
2858 	}
2859 
2860 	return gold;
2861 }
2862 
DropItemBeforeTrig()2863 BOOL DropItemBeforeTrig()
2864 {
2865 	if (TryInvPut()) {
2866 		NetSendCmdPItem(TRUE, CMD_PUTITEM, cursmx, cursmy);
2867 		NewCursor(CURSOR_HAND);
2868 		return TRUE;
2869 	}
2870 
2871 	return FALSE;
2872 }
2873 
2874 DEVILUTION_END_NAMESPACE
2875