1 /**
2  * @file items.cpp
3  *
4  * Implementation of item functionality.
5  */
6 #include <algorithm>
7 #include "all.h"
8 #include "options.h"
9 
10 DEVILUTION_BEGIN_NAMESPACE
11 
12 int itemactive[MAXITEMS];
13 BOOL uitemflag;
14 int itemavail[MAXITEMS];
15 ItemStruct curruitem;
16 ItemGetRecordStruct itemrecord[MAXITEMS];
17 /** Contains the items on ground in the current game. */
18 ItemStruct item[MAXITEMS + 1];
19 BOOL itemhold[3][3];
20 CornerStoneStruct CornerStone;
21 BYTE *itemanims[ITEMTYPES];
22 BOOL UniqueItemFlag[128];
23 int numitems;
24 int gnNumGetRecords;
25 
26 /* data */
27 
28 int OilLevels[] = { 1, 10, 1, 10, 4, 1, 5, 17, 1, 10 };
29 int OilValues[] = { 500, 2500, 500, 2500, 1500, 100, 2500, 15000, 500, 2500 };
30 enum item_misc_id OilMagic[] = {
31 	IMISC_OILACC,
32 	IMISC_OILMAST,
33 	IMISC_OILSHARP,
34 	IMISC_OILDEATH,
35 	IMISC_OILSKILL,
36 	IMISC_OILBSMTH,
37 	IMISC_OILFORT,
38 	IMISC_OILPERM,
39 	IMISC_OILHARD,
40 	IMISC_OILIMP,
41 };
42 char OilNames[10][25] = {
43 	"Oil of Accuracy",
44 	"Oil of Mastery",
45 	"Oil of Sharpness",
46 	"Oil of Death",
47 	"Oil of Skill",
48 	"Blacksmith Oil",
49 	"Oil of Fortitude",
50 	"Oil of Permanence",
51 	"Oil of Hardening",
52 	"Oil of Imperviousness"
53 };
54 int MaxGold = GOLD_MAX_LIMIT;
55 
56 /** Maps from item_cursor_graphic to in-memory item type. */
57 BYTE ItemCAnimTbl[] = {
58 	20, 16, 16, 16, 4, 4, 4, 12, 12, 12,
59 	12, 12, 12, 12, 12, 21, 21, 25, 12, 28,
60 	28, 28, 38, 38, 38, 32, 38, 38, 38, 24,
61 	24, 26, 2, 25, 22, 23, 24, 25, 27, 27,
62 	29, 0, 0, 0, 12, 12, 12, 12, 12, 0,
63 	8, 8, 0, 8, 8, 8, 8, 8, 8, 6,
64 	8, 8, 8, 6, 8, 8, 6, 8, 8, 6,
65 	6, 6, 8, 8, 8, 5, 9, 13, 13, 13,
66 	5, 5, 5, 15, 5, 5, 18, 18, 18, 30,
67 	5, 5, 14, 5, 14, 13, 16, 18, 5, 5,
68 	7, 1, 3, 17, 1, 15, 10, 14, 3, 11,
69 	8, 0, 1, 7, 0, 7, 15, 7, 3, 3,
70 	3, 6, 6, 11, 11, 11, 31, 14, 14, 14,
71 	6, 6, 7, 3, 8, 14, 0, 14, 14, 0,
72 	33, 1, 1, 1, 1, 1, 7, 7, 7, 14,
73 	14, 17, 17, 17, 0, 34, 1, 0, 3, 17,
74 	8, 8, 6, 1, 3, 3, 11, 3, 12, 12,
75 	12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
76 	12, 12, 12, 12, 12, 12, 12, 35, 39, 36,
77 	36, 36, 37, 38, 38, 38, 38, 38, 41, 42,
78 	8, 8, 8, 17, 0, 6, 8, 11, 11, 3,
79 	3, 1, 6, 6, 6, 1, 8, 6, 11, 3,
80 	6, 8, 1, 6, 6, 17, 40, 0, 0
81 };
82 /** Map of item type .cel file names. */
83 const char *const ItemDropNames[] = {
84 	"Armor2",
85 	"Axe",
86 	"FBttle",
87 	"Bow",
88 	"GoldFlip",
89 	"Helmut",
90 	"Mace",
91 	"Shield",
92 	"SwrdFlip",
93 	"Rock",
94 	"Cleaver",
95 	"Staff",
96 	"Ring",
97 	"CrownF",
98 	"LArmor",
99 	"WShield",
100 	"Scroll",
101 	"FPlateAr",
102 	"FBook",
103 	"Food",
104 	"FBttleBB",
105 	"FBttleDY",
106 	"FBttleOR",
107 	"FBttleBR",
108 	"FBttleBL",
109 	"FBttleBY",
110 	"FBttleWH",
111 	"FBttleDB",
112 	"FEar",
113 	"FBrain",
114 	"FMush",
115 	"Innsign",
116 	"Bldstn",
117 	"Fanvil",
118 	"FLazStaf",
119 	"bombs1",
120 	"halfps1",
121 	"wholeps1",
122 	"runes1",
123 	"teddys1",
124 	"cows1",
125 	"donkys1",
126 	"mooses1",
127 };
128 /** Maps of item drop animation length. */
129 BYTE ItemAnimLs[] = {
130 	15,
131 	13,
132 	16,
133 	13,
134 	10,
135 	13,
136 	13,
137 	13,
138 	13,
139 	10,
140 	13,
141 	13,
142 	13,
143 	13,
144 	13,
145 	13,
146 	13,
147 	13,
148 	13,
149 	1,
150 	16,
151 	16,
152 	16,
153 	16,
154 	16,
155 	16,
156 	16,
157 	16,
158 	13,
159 	12,
160 	12,
161 	13,
162 	13,
163 	13,
164 	8,
165 	10,
166 	16,
167 	16,
168 	10,
169 	10,
170 	15,
171 	15,
172 	15,
173 };
174 /** Maps of drop sounds effect of dropping the item on ground. */
175 int ItemDropSnds[] = {
176 	IS_FHARM,
177 	IS_FAXE,
178 	IS_FPOT,
179 	IS_FBOW,
180 	IS_GOLD,
181 	IS_FCAP,
182 	IS_FSWOR,
183 	IS_FSHLD,
184 	IS_FSWOR,
185 	IS_FROCK,
186 	IS_FAXE,
187 	IS_FSTAF,
188 	IS_FRING,
189 	IS_FCAP,
190 	IS_FLARM,
191 	IS_FSHLD,
192 	IS_FSCRL,
193 	IS_FHARM,
194 	IS_FBOOK,
195 	IS_FLARM,
196 	IS_FPOT,
197 	IS_FPOT,
198 	IS_FPOT,
199 	IS_FPOT,
200 	IS_FPOT,
201 	IS_FPOT,
202 	IS_FPOT,
203 	IS_FPOT,
204 	IS_FBODY,
205 	IS_FBODY,
206 	IS_FMUSH,
207 	IS_ISIGN,
208 	IS_FBLST,
209 	IS_FANVL,
210 	IS_FSTAF,
211 	IS_FROCK,
212 	IS_FSCRL,
213 	IS_FSCRL,
214 	IS_FROCK,
215 	IS_FMUSH,
216 	IS_FHARM,
217 	IS_FLARM,
218 	IS_FLARM,
219 };
220 /** Maps of drop sounds effect of placing the item in the inventory. */
221 int ItemInvSnds[] = {
222 	IS_IHARM,
223 	IS_IAXE,
224 	IS_IPOT,
225 	IS_IBOW,
226 	IS_GOLD,
227 	IS_ICAP,
228 	IS_ISWORD,
229 	IS_ISHIEL,
230 	IS_ISWORD,
231 	IS_IROCK,
232 	IS_IAXE,
233 	IS_ISTAF,
234 	IS_IRING,
235 	IS_ICAP,
236 	IS_ILARM,
237 	IS_ISHIEL,
238 	IS_ISCROL,
239 	IS_IHARM,
240 	IS_IBOOK,
241 	IS_IHARM,
242 	IS_IPOT,
243 	IS_IPOT,
244 	IS_IPOT,
245 	IS_IPOT,
246 	IS_IPOT,
247 	IS_IPOT,
248 	IS_IPOT,
249 	IS_IPOT,
250 	IS_IBODY,
251 	IS_IBODY,
252 	IS_IMUSH,
253 	IS_ISIGN,
254 	IS_IBLST,
255 	IS_IANVL,
256 	IS_ISTAF,
257 	IS_IROCK,
258 	IS_ISCROL,
259 	IS_ISCROL,
260 	IS_IROCK,
261 	IS_IMUSH,
262 	IS_IHARM,
263 	IS_ILARM,
264 	IS_ILARM,
265 };
266 /** Specifies the current Y-coordinate used for validation of items on ground. */
267 int idoppely = 16;
268 /** Maps from Griswold premium item number to a quality level delta as added to the base quality level. */
269 int premiumlvladd[] = {
270 	// clang-format off
271 	-1,
272 	-1,
273 	 0,
274 	 0,
275 	 1,
276 	 2,
277 	// clang-format on
278 };
279 /** Maps from Griswold premium item number to a quality level delta as added to the base quality level. */
280 int premiumLvlAddHellfire[] = {
281 	// clang-format off
282 	-1,
283 	-1,
284 	-1,
285 	 0,
286 	 0,
287 	 0,
288 	 0,
289 	 1,
290 	 1,
291 	 1,
292 	 1,
293 	 2,
294 	 2,
295 	 3,
296 	 3,
297 	// clang-format on
298 };
299 
IsItemAvailable(int i)300 bool IsItemAvailable(int i)
301 {
302 	if (gbIsHellfire)
303 		return true;
304 	return (
305 	           i != IDI_MAPOFDOOM                   // Cathedral Map
306 	           && i != IDI_LGTFORGE                 // Bovine Plate
307 	           && (i < IDI_OIL || i > IDI_GREYSUIT) // Hellfire exclusive items
308 	           && (i < 83 || i > 86)                // Oils
309 	           && i != 92                           // Scroll of Search
310 	           && (i < 161 || i > 165)              // Runes
311 	           && i != IDI_SORCERER                 // Short Staff of Mana
312 	           )
313 	    || (
314 	        // Bard items are technically Hellfire-exclusive
315 	        // but are just normal items with adjusted stats.
316 	        sgOptions.Gameplay.bTestBard && (i == IDI_BARDSWORD || i == IDI_BARDDAGGER));
317 }
318 
IsUniqueAvailable(int i)319 bool IsUniqueAvailable(int i)
320 {
321 	return gbIsHellfire || i <= 89;
322 }
323 
IsPrefixValidForItemType(int i,int flgs)324 static bool IsPrefixValidForItemType(int i, int flgs)
325 {
326 	int PLIType = PL_Prefix[i].PLIType;
327 
328 	if (!gbIsHellfire) {
329 		if (i > 82)
330 			return false;
331 
332 		if (i >= 12 && i <= 20)
333 			PLIType &= ~PLT_STAFF;
334 	}
335 
336 	return (flgs & PLIType) != 0;
337 }
338 
IsSuffixValidForItemType(int i,int flgs)339 static bool IsSuffixValidForItemType(int i, int flgs)
340 {
341 	int PLIType = PL_Suffix[i].PLIType;
342 
343 	if (!gbIsHellfire) {
344 		if (i > 94)
345 			return false;
346 
347 		if ((i >= 0 && i <= 1)
348 		    || (i >= 14 && i <= 15)
349 		    || (i >= 21 && i <= 22)
350 		    || (i >= 34 && i <= 36)
351 		    || (i >= 41 && i <= 44)
352 		    || (i >= 60 && i <= 63))
353 			PLIType &= ~PLT_STAFF;
354 	}
355 
356 	return (flgs & PLIType) != 0;
357 }
358 
get_ring_max_value(int i)359 int get_ring_max_value(int i)
360 {
361 	int j, res;
362 
363 	res = 0;
364 	for (j = 0; j < NUM_INVLOC; j++) {
365 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_RING && res < plr[i].InvBody[j]._iIvalue)
366 			res = plr[i].InvBody[j]._iIvalue;
367 	}
368 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
369 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_RING && res < plr[i].InvList[j]._iIvalue)
370 			res = plr[i].InvList[j]._iIvalue;
371 	}
372 
373 	return res;
374 }
375 
get_bow_max_value(int i)376 int get_bow_max_value(int i)
377 {
378 	int j, res;
379 
380 	res = 0;
381 	for (j = 0; j < NUM_INVLOC; j++) {
382 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_BOW && res < plr[i].InvBody[j]._iIvalue)
383 			res = plr[i].InvBody[j]._iIvalue;
384 	}
385 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
386 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_BOW && res < plr[i].InvList[j]._iIvalue)
387 			res = plr[i].InvList[j]._iIvalue;
388 	}
389 
390 	return res;
391 }
392 
get_staff_max_value(int i)393 int get_staff_max_value(int i)
394 {
395 	int j, res;
396 
397 	res = 0;
398 	for (j = 0; j < NUM_INVLOC; j++) {
399 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_STAFF && res < plr[i].InvBody[j]._iIvalue)
400 			res = plr[i].InvBody[j]._iIvalue;
401 	}
402 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
403 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_STAFF && res < plr[i].InvList[j]._iIvalue)
404 			res = plr[i].InvList[j]._iIvalue;
405 	}
406 
407 	return res;
408 }
409 
get_sword_max_value(int i)410 int get_sword_max_value(int i)
411 {
412 	int j, res;
413 
414 	res = 0;
415 	for (j = 0; j < NUM_INVLOC; j++) {
416 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_SWORD && res < plr[i].InvBody[j]._iIvalue)
417 			res = plr[i].InvBody[j]._iIvalue;
418 	}
419 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
420 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_SWORD && res < plr[i].InvList[j]._iIvalue)
421 			res = plr[i].InvList[j]._iIvalue;
422 	}
423 
424 	return res;
425 }
426 
get_helm_max_value(int i)427 int get_helm_max_value(int i)
428 {
429 	int j, res;
430 
431 	res = 0;
432 	for (j = 0; j < NUM_INVLOC; j++) {
433 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_HELM && res < plr[i].InvBody[j]._iIvalue)
434 			res = plr[i].InvBody[j]._iIvalue;
435 	}
436 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
437 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_HELM && res < plr[i].InvList[j]._iIvalue)
438 			res = plr[i].InvList[j]._iIvalue;
439 	}
440 
441 	return res;
442 }
443 
get_shield_max_value(int i)444 int get_shield_max_value(int i)
445 {
446 	int j, res;
447 
448 	res = 0;
449 	for (j = 0; j < NUM_INVLOC; j++) {
450 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_SHIELD && res < plr[i].InvBody[j]._iIvalue)
451 			res = plr[i].InvBody[j]._iIvalue;
452 	}
453 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
454 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_SHIELD && res < plr[i].InvList[j]._iIvalue)
455 			res = plr[i].InvList[j]._iIvalue;
456 	}
457 
458 	return res;
459 }
460 
get_armor_max_value(int i)461 int get_armor_max_value(int i)
462 {
463 	int j, res;
464 
465 	res = 0;
466 	for (j = 0; j < NUM_INVLOC; j++) {
467 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE
468 		    && (plr[i].InvBody[j]._itype == ITYPE_LARMOR || plr[i].InvBody[j]._itype == ITYPE_MARMOR || plr[i].InvBody[j]._itype == ITYPE_HARMOR)
469 		    && res < plr[i].InvBody[j]._iIvalue)
470 			res = plr[i].InvBody[j]._iIvalue;
471 	}
472 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
473 		if (plr[i].InvList[j]._iClass != ICLASS_NONE
474 		    && (plr[i].InvList[j]._itype == ITYPE_LARMOR || plr[i].InvList[j]._itype == ITYPE_MARMOR || plr[i].InvList[j]._itype == ITYPE_HARMOR)
475 		    && res < plr[i].InvList[j]._iIvalue)
476 			res = plr[i].InvList[j]._iIvalue;
477 	}
478 
479 	return res;
480 }
481 
get_mace_max_value(int i)482 int get_mace_max_value(int i)
483 {
484 	int j, res;
485 
486 	res = 0;
487 	for (j = 0; j < NUM_INVLOC; j++) {
488 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_MACE && res < plr[i].InvBody[j]._iIvalue)
489 			res = plr[i].InvBody[j]._iIvalue;
490 	}
491 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
492 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_MACE && res < plr[i].InvList[j]._iIvalue)
493 			res = plr[i].InvList[j]._iIvalue;
494 	}
495 
496 	return res;
497 }
498 
get_amulet_max_value(int i)499 int get_amulet_max_value(int i)
500 {
501 	int j, res;
502 
503 	res = 0;
504 	for (j = 0; j < NUM_INVLOC; j++) {
505 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_AMULET && res < plr[i].InvBody[j]._iIvalue)
506 			res = plr[i].InvBody[j]._iIvalue;
507 	}
508 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
509 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_AMULET && res < plr[i].InvList[j]._iIvalue)
510 			res = plr[i].InvList[j]._iIvalue;
511 	}
512 
513 	return res;
514 }
515 
get_axe_max_value(int i)516 int get_axe_max_value(int i)
517 {
518 	int j, res;
519 
520 	res = 0;
521 	for (j = 0; j < NUM_INVLOC; j++) {
522 		if (plr[i].InvBody[j]._iClass != ICLASS_NONE && plr[i].InvBody[j]._itype == ITYPE_AXE && res < plr[i].InvBody[j]._iIvalue)
523 			res = plr[i].InvBody[j]._iIvalue;
524 	}
525 	for (j = 0; j < NUM_INV_GRID_ELEM; j++) {
526 		if (plr[i].InvList[j]._iClass != ICLASS_NONE && plr[i].InvList[j]._itype == ITYPE_AXE && res < plr[i].InvList[j]._iIvalue)
527 			res = plr[i].InvList[j]._iIvalue;
528 	}
529 
530 	return res;
531 }
532 
items_get_currlevel()533 int items_get_currlevel()
534 {
535 	int lvl;
536 
537 	lvl = currlevel;
538 	if (currlevel >= 17 && currlevel <= 20)
539 		lvl = currlevel - 8;
540 	if (currlevel >= 21 && currlevel <= 24)
541 		lvl = currlevel - 7;
542 
543 	return lvl;
544 }
545 
InitItemGFX()546 void InitItemGFX()
547 {
548 	char arglist[64];
549 
550 	int itemTypes = gbIsHellfire ? ITEMTYPES : 35;
551 	for (int i = 0; i < itemTypes; i++) {
552 		sprintf(arglist, "Items\\%s.CEL", ItemDropNames[i]);
553 		itemanims[i] = LoadFileInMem(arglist, NULL);
554 	}
555 	memset(UniqueItemFlag, 0, sizeof(UniqueItemFlag));
556 }
557 
ItemPlace(int xp,int yp)558 BOOL ItemPlace(int xp, int yp)
559 {
560 	if (dMonster[xp][yp] != 0)
561 		return FALSE;
562 	if (dPlayer[xp][yp] != 0)
563 		return FALSE;
564 	if (dItem[xp][yp] != 0)
565 		return FALSE;
566 	if (dObject[xp][yp] != 0)
567 		return FALSE;
568 	if (dFlags[xp][yp] & BFLAG_POPULATED)
569 		return FALSE;
570 	if (nSolidTable[dPiece[xp][yp]])
571 		return FALSE;
572 
573 	return TRUE;
574 }
575 
AddInitItems()576 void AddInitItems()
577 {
578 	int x, y, j, rnd;
579 
580 	int curlv = items_get_currlevel();
581 	rnd = random_(11, 3) + 3;
582 	for (j = 0; j < rnd; j++) {
583 		int ii = AllocateItem();
584 
585 		x = random_(12, 80) + 16;
586 		y = random_(12, 80) + 16;
587 		while (!ItemPlace(x, y)) {
588 			x = random_(12, 80) + 16;
589 			y = random_(12, 80) + 16;
590 		}
591 		item[ii]._ix = x;
592 		item[ii]._iy = y;
593 
594 		dItem[x][y] = ii + 1;
595 
596 		item[ii]._iSeed = AdvanceRndSeed();
597 		SetRndSeed(item[ii]._iSeed);
598 
599 		if (random_(12, 2) != 0)
600 			GetItemAttrs(ii, IDI_HEAL, curlv);
601 		else
602 			GetItemAttrs(ii, IDI_MANA, curlv);
603 
604 		item[ii]._iCreateInfo = curlv | CF_PREGEN;
605 		SetupItem(ii);
606 		item[ii]._iAnimFrame = item[ii]._iAnimLen;
607 		item[ii]._iAnimFlag = FALSE;
608 		item[ii]._iSelFlag = 1;
609 		DeltaAddItem(ii);
610 	}
611 }
612 
items_42390F()613 static void items_42390F()
614 {
615 	int x, y, id;
616 
617 	x = random_(12, 80) + 16;
618 	y = random_(12, 80) + 16;
619 	while (!ItemPlace(x, y)) {
620 		x = random_(12, 80) + 16;
621 		y = random_(12, 80) + 16;
622 	}
623 	switch (currlevel) {
624 	case 22:
625 		id = IDI_NOTE2;
626 		break;
627 	case 23:
628 		id = IDI_NOTE3;
629 		break;
630 	default:
631 		id = IDI_NOTE1;
632 		break;
633 	}
634 	SpawnQuestItem(id, x, y, 0, 1);
635 }
636 
InitItems()637 void InitItems()
638 {
639 	int i;
640 
641 	memset(&item[0], 0, sizeof(*item));
642 	GetItemAttrs(0, IDI_GOLD, 1);
643 	golditem = item[0];
644 	golditem._iStatFlag = TRUE;
645 	numitems = 0;
646 
647 	for (i = 0; i < MAXITEMS; i++) {
648 		item[i]._itype = ITYPE_NONE;
649 		item[i]._ix = 0;
650 		item[i]._iy = 0;
651 		item[i]._iAnimFlag = FALSE;
652 		item[i]._iSelFlag = 0;
653 		item[i]._iIdentified = FALSE;
654 		item[i]._iPostDraw = FALSE;
655 	}
656 
657 	for (i = 0; i < MAXITEMS; i++) {
658 		itemavail[i] = i;
659 		itemactive[i] = 0;
660 	}
661 
662 	if (!setlevel) {
663 		AdvanceRndSeed(); /* unused */
664 		if (QuestStatus(Q_ROCK))
665 			SpawnRock();
666 		if (QuestStatus(Q_ANVIL))
667 			SpawnQuestItem(IDI_ANVIL, 2 * setpc_x + 27, 2 * setpc_y + 27, 0, 1);
668 		if (gbCowQuest && currlevel == 20)
669 			SpawnQuestItem(IDI_BROWNSUIT, 25, 25, 3, 1);
670 		if (gbCowQuest && currlevel == 19)
671 			SpawnQuestItem(IDI_GREYSUIT, 25, 25, 3, 1);
672 		if (currlevel > 0 && currlevel < 16)
673 			AddInitItems();
674 		if (currlevel >= 21 && currlevel <= 23)
675 			items_42390F();
676 	}
677 
678 	uitemflag = FALSE;
679 }
680 
CalcPlrItemVals(int p,BOOL Loadgfx)681 void CalcPlrItemVals(int p, BOOL Loadgfx)
682 {
683 	int pvid, d;
684 
685 	int mind = 0; // min damage
686 	int maxd = 0; // max damage
687 	int tac = 0;  // accuracy
688 
689 	int g;
690 	int i;
691 	int mi;
692 
693 	int bdam = 0;   // bonus damage
694 	int btohit = 0; // bonus chance to hit
695 	int bac = 0;    // bonus accuracy
696 
697 	int iflgs = ISPL_NONE; // item_special_effect flags
698 
699 	int pDamAcFlags = 0;
700 
701 	int sadd = 0; // added strength
702 	int madd = 0; // added magic
703 	int dadd = 0; // added dexterity
704 	int vadd = 0; // added vitality
705 
706 	Uint64 spl = 0; // bitarray for all enabled/active spells
707 
708 	int fr = 0; // fire resistance
709 	int lr = 0; // lightning resistance
710 	int mr = 0; // magic resistance
711 
712 	int dmod = 0; // bonus damage mod?
713 	int ghit = 0; // increased damage from enemies
714 
715 	int lrad = 10; // light radius
716 
717 	int ihp = 0;   // increased HP
718 	int imana = 0; // increased mana
719 
720 	int spllvladd = 0; // increased spell level
721 	int enac = 0;      // enhanced accuracy
722 
723 	int fmin = 0; // minimum fire damage
724 	int fmax = 0; // maximum fire damage
725 	int lmin = 0; // minimum lightning damage
726 	int lmax = 0; // maximum lightning damage
727 
728 	for (i = 0; i < NUM_INVLOC; i++) {
729 		ItemStruct *itm = &plr[p].InvBody[i];
730 		if (!itm->isEmpty() && itm->_iStatFlag) {
731 
732 			mind += itm->_iMinDam;
733 			maxd += itm->_iMaxDam;
734 			tac += itm->_iAC;
735 
736 			if (itm->_iSpell != SPL_NULL) {
737 				spl |= GetSpellBitmask(itm->_iSpell);
738 			}
739 
740 			if (itm->_iMagical == ITEM_QUALITY_NORMAL || itm->_iIdentified) {
741 				bdam += itm->_iPLDam;
742 				btohit += itm->_iPLToHit;
743 				if (itm->_iPLAC) {
744 					int tmpac = itm->_iAC;
745 					tmpac *= itm->_iPLAC;
746 					tmpac /= 100;
747 					if (tmpac == 0)
748 						tmpac = 1;
749 					bac += tmpac;
750 				}
751 				iflgs |= itm->_iFlags;
752 				pDamAcFlags |= itm->_iDamAcFlags;
753 				sadd += itm->_iPLStr;
754 				madd += itm->_iPLMag;
755 				dadd += itm->_iPLDex;
756 				vadd += itm->_iPLVit;
757 				fr += itm->_iPLFR;
758 				lr += itm->_iPLLR;
759 				mr += itm->_iPLMR;
760 				dmod += itm->_iPLDamMod;
761 				ghit += itm->_iPLGetHit;
762 				lrad += itm->_iPLLight;
763 				ihp += itm->_iPLHP;
764 				imana += itm->_iPLMana;
765 				spllvladd += itm->_iSplLvlAdd;
766 				enac += itm->_iPLEnAc;
767 				fmin += itm->_iFMinDam;
768 				fmax += itm->_iFMaxDam;
769 				lmin += itm->_iLMinDam;
770 				lmax += itm->_iLMaxDam;
771 			}
772 		}
773 	}
774 
775 	if (mind == 0 && maxd == 0) {
776 		mind = 1;
777 		maxd = 1;
778 
779 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) {
780 			maxd = 3;
781 		}
782 
783 		if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) {
784 			maxd = 3;
785 		}
786 
787 		if (plr[p]._pClass == PC_MONK) {
788 			mind = std::max(mind, plr[p]._pLevel >> 1);
789 			maxd = std::max(maxd, (int)plr[p]._pLevel);
790 		}
791 	}
792 
793 	if ((plr[p]._pSpellFlags & 2) == 2) {
794 		sadd += 2 * plr[p]._pLevel;
795 		dadd += plr[p]._pLevel + plr[p]._pLevel / 2;
796 		vadd += 2 * plr[p]._pLevel;
797 	}
798 	if ((plr[p]._pSpellFlags & 4) == 4) {
799 		sadd -= 2 * plr[p]._pLevel;
800 		dadd -= plr[p]._pLevel + plr[p]._pLevel / 2;
801 		vadd -= 2 * plr[p]._pLevel;
802 	}
803 
804 	plr[p]._pIMinDam = mind;
805 	plr[p]._pIMaxDam = maxd;
806 	plr[p]._pIAC = tac;
807 	plr[p]._pIBonusDam = bdam;
808 	plr[p]._pIBonusToHit = btohit;
809 	plr[p]._pIBonusAC = bac;
810 	plr[p]._pIFlags = iflgs;
811 	plr[p].pDamAcFlags = pDamAcFlags;
812 	plr[p]._pIBonusDamMod = dmod;
813 	plr[p]._pIGetHit = ghit;
814 
815 	if (lrad < 2) {
816 		lrad = 2;
817 	}
818 	if (lrad > 15) {
819 		lrad = 15;
820 	}
821 
822 	if (plr[p]._pLightRad != lrad && p == myplr) {
823 		ChangeLightRadius(plr[p]._plid, lrad);
824 		ChangeVisionRadius(plr[p]._pvid, lrad);
825 		plr[p]._pLightRad = lrad;
826 	}
827 
828 	plr[p]._pStrength = sadd + plr[p]._pBaseStr;
829 	if (plr[p]._pStrength < 0) {
830 		plr[p]._pStrength = 0;
831 	}
832 
833 	plr[p]._pMagic = madd + plr[p]._pBaseMag;
834 	if (plr[p]._pMagic < 0) {
835 		plr[p]._pMagic = 0;
836 	}
837 
838 	plr[p]._pDexterity = dadd + plr[p]._pBaseDex;
839 	if (plr[p]._pDexterity < 0) {
840 		plr[p]._pDexterity = 0;
841 	}
842 
843 	plr[p]._pVitality = vadd + plr[p]._pBaseVit;
844 	if (plr[p]._pVitality < 0) {
845 		plr[p]._pVitality = 0;
846 	}
847 
848 	if (plr[p]._pClass == PC_ROGUE) {
849 		plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 200;
850 	} else if (plr[p]._pClass == PC_MONK) {
851 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_STAFF) {
852 			if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype != ITYPE_STAFF && (!plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty() || !plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty())) {
853 				plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 300;
854 			} else {
855 				plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 150;
856 			}
857 		} else {
858 			plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 150;
859 		}
860 	} else if (plr[p]._pClass == PC_BARD) {
861 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SWORD || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SWORD)
862 			plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 150;
863 		else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_BOW || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_BOW) {
864 			plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 250;
865 		} else {
866 			plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 100;
867 		}
868 	} else if (plr[p]._pClass == PC_BARBARIAN) {
869 
870 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_AXE || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_AXE) {
871 			plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 75;
872 		} else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_MACE || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_MACE) {
873 			plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 75;
874 		} else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_BOW || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_BOW) {
875 			plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 300;
876 		} else {
877 			plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 100;
878 		}
879 
880 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD) {
881 			if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD)
882 				plr[p]._pIAC -= plr[p].InvBody[INVLOC_HAND_LEFT]._iAC / 2;
883 			else if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD)
884 				plr[p]._pIAC -= plr[p].InvBody[INVLOC_HAND_RIGHT]._iAC / 2;
885 		} else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_RIGHT]._itype != ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_BOW && plr[p].InvBody[INVLOC_HAND_RIGHT]._itype != ITYPE_BOW) {
886 			plr[p]._pDamageMod += plr[p]._pLevel * plr[p]._pVitality / 100;
887 		}
888 		plr[p]._pIAC += plr[p]._pLevel / 4;
889 	} else {
890 		plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 100;
891 	}
892 
893 	plr[p]._pISpells = spl;
894 
895 	EnsureValidReadiedSpell(plr[p]);
896 
897 	plr[p]._pISplLvlAdd = spllvladd;
898 	plr[p]._pIEnAc = enac;
899 
900 	if (plr[p]._pClass == PC_BARBARIAN) {
901 		mr += plr[p]._pLevel;
902 		fr += plr[p]._pLevel;
903 		lr += plr[p]._pLevel;
904 	}
905 
906 	if ((plr[p]._pSpellFlags & 4) == 4) {
907 		mr -= plr[p]._pLevel;
908 		fr -= plr[p]._pLevel;
909 		lr -= plr[p]._pLevel;
910 	}
911 
912 	if (iflgs & ISPL_ALLRESZERO) {
913 		// reset resistances to zero if the respective special effect is active
914 		mr = 0;
915 		fr = 0;
916 		lr = 0;
917 	}
918 
919 	if (mr > MAXRESIST)
920 		mr = MAXRESIST;
921 	else if (mr < 0)
922 		mr = 0;
923 	plr[p]._pMagResist = mr;
924 
925 	if (fr > MAXRESIST)
926 		fr = MAXRESIST;
927 	else if (fr < 0)
928 		fr = 0;
929 	plr[p]._pFireResist = fr;
930 
931 	if (lr > MAXRESIST)
932 		lr = MAXRESIST;
933 	else if (lr < 0)
934 		lr = 0;
935 	plr[p]._pLghtResist = lr;
936 
937 	if (plr[p]._pClass == PC_WARRIOR) {
938 		vadd <<= 1;
939 	} else if (plr[p]._pClass == PC_BARBARIAN) {
940 		vadd += vadd;
941 		vadd += (vadd >> 2);
942 	} else if (plr[p]._pClass == PC_ROGUE || plr[p]._pClass == PC_MONK || plr[p]._pClass == PC_BARD) {
943 		vadd += vadd >> 1;
944 	}
945 	ihp += (vadd << 6); // BUGFIX: blood boil can cause negative shifts here (see line 757)
946 
947 	if (plr[p]._pClass == PC_SORCERER) {
948 		madd <<= 1;
949 	}
950 	if (plr[p]._pClass == PC_ROGUE || plr[p]._pClass == PC_MONK) {
951 		madd += madd >> 1;
952 	} else if (plr[p]._pClass == PC_BARD) {
953 		madd += (madd >> 2) + (madd >> 1);
954 	}
955 	imana += (madd << 6);
956 
957 	plr[p]._pHitPoints = ihp + plr[p]._pHPBase;
958 	plr[p]._pMaxHP = ihp + plr[p]._pMaxHPBase;
959 	if (plr[p]._pHitPoints > plr[p]._pMaxHP)
960 		plr[p]._pHitPoints = plr[p]._pMaxHP;
961 
962 	if (p == myplr && (plr[p]._pHitPoints >> 6) <= 0) {
963 		SetPlayerHitPoints(p, 0);
964 	}
965 
966 	plr[p]._pMana = imana + plr[p]._pManaBase;
967 	plr[p]._pMaxMana = imana + plr[p]._pMaxManaBase;
968 	if (plr[p]._pMana > plr[p]._pMaxMana)
969 		plr[p]._pMana = plr[p]._pMaxMana;
970 
971 	plr[p]._pIFMinDam = fmin;
972 	plr[p]._pIFMaxDam = fmax;
973 	plr[p]._pILMinDam = lmin;
974 	plr[p]._pILMaxDam = lmax;
975 
976 	if (iflgs & ISPL_INFRAVISION) {
977 		plr[p]._pInfraFlag = TRUE;
978 	} else {
979 		plr[p]._pInfraFlag = FALSE;
980 	}
981 
982 	plr[p]._pBlockFlag = FALSE;
983 	if (plr[p]._pClass == PC_MONK) {
984 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) {
985 			plr[p]._pBlockFlag = TRUE;
986 			plr[p]._pIFlags |= ISPL_FASTBLOCK;
987 		}
988 		if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) {
989 			plr[p]._pBlockFlag = TRUE;
990 			plr[p]._pIFlags |= ISPL_FASTBLOCK;
991 		}
992 		if (plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty() && plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty())
993 			plr[p]._pBlockFlag = TRUE;
994 		if (plr[p].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON && plr[p].InvBody[INVLOC_HAND_LEFT]._iLoc != ILOC_TWOHAND && plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty())
995 			plr[p]._pBlockFlag = TRUE;
996 		if (plr[p].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON && plr[p].InvBody[INVLOC_HAND_RIGHT]._iLoc != ILOC_TWOHAND && plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty())
997 			plr[p]._pBlockFlag = TRUE;
998 	}
999 	plr[p]._pwtype = WT_MELEE;
1000 
1001 	g = 0;
1002 
1003 	if (!plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty()
1004 	    && plr[p].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON
1005 	    && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) {
1006 		g = plr[p].InvBody[INVLOC_HAND_LEFT]._itype;
1007 	}
1008 
1009 	if (!plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty()
1010 	    && plr[p].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON
1011 	    && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) {
1012 		g = plr[p].InvBody[INVLOC_HAND_RIGHT]._itype;
1013 	}
1014 
1015 	switch (g) {
1016 	case ITYPE_SWORD:
1017 		g = ANIM_ID_SWORD;
1018 		break;
1019 	case ITYPE_AXE:
1020 		g = ANIM_ID_AXE;
1021 		break;
1022 	case ITYPE_BOW:
1023 		plr[p]._pwtype = WT_RANGED;
1024 		g = ANIM_ID_BOW;
1025 		break;
1026 	case ITYPE_MACE:
1027 		g = ANIM_ID_MACE;
1028 		break;
1029 	case ITYPE_STAFF:
1030 		g = ANIM_ID_STAFF;
1031 		break;
1032 	}
1033 
1034 	if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) {
1035 		plr[p]._pBlockFlag = TRUE;
1036 		g++;
1037 	}
1038 	if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) {
1039 		plr[p]._pBlockFlag = TRUE;
1040 		g++;
1041 	}
1042 
1043 	if (plr[p].InvBody[INVLOC_CHEST]._itype == ITYPE_HARMOR && plr[p].InvBody[INVLOC_CHEST]._iStatFlag) {
1044 		if (plr[p]._pClass == PC_MONK && plr[p].InvBody[INVLOC_CHEST]._iMagical == ITEM_QUALITY_UNIQUE)
1045 			plr[p]._pIAC += plr[p]._pLevel >> 1;
1046 		g += ANIM_ID_HEAVY_ARMOR;
1047 	} else if (plr[p].InvBody[INVLOC_CHEST]._itype == ITYPE_MARMOR && plr[p].InvBody[INVLOC_CHEST]._iStatFlag) {
1048 		if (plr[p]._pClass == PC_MONK) {
1049 			if (plr[p].InvBody[INVLOC_CHEST]._iMagical == ITEM_QUALITY_UNIQUE)
1050 				plr[p]._pIAC += plr[p]._pLevel << 1;
1051 			else
1052 				plr[p]._pIAC += plr[p]._pLevel >> 1;
1053 		}
1054 		g += ANIM_ID_MEDIUM_ARMOR;
1055 	} else if (plr[p]._pClass == PC_MONK) {
1056 		plr[p]._pIAC += plr[p]._pLevel << 1;
1057 	}
1058 
1059 	if (plr[p]._pgfxnum != g && Loadgfx) {
1060 		plr[p]._pgfxnum = g;
1061 		plr[p]._pGFXLoad = 0;
1062 		LoadPlrGFX(p, PFILE_STAND);
1063 		SetPlrAnims(p);
1064 
1065 		d = plr[p]._pdir;
1066 
1067 		assert(plr[p]._pNAnim[d]);
1068 		plr[p]._pAnimData = plr[p]._pNAnim[d];
1069 
1070 		plr[p]._pAnimLen = plr[p]._pNFrames;
1071 		plr[p]._pAnimFrame = 1;
1072 		plr[p]._pAnimCnt = 0;
1073 		plr[p]._pAnimDelay = 3;
1074 		plr[p]._pAnimWidth = plr[p]._pNWidth;
1075 		plr[p]._pAnimWidth2 = (plr[p]._pNWidth - 64) >> 1;
1076 	} else {
1077 		plr[p]._pgfxnum = g;
1078 	}
1079 
1080 	for (i = 0; i < nummissiles; i++) {
1081 		mi = missileactive[i];
1082 		if (missile[mi]._mitype == MIS_MANASHIELD && missile[mi]._misource == p) {
1083 			missile[mi]._miVar1 = plr[p]._pHitPoints;
1084 			missile[mi]._miVar2 = plr[p]._pHPBase;
1085 			break;
1086 		}
1087 	}
1088 	if (plr[p].InvBody[INVLOC_AMULET].isEmpty() || plr[p].InvBody[INVLOC_AMULET].IDidx != IDI_AURIC) {
1089 		int half = MaxGold;
1090 		MaxGold = GOLD_MAX_LIMIT;
1091 
1092 		if (half != MaxGold)
1093 			StripTopGold(p);
1094 	} else {
1095 		MaxGold = GOLD_MAX_LIMIT * 2;
1096 	}
1097 
1098 	drawmanaflag = TRUE;
1099 	drawhpflag = TRUE;
1100 }
1101 
CalcPlrScrolls(int p)1102 void CalcPlrScrolls(int p)
1103 {
1104 	int i, j;
1105 
1106 	plr[p]._pScrlSpells = 0;
1107 	for (i = 0; i < plr[p]._pNumInv; i++) {
1108 		if (!plr[p].InvList[i].isEmpty() && (plr[p].InvList[i]._iMiscId == IMISC_SCROLL || plr[p].InvList[i]._iMiscId == IMISC_SCROLLT)) {
1109 			if (plr[p].InvList[i]._iStatFlag)
1110 				plr[p]._pScrlSpells |= GetSpellBitmask(plr[p].InvList[i]._iSpell);
1111 		}
1112 	}
1113 
1114 	for (j = 0; j < MAXBELTITEMS; j++) {
1115 		if (!plr[p].SpdList[j].isEmpty() && (plr[p].SpdList[j]._iMiscId == IMISC_SCROLL || plr[p].SpdList[j]._iMiscId == IMISC_SCROLLT)) {
1116 			if (plr[p].SpdList[j]._iStatFlag)
1117 				plr[p]._pScrlSpells |= GetSpellBitmask(plr[p].SpdList[j]._iSpell);
1118 		}
1119 	}
1120 	EnsureValidReadiedSpell(plr[p]);
1121 }
1122 
CalcPlrStaff(int p)1123 void CalcPlrStaff(int p)
1124 {
1125 	plr[p]._pISpells = 0;
1126 	if (!plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty()
1127 	    && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag
1128 	    && plr[p].InvBody[INVLOC_HAND_LEFT]._iCharges > 0) {
1129 		plr[p]._pISpells |= GetSpellBitmask(plr[p].InvBody[INVLOC_HAND_LEFT]._iSpell);
1130 	}
1131 }
1132 
CalcSelfItems(int pnum)1133 void CalcSelfItems(int pnum)
1134 {
1135 	int i;
1136 	PlayerStruct *p;
1137 	ItemStruct *pi;
1138 	BOOL sf, changeflag;
1139 	int sa, ma, da;
1140 
1141 	p = &plr[pnum];
1142 
1143 	sa = 0;
1144 	ma = 0;
1145 	da = 0;
1146 	pi = p->InvBody;
1147 	for (i = 0; i < NUM_INVLOC; i++, pi++) {
1148 		if (!pi->isEmpty()) {
1149 			pi->_iStatFlag = TRUE;
1150 			if (pi->_iIdentified) {
1151 				sa += pi->_iPLStr;
1152 				ma += pi->_iPLMag;
1153 				da += pi->_iPLDex;
1154 			}
1155 		}
1156 	}
1157 	do {
1158 		changeflag = FALSE;
1159 		pi = p->InvBody;
1160 		for (i = 0; i < NUM_INVLOC; i++, pi++) {
1161 			if (!pi->isEmpty() && pi->_iStatFlag) {
1162 				sf = TRUE;
1163 				if (sa + p->_pBaseStr < pi->_iMinStr)
1164 					sf = FALSE;
1165 				if (ma + p->_pBaseMag < pi->_iMinMag)
1166 					sf = FALSE;
1167 				if (da + p->_pBaseDex < pi->_iMinDex)
1168 					sf = FALSE;
1169 				if (!sf) {
1170 					changeflag = TRUE;
1171 					pi->_iStatFlag = FALSE;
1172 					if (pi->_iIdentified) {
1173 						sa -= pi->_iPLStr;
1174 						ma -= pi->_iPLMag;
1175 						da -= pi->_iPLDex;
1176 					}
1177 				}
1178 			}
1179 		}
1180 	} while (changeflag);
1181 }
1182 
ItemMinStats(PlayerStruct * p,ItemStruct * x)1183 static BOOL ItemMinStats(PlayerStruct *p, ItemStruct *x)
1184 {
1185 	if (p->_pMagic < x->_iMinMag)
1186 		return FALSE;
1187 
1188 	if (p->_pStrength < x->_iMinStr)
1189 		return FALSE;
1190 
1191 	if (p->_pDexterity < x->_iMinDex)
1192 		return FALSE;
1193 
1194 	return TRUE;
1195 }
1196 
CalcPlrItemMin(int pnum)1197 void CalcPlrItemMin(int pnum)
1198 {
1199 	PlayerStruct *p;
1200 	ItemStruct *pi;
1201 	int i;
1202 
1203 	p = &plr[pnum];
1204 	pi = p->InvList;
1205 	i = p->_pNumInv;
1206 
1207 	while (i--) {
1208 		pi->_iStatFlag = ItemMinStats(p, pi);
1209 		pi++;
1210 	}
1211 
1212 	pi = p->SpdList;
1213 	for (i = MAXBELTITEMS; i != 0; i--) {
1214 		if (!pi->isEmpty()) {
1215 			pi->_iStatFlag = ItemMinStats(p, pi);
1216 		}
1217 		pi++;
1218 	}
1219 }
1220 
CalcPlrBookVals(int p)1221 void CalcPlrBookVals(int p)
1222 {
1223 	int i, slvl;
1224 
1225 	if (currlevel == 0) {
1226 		for (i = 1; !witchitem[i].isEmpty(); i++) {
1227 			WitchBookLevel(i);
1228 			witchitem[i]._iStatFlag = StoreStatOk(&witchitem[i]);
1229 		}
1230 	}
1231 
1232 	for (i = 0; i < plr[p]._pNumInv; i++) {
1233 		if (plr[p].InvList[i]._itype == ITYPE_MISC && plr[p].InvList[i]._iMiscId == IMISC_BOOK) {
1234 			plr[p].InvList[i]._iMinMag = spelldata[plr[p].InvList[i]._iSpell].sMinInt;
1235 			slvl = plr[p]._pSplLvl[plr[p].InvList[i]._iSpell];
1236 
1237 			while (slvl != 0) {
1238 				plr[p].InvList[i]._iMinMag += 20 * plr[p].InvList[i]._iMinMag / 100;
1239 				slvl--;
1240 				if (plr[p].InvList[i]._iMinMag + 20 * plr[p].InvList[i]._iMinMag / 100 > 255) {
1241 					plr[p].InvList[i]._iMinMag = 255;
1242 					slvl = 0;
1243 				}
1244 			}
1245 			plr[p].InvList[i]._iStatFlag = ItemMinStats(&plr[p], &plr[p].InvList[i]);
1246 		}
1247 	}
1248 }
1249 
CalcPlrInv(int p,BOOL Loadgfx)1250 void CalcPlrInv(int p, BOOL Loadgfx)
1251 {
1252 	CalcPlrItemMin(p);
1253 	CalcSelfItems(p);
1254 	CalcPlrItemVals(p, Loadgfx);
1255 	CalcPlrItemMin(p);
1256 	if (p == myplr) {
1257 		CalcPlrBookVals(p);
1258 		CalcPlrScrolls(p);
1259 		CalcPlrStaff(p);
1260 		if (p == myplr && currlevel == 0)
1261 			RecalcStoreStats();
1262 	}
1263 }
1264 
SetPlrHandItem(ItemStruct * h,int idata)1265 void SetPlrHandItem(ItemStruct *h, int idata)
1266 {
1267 	ItemDataStruct *pAllItem;
1268 
1269 	pAllItem = &AllItemsList[idata];
1270 
1271 	// zero-initialize struct
1272 	memset(h, 0, sizeof(*h));
1273 
1274 	h->_itype = pAllItem->itype;
1275 	h->_iCurs = pAllItem->iCurs;
1276 	strcpy(h->_iName, pAllItem->iName);
1277 	strcpy(h->_iIName, pAllItem->iName);
1278 	h->_iLoc = pAllItem->iLoc;
1279 	h->_iClass = pAllItem->iClass;
1280 	h->_iMinDam = pAllItem->iMinDam;
1281 	h->_iMaxDam = pAllItem->iMaxDam;
1282 	h->_iAC = pAllItem->iMinAC;
1283 	h->_iMiscId = pAllItem->iMiscId;
1284 	h->_iSpell = pAllItem->iSpell;
1285 
1286 	if (pAllItem->iMiscId == IMISC_STAFF) {
1287 		h->_iCharges = gbIsHellfire ? 18 : 40;
1288 	}
1289 
1290 	h->_iMaxCharges = h->_iCharges;
1291 	h->_iDurability = pAllItem->iDurability;
1292 	h->_iMaxDur = pAllItem->iDurability;
1293 	h->_iMinStr = pAllItem->iMinStr;
1294 	h->_iMinMag = pAllItem->iMinMag;
1295 	h->_iMinDex = pAllItem->iMinDex;
1296 	h->_ivalue = pAllItem->iValue;
1297 	h->_iIvalue = pAllItem->iValue;
1298 	h->_iPrePower = IPL_INVALID;
1299 	h->_iSufPower = IPL_INVALID;
1300 	h->_iMagical = ITEM_QUALITY_NORMAL;
1301 	h->IDidx = idata;
1302 	if (gbIsHellfire)
1303 		h->dwBuff |= CF_HELLFIRE;
1304 }
1305 
GetPlrHandSeed(ItemStruct * h)1306 void GetPlrHandSeed(ItemStruct *h)
1307 {
1308 	h->_iSeed = AdvanceRndSeed();
1309 }
1310 
1311 /**
1312  * @brief Set a new unique seed value on the given item
1313  * @param pnum Player id
1314  * @param h Item to update
1315  */
GetGoldSeed(int pnum,ItemStruct * h)1316 void GetGoldSeed(int pnum, ItemStruct *h)
1317 {
1318 	int i, ii, s;
1319 	BOOL doneflag;
1320 
1321 	do {
1322 		doneflag = TRUE;
1323 		s = AdvanceRndSeed();
1324 		for (i = 0; i < numitems; i++) {
1325 			ii = itemactive[i];
1326 			if (item[ii]._iSeed == s)
1327 				doneflag = FALSE;
1328 		}
1329 		if (pnum == myplr) {
1330 			for (i = 0; i < plr[pnum]._pNumInv; i++) {
1331 				if (plr[pnum].InvList[i]._iSeed == s)
1332 					doneflag = FALSE;
1333 			}
1334 		}
1335 	} while (!doneflag);
1336 
1337 	h->_iSeed = s;
1338 }
1339 
SetPlrHandSeed(ItemStruct * h,int iseed)1340 void SetPlrHandSeed(ItemStruct *h, int iseed)
1341 {
1342 	h->_iSeed = iseed;
1343 }
1344 
GetGoldCursor(int value)1345 int GetGoldCursor(int value)
1346 {
1347 	if (value >= GOLD_MEDIUM_LIMIT)
1348 		return ICURS_GOLD_LARGE;
1349 
1350 	if (value <= GOLD_SMALL_LIMIT)
1351 		return ICURS_GOLD_SMALL;
1352 
1353 	return ICURS_GOLD_MEDIUM;
1354 }
1355 
1356 /**
1357  * @brief Update the gold cursor on the given gold item
1358  * @param h The item to update
1359  */
SetPlrHandGoldCurs(ItemStruct * h)1360 void SetPlrHandGoldCurs(ItemStruct *h)
1361 {
1362 	h->_iCurs = GetGoldCursor(h->_ivalue);
1363 }
1364 
CreatePlrItems(int p)1365 void CreatePlrItems(int p)
1366 {
1367 	int i;
1368 	ItemStruct *pi = plr[p].InvBody;
1369 
1370 	for (i = NUM_INVLOC; i != 0; i--) {
1371 		pi->_itype = ITYPE_NONE;
1372 		pi++;
1373 	}
1374 
1375 	// converting this to a for loop creates a `rep stosd` instruction,
1376 	// so this probably actually was a memset
1377 	memset(&plr[p].InvGrid, 0, sizeof(plr[p].InvGrid));
1378 
1379 	pi = plr[p].InvList;
1380 	for (i = NUM_INV_GRID_ELEM; i != 0; i--) {
1381 		pi->_itype = ITYPE_NONE;
1382 		pi++;
1383 	}
1384 
1385 	plr[p]._pNumInv = 0;
1386 
1387 	pi = &plr[p].SpdList[0];
1388 	for (i = MAXBELTITEMS; i != 0; i--) {
1389 		pi->_itype = ITYPE_NONE;
1390 		pi++;
1391 	}
1392 
1393 	switch (plr[p]._pClass) {
1394 	case PC_WARRIOR:
1395 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_WARRIOR);
1396 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]);
1397 
1398 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_RIGHT], IDI_WARRSHLD);
1399 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_RIGHT]);
1400 
1401 #ifdef _DEBUG
1402 		if (!debug_mode_key_w)
1403 #endif
1404 		{
1405 			SetPlrHandItem(&plr[p].HoldItem, IDI_WARRCLUB);
1406 			GetPlrHandSeed(&plr[p].HoldItem);
1407 			AutoPlace(p, 0, 1, 3, TRUE);
1408 		}
1409 
1410 		SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL);
1411 		GetPlrHandSeed(&plr[p].SpdList[0]);
1412 
1413 		SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL);
1414 		GetPlrHandSeed(&plr[p].SpdList[1]);
1415 		break;
1416 	case PC_ROGUE:
1417 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_ROGUE);
1418 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]);
1419 
1420 		SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL);
1421 		GetPlrHandSeed(&plr[p].SpdList[0]);
1422 
1423 		SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL);
1424 		GetPlrHandSeed(&plr[p].SpdList[1]);
1425 		break;
1426 	case PC_SORCERER:
1427 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], gbIsHellfire ? IDI_SORCERER : 166);
1428 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]);
1429 
1430 		SetPlrHandItem(&plr[p].SpdList[0], gbIsHellfire ? IDI_HEAL : IDI_MANA);
1431 		GetPlrHandSeed(&plr[p].SpdList[0]);
1432 
1433 		SetPlrHandItem(&plr[p].SpdList[1], gbIsHellfire ? IDI_HEAL : IDI_MANA);
1434 		GetPlrHandSeed(&plr[p].SpdList[1]);
1435 		break;
1436 
1437 	case PC_MONK:
1438 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_SHORTSTAFF);
1439 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]);
1440 		SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL);
1441 		GetPlrHandSeed(&plr[p].SpdList[0]);
1442 
1443 		SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL);
1444 		GetPlrHandSeed(&plr[p].SpdList[1]);
1445 		break;
1446 	case PC_BARD:
1447 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_BARDSWORD);
1448 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]);
1449 
1450 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_RIGHT], IDI_BARDDAGGER);
1451 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_RIGHT]);
1452 		SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL);
1453 		GetPlrHandSeed(&plr[p].SpdList[0]);
1454 
1455 		SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL);
1456 		GetPlrHandSeed(&plr[p].SpdList[1]);
1457 		break;
1458 	case PC_BARBARIAN:
1459 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], 139); // TODO: add more enums to items
1460 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]);
1461 
1462 		SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_RIGHT], IDI_WARRSHLD);
1463 		GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_RIGHT]);
1464 		SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL);
1465 		GetPlrHandSeed(&plr[p].SpdList[0]);
1466 
1467 		SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL);
1468 		GetPlrHandSeed(&plr[p].SpdList[1]);
1469 		break;
1470 	case NUM_CLASSES:
1471 		break;
1472 	}
1473 
1474 	SetPlrHandItem(&plr[p].HoldItem, IDI_GOLD);
1475 	GetPlrHandSeed(&plr[p].HoldItem);
1476 
1477 #ifdef _DEBUG
1478 	if (!debug_mode_key_w) {
1479 #endif
1480 		plr[p].HoldItem._ivalue = 100;
1481 		plr[p].HoldItem._iCurs = ICURS_GOLD_SMALL;
1482 		plr[p]._pGold = plr[p].HoldItem._ivalue;
1483 		plr[p].InvList[plr[p]._pNumInv++] = plr[p].HoldItem;
1484 		plr[p].InvGrid[30] = plr[p]._pNumInv;
1485 #ifdef _DEBUG
1486 	} else {
1487 		plr[p].HoldItem._ivalue = GOLD_MAX_LIMIT;
1488 		plr[p].HoldItem._iCurs = ICURS_GOLD_LARGE;
1489 		plr[p]._pGold = plr[p].HoldItem._ivalue * 40;
1490 		for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
1491 			GetPlrHandSeed(&plr[p].HoldItem);
1492 			plr[p].InvList[plr[p]._pNumInv++] = plr[p].HoldItem;
1493 			plr[p].InvGrid[i] = plr[p]._pNumInv;
1494 		}
1495 	}
1496 #endif
1497 
1498 	CalcPlrItemVals(p, FALSE);
1499 }
1500 
ItemSpaceOk(int i,int j)1501 BOOL ItemSpaceOk(int i, int j)
1502 {
1503 	int oi;
1504 
1505 	// BUGFIX: Check `i + 1 >= MAXDUNX` and `j + 1 >= MAXDUNY` (applied)
1506 	if (i < 0 || i + 1 >= MAXDUNX || j < 0 || j + 1 >= MAXDUNY)
1507 		return FALSE;
1508 
1509 	if (dMonster[i][j] != 0)
1510 		return FALSE;
1511 
1512 	if (dPlayer[i][j] != 0)
1513 		return FALSE;
1514 
1515 	if (dItem[i][j] != 0)
1516 		return FALSE;
1517 
1518 	if (dObject[i][j] != 0) {
1519 		oi = dObject[i][j] > 0 ? dObject[i][j] - 1 : -(dObject[i][j] + 1);
1520 		if (object[oi]._oSolidFlag)
1521 			return FALSE;
1522 	}
1523 
1524 	if (dObject[i + 1][j + 1] > 0 && object[dObject[i + 1][j + 1] - 1]._oSelFlag != 0)
1525 		return FALSE;
1526 
1527 	if (dObject[i + 1][j + 1] < 0 && object[-(dObject[i + 1][j + 1] + 1)]._oSelFlag != 0)
1528 		return FALSE;
1529 
1530 	if (dObject[i + 1][j] > 0
1531 	    && dObject[i][j + 1] > 0
1532 	    && object[dObject[i + 1][j] - 1]._oSelFlag != 0
1533 	    && object[dObject[i][j + 1] - 1]._oSelFlag != 0) {
1534 		return FALSE;
1535 	}
1536 
1537 	return !nSolidTable[dPiece[i][j]];
1538 }
1539 
GetItemSpace(int x,int y,char inum)1540 static bool GetItemSpace(int x, int y, char inum)
1541 {
1542 	int i, j, rs;
1543 	int xx, yy;
1544 	BOOL savail;
1545 
1546 	yy = 0;
1547 	for (j = y - 1; j <= y + 1; j++) {
1548 		xx = 0;
1549 		for (i = x - 1; i <= x + 1; i++) {
1550 			itemhold[xx][yy] = ItemSpaceOk(i, j);
1551 			xx++;
1552 		}
1553 		yy++;
1554 	}
1555 
1556 	savail = FALSE;
1557 	for (j = 0; j < 3; j++) {
1558 		for (i = 0; i < 3; i++) {
1559 			if (itemhold[i][j])
1560 				savail = TRUE;
1561 		}
1562 	}
1563 
1564 	rs = random_(13, 15) + 1;
1565 
1566 	if (!savail)
1567 		return false;
1568 
1569 	xx = 0;
1570 	yy = 0;
1571 	while (rs > 0) {
1572 		if (itemhold[xx][yy])
1573 			rs--;
1574 		if (rs > 0) {
1575 			xx++;
1576 			if (xx == 3) {
1577 				xx = 0;
1578 				yy++;
1579 				if (yy == 3)
1580 					yy = 0;
1581 			}
1582 		}
1583 	}
1584 
1585 	xx += x - 1;
1586 	yy += y - 1;
1587 	item[inum]._ix = xx;
1588 	item[inum]._iy = yy;
1589 	dItem[xx][yy] = inum + 1;
1590 
1591 	return true;
1592 }
1593 
AllocateItem()1594 int AllocateItem()
1595 {
1596 	int inum = itemavail[0];
1597 	itemavail[0] = itemavail[MAXITEMS - numitems - 1];
1598 	itemactive[numitems] = inum;
1599 	numitems++;
1600 
1601 	memset(&item[inum], 0, sizeof(*item));
1602 
1603 	return inum;
1604 }
1605 
GetSuperItemSpace(int x,int y,char inum)1606 static void GetSuperItemSpace(int x, int y, char inum)
1607 {
1608 	if (!GetItemSpace(x, y, inum)) {
1609 		for (int k = 2; k < 50; k++) {
1610 			for (int j = -k; j <= k; j++) {
1611 				int yy = y + j;
1612 				for (int i = -k; i <= k; i++) {
1613 					int xx = i + x;
1614 					if (ItemSpaceOk(xx, yy)) {
1615 						item[inum]._ix = xx;
1616 						item[inum]._iy = yy;
1617 						dItem[xx][yy] = inum + 1;
1618 						return;
1619 					}
1620 				}
1621 			}
1622 		}
1623 	}
1624 }
1625 
GetSuperItemLoc(int x,int y,int * xx,int * yy)1626 void GetSuperItemLoc(int x, int y, int *xx, int *yy)
1627 {
1628 	int i, j, k;
1629 
1630 	for (k = 1; k < 50; k++) {
1631 		for (j = -k; j <= k; j++) {
1632 			*yy = y + j;
1633 			for (i = -k; i <= k; i++) {
1634 				*xx = i + x;
1635 				if (ItemSpaceOk(*xx, *yy)) {
1636 					return;
1637 				}
1638 			}
1639 		}
1640 	}
1641 }
1642 
CalcItemValue(int i)1643 void CalcItemValue(int i)
1644 {
1645 	int v;
1646 
1647 	v = item[i]._iVMult1 + item[i]._iVMult2;
1648 	if (v > 0) {
1649 		v *= item[i]._ivalue;
1650 	}
1651 	if (v < 0) {
1652 		v = item[i]._ivalue / v;
1653 	}
1654 	v = item[i]._iVAdd1 + item[i]._iVAdd2 + v;
1655 	if (v <= 0) {
1656 		v = 1;
1657 	}
1658 	item[i]._iIvalue = v;
1659 }
1660 
GetBookSpell(int i,int lvl)1661 void GetBookSpell(int i, int lvl)
1662 {
1663 	int rv;
1664 
1665 	if (lvl == 0)
1666 		lvl = 1;
1667 
1668 	int maxSpells = gbIsHellfire ? MAX_SPELLS : 37;
1669 
1670 	rv = random_(14, maxSpells) + 1;
1671 
1672 	if (gbIsSpawn && lvl > 5)
1673 		lvl = 5;
1674 
1675 	int s = SPL_FIREBOLT;
1676 	enum spell_id bs = SPL_FIREBOLT;
1677 	while (rv > 0) {
1678 		int sLevel = GetSpellBookLevel(static_cast<spell_id>(s));
1679 		if (sLevel != -1 && lvl >= sLevel) {
1680 			rv--;
1681 			bs = static_cast<spell_id>(s);
1682 		}
1683 		s++;
1684 		if (!gbIsMultiplayer) {
1685 			if (s == SPL_RESURRECT)
1686 				s = SPL_TELEKINESIS;
1687 		}
1688 		if (!gbIsMultiplayer) {
1689 			if (s == SPL_HEALOTHER)
1690 				s = SPL_FLARE;
1691 		}
1692 		if (s == maxSpells)
1693 			s = 1;
1694 	}
1695 	strcat(item[i]._iName, spelldata[bs].sNameText);
1696 	strcat(item[i]._iIName, spelldata[bs].sNameText);
1697 	item[i]._iSpell = bs;
1698 	item[i]._iMinMag = spelldata[bs].sMinInt;
1699 	item[i]._ivalue += spelldata[bs].sBookCost;
1700 	item[i]._iIvalue += spelldata[bs].sBookCost;
1701 	if (spelldata[bs].sType == STYPE_FIRE)
1702 		item[i]._iCurs = ICURS_BOOK_RED;
1703 	else if (spelldata[bs].sType == STYPE_LIGHTNING)
1704 		item[i]._iCurs = ICURS_BOOK_BLUE;
1705 	else if (spelldata[bs].sType == STYPE_MAGIC)
1706 		item[i]._iCurs = ICURS_BOOK_GREY;
1707 }
1708 
GetStaffPower(int i,int lvl,int bs,BOOL onlygood)1709 void GetStaffPower(int i, int lvl, int bs, BOOL onlygood)
1710 {
1711 	int l[256];
1712 	char istr[128];
1713 	int nl, j, preidx;
1714 	BOOL addok;
1715 	int tmp;
1716 
1717 	tmp = random_(15, 10);
1718 	preidx = -1;
1719 	if (tmp == 0 || onlygood) {
1720 		nl = 0;
1721 		for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) {
1722 			if (IsPrefixValidForItemType(j, PLT_STAFF) && PL_Prefix[j].PLMinLvl <= lvl) {
1723 				addok = TRUE;
1724 				if (onlygood && !PL_Prefix[j].PLOk)
1725 					addok = FALSE;
1726 				if (addok) {
1727 					l[nl] = j;
1728 					nl++;
1729 					if (PL_Prefix[j].PLDouble) {
1730 						l[nl] = j;
1731 						nl++;
1732 					}
1733 				}
1734 			}
1735 		}
1736 		if (nl != 0) {
1737 			preidx = l[random_(16, nl)];
1738 			sprintf(istr, "%s %s", PL_Prefix[preidx].PLName, item[i]._iIName);
1739 			strcpy(item[i]._iIName, istr);
1740 			item[i]._iMagical = ITEM_QUALITY_MAGIC;
1741 			SaveItemPower(
1742 			    i,
1743 			    PL_Prefix[preidx].PLPower,
1744 			    PL_Prefix[preidx].PLParam1,
1745 			    PL_Prefix[preidx].PLParam2,
1746 			    PL_Prefix[preidx].PLMinVal,
1747 			    PL_Prefix[preidx].PLMaxVal,
1748 			    PL_Prefix[preidx].PLMultVal);
1749 			item[i]._iPrePower = PL_Prefix[preidx].PLPower;
1750 		}
1751 	}
1752 	if (!control_WriteStringToBuffer((BYTE *)item[i]._iIName)) {
1753 		strcpy(item[i]._iIName, AllItemsList[item[i].IDidx].iSName);
1754 		if (preidx != -1) {
1755 			sprintf(istr, "%s %s", PL_Prefix[preidx].PLName, item[i]._iIName);
1756 			strcpy(item[i]._iIName, istr);
1757 		}
1758 		sprintf(istr, "%s of %s", item[i]._iIName, spelldata[bs].sNameText);
1759 		strcpy(item[i]._iIName, istr);
1760 		if (item[i]._iMagical == ITEM_QUALITY_NORMAL)
1761 			strcpy(item[i]._iName, item[i]._iIName);
1762 	}
1763 	CalcItemValue(i);
1764 }
1765 
GetStaffSpell(int i,int lvl,BOOL onlygood)1766 void GetStaffSpell(int i, int lvl, BOOL onlygood)
1767 {
1768 	int l, rv, minc, maxc, v;
1769 	char istr[68];
1770 
1771 	if (!gbIsHellfire && random_(17, 4) == 0) {
1772 		GetItemPower(i, lvl >> 1, lvl, PLT_STAFF, onlygood);
1773 	} else {
1774 		int maxSpells = gbIsHellfire ? MAX_SPELLS : 37;
1775 		l = lvl >> 1;
1776 		if (l == 0)
1777 			l = 1;
1778 		rv = random_(18, maxSpells) + 1;
1779 
1780 		if (gbIsSpawn && lvl > 10)
1781 			lvl = 10;
1782 
1783 		int s = SPL_FIREBOLT;
1784 		enum spell_id bs = SPL_NULL;
1785 		while (rv > 0) {
1786 			int sLevel = GetSpellStaffLevel(static_cast<spell_id>(s));
1787 			if (sLevel != -1 && l >= sLevel) {
1788 				rv--;
1789 				bs = static_cast<spell_id>(s);
1790 			}
1791 			s++;
1792 			if (!gbIsMultiplayer && s == SPL_RESURRECT)
1793 				s = SPL_TELEKINESIS;
1794 			if (!gbIsMultiplayer && s == SPL_HEALOTHER)
1795 				s = SPL_FLARE;
1796 			if (s == maxSpells)
1797 				s = SPL_FIREBOLT;
1798 		}
1799 		sprintf(istr, "%s of %s", item[i]._iName, spelldata[bs].sNameText);
1800 		if (!control_WriteStringToBuffer((BYTE *)istr))
1801 			sprintf(istr, "Staff of %s", spelldata[bs].sNameText);
1802 		strcpy(item[i]._iName, istr);
1803 		strcpy(item[i]._iIName, istr);
1804 
1805 		minc = spelldata[bs].sStaffMin;
1806 		maxc = spelldata[bs].sStaffMax - minc + 1;
1807 		item[i]._iSpell = bs;
1808 		item[i]._iCharges = minc + random_(19, maxc);
1809 		item[i]._iMaxCharges = item[i]._iCharges;
1810 
1811 		item[i]._iMinMag = spelldata[bs].sMinInt;
1812 		v = item[i]._iCharges * spelldata[bs].sStaffCost / 5;
1813 		item[i]._ivalue += v;
1814 		item[i]._iIvalue += v;
1815 		GetStaffPower(i, lvl, bs, onlygood);
1816 	}
1817 }
1818 
GetOilType(int i,int max_lvl)1819 void GetOilType(int i, int max_lvl)
1820 {
1821 	int cnt, t, j, r;
1822 	char rnd[32];
1823 
1824 	if (!gbIsMultiplayer) {
1825 		if (max_lvl == 0)
1826 			max_lvl = 1;
1827 		cnt = 0;
1828 
1829 		for (j = 0; j < (int)(sizeof(OilLevels) / sizeof(OilLevels[0])); j++) {
1830 			if (OilLevels[j] <= max_lvl) {
1831 				rnd[cnt] = j;
1832 				cnt++;
1833 			}
1834 		}
1835 		r = random_(165, cnt);
1836 		t = rnd[r];
1837 	} else {
1838 		r = random_(165, 2);
1839 		t = (r != 0 ? 6 : 5);
1840 	}
1841 	strcpy(item[i]._iName, OilNames[t]);
1842 	strcpy(item[i]._iIName, OilNames[t]);
1843 	item[i]._iMiscId = OilMagic[t];
1844 	item[i]._ivalue = OilValues[t];
1845 	item[i]._iIvalue = OilValues[t];
1846 }
1847 
GetItemAttrs(int i,int idata,int lvl)1848 void GetItemAttrs(int i, int idata, int lvl)
1849 {
1850 	item[i]._itype = AllItemsList[idata].itype;
1851 	item[i]._iCurs = AllItemsList[idata].iCurs;
1852 	strcpy(item[i]._iName, AllItemsList[idata].iName);
1853 	strcpy(item[i]._iIName, AllItemsList[idata].iName);
1854 	item[i]._iLoc = AllItemsList[idata].iLoc;
1855 	item[i]._iClass = AllItemsList[idata].iClass;
1856 	item[i]._iMinDam = AllItemsList[idata].iMinDam;
1857 	item[i]._iMaxDam = AllItemsList[idata].iMaxDam;
1858 	item[i]._iAC = AllItemsList[idata].iMinAC + random_(20, AllItemsList[idata].iMaxAC - AllItemsList[idata].iMinAC + 1);
1859 	item[i]._iFlags = AllItemsList[idata].iFlags;
1860 	item[i]._iMiscId = AllItemsList[idata].iMiscId;
1861 	item[i]._iSpell = AllItemsList[idata].iSpell;
1862 	item[i]._iMagical = ITEM_QUALITY_NORMAL;
1863 	item[i]._ivalue = AllItemsList[idata].iValue;
1864 	item[i]._iIvalue = AllItemsList[idata].iValue;
1865 	item[i]._iDurability = AllItemsList[idata].iDurability;
1866 	item[i]._iMaxDur = AllItemsList[idata].iDurability;
1867 	item[i]._iMinStr = AllItemsList[idata].iMinStr;
1868 	item[i]._iMinMag = AllItemsList[idata].iMinMag;
1869 	item[i]._iMinDex = AllItemsList[idata].iMinDex;
1870 	item[i].IDidx = idata;
1871 	if (gbIsHellfire)
1872 		item[i].dwBuff |= CF_HELLFIRE;
1873 	item[i]._iPrePower = IPL_INVALID;
1874 	item[i]._iSufPower = IPL_INVALID;
1875 
1876 	if (item[i]._iMiscId == IMISC_BOOK)
1877 		GetBookSpell(i, lvl);
1878 
1879 	if (gbIsHellfire && item[i]._iMiscId == IMISC_OILOF)
1880 		GetOilType(i, lvl);
1881 
1882 	if (item[i]._itype != ITYPE_GOLD)
1883 		return;
1884 
1885 	int rndv;
1886 	int itemlevel = items_get_currlevel();
1887 	if (gnDifficulty == DIFF_NORMAL)
1888 		rndv = 5 * itemlevel + random_(21, 10 * itemlevel);
1889 	else if (gnDifficulty == DIFF_NIGHTMARE)
1890 		rndv = 5 * (itemlevel + 16) + random_(21, 10 * (itemlevel + 16));
1891 	else if (gnDifficulty == DIFF_HELL)
1892 		rndv = 5 * (itemlevel + 32) + random_(21, 10 * (itemlevel + 32));
1893 	if (leveltype == DTYPE_HELL)
1894 		rndv += rndv >> 3;
1895 	if (rndv > GOLD_MAX_LIMIT)
1896 		rndv = GOLD_MAX_LIMIT;
1897 
1898 	item[i]._ivalue = rndv;
1899 	SetPlrHandGoldCurs(&item[i]);
1900 }
1901 
RndPL(int param1,int param2)1902 int RndPL(int param1, int param2)
1903 {
1904 	return param1 + random_(22, param2 - param1 + 1);
1905 }
1906 
PLVal(int pv,int p1,int p2,int minv,int maxv)1907 int PLVal(int pv, int p1, int p2, int minv, int maxv)
1908 {
1909 	if (p1 == p2)
1910 		return minv;
1911 	if (minv == maxv)
1912 		return minv;
1913 	return minv + (maxv - minv) * (100 * (pv - p1) / (p2 - p1)) / 100;
1914 }
1915 
SaveItemPower(int i,item_effect_type power,int param1,int param2,int minval,int maxval,int multval)1916 void SaveItemPower(int i, item_effect_type power, int param1, int param2, int minval, int maxval, int multval)
1917 {
1918 	int r, r2;
1919 
1920 	r = RndPL(param1, param2);
1921 	switch (power) {
1922 	case IPL_TOHIT:
1923 		item[i]._iPLToHit += r;
1924 		break;
1925 	case IPL_TOHIT_CURSE:
1926 		item[i]._iPLToHit -= r;
1927 		break;
1928 	case IPL_DAMP:
1929 		item[i]._iPLDam += r;
1930 		break;
1931 	case IPL_DAMP_CURSE:
1932 		item[i]._iPLDam -= r;
1933 		break;
1934 	case IPL_DOPPELGANGER:
1935 		item[i]._iDamAcFlags |= 16;
1936 		// no break
1937 	case IPL_TOHIT_DAMP:
1938 		r = RndPL(param1, param2);
1939 		item[i]._iPLDam += r;
1940 		if (param1 == 20)
1941 			r2 = RndPL(1, 5);
1942 		if (param1 == 36)
1943 			r2 = RndPL(6, 10);
1944 		if (param1 == 51)
1945 			r2 = RndPL(11, 15);
1946 		if (param1 == 66)
1947 			r2 = RndPL(16, 20);
1948 		if (param1 == 81)
1949 			r2 = RndPL(21, 30);
1950 		if (param1 == 96)
1951 			r2 = RndPL(31, 40);
1952 		if (param1 == 111)
1953 			r2 = RndPL(41, 50);
1954 		if (param1 == 126)
1955 			r2 = RndPL(51, 75);
1956 		if (param1 == 151)
1957 			r2 = RndPL(76, 100);
1958 		item[i]._iPLToHit += r2;
1959 		break;
1960 	case IPL_TOHIT_DAMP_CURSE:
1961 		item[i]._iPLDam -= r;
1962 		if (param1 == 25)
1963 			r2 = RndPL(1, 5);
1964 		if (param1 == 50)
1965 			r2 = RndPL(6, 10);
1966 		item[i]._iPLToHit -= r2;
1967 		break;
1968 	case IPL_ACP:
1969 		item[i]._iPLAC += r;
1970 		break;
1971 	case IPL_ACP_CURSE:
1972 		item[i]._iPLAC -= r;
1973 		break;
1974 	case IPL_SETAC:
1975 		item[i]._iAC = r;
1976 		break;
1977 	case IPL_AC_CURSE:
1978 		item[i]._iAC -= r;
1979 		break;
1980 	case IPL_FIRERES:
1981 		item[i]._iPLFR += r;
1982 		break;
1983 	case IPL_LIGHTRES:
1984 		item[i]._iPLLR += r;
1985 		break;
1986 	case IPL_MAGICRES:
1987 		item[i]._iPLMR += r;
1988 		break;
1989 	case IPL_ALLRES:
1990 		item[i]._iPLFR += r;
1991 		item[i]._iPLLR += r;
1992 		item[i]._iPLMR += r;
1993 		if (item[i]._iPLFR < 0)
1994 			item[i]._iPLFR = 0;
1995 		if (item[i]._iPLLR < 0)
1996 			item[i]._iPLLR = 0;
1997 		if (item[i]._iPLMR < 0)
1998 			item[i]._iPLMR = 0;
1999 		break;
2000 	case IPL_SPLLVLADD:
2001 		item[i]._iSplLvlAdd = r;
2002 		break;
2003 	case IPL_CHARGES:
2004 		item[i]._iCharges *= param1;
2005 		item[i]._iMaxCharges = item[i]._iCharges;
2006 		break;
2007 	case IPL_SPELL:
2008 		item[i]._iSpell = static_cast<spell_id>(param1);
2009 		item[i]._iCharges = param2;
2010 		item[i]._iMaxCharges = param2;
2011 		break;
2012 	case IPL_FIREDAM:
2013 		item[i]._iFlags |= ISPL_FIREDAM;
2014 		item[i]._iFlags &= ~ISPL_LIGHTDAM;
2015 		item[i]._iFMinDam = param1;
2016 		item[i]._iFMaxDam = param2;
2017 		item[i]._iLMinDam = 0;
2018 		item[i]._iLMaxDam = 0;
2019 		break;
2020 	case IPL_LIGHTDAM:
2021 		item[i]._iFlags |= ISPL_LIGHTDAM;
2022 		item[i]._iFlags &= ~ISPL_FIREDAM;
2023 		item[i]._iLMinDam = param1;
2024 		item[i]._iLMaxDam = param2;
2025 		item[i]._iFMinDam = 0;
2026 		item[i]._iFMaxDam = 0;
2027 		break;
2028 	case IPL_STR:
2029 		item[i]._iPLStr += r;
2030 		break;
2031 	case IPL_STR_CURSE:
2032 		item[i]._iPLStr -= r;
2033 		break;
2034 	case IPL_MAG:
2035 		item[i]._iPLMag += r;
2036 		break;
2037 	case IPL_MAG_CURSE:
2038 		item[i]._iPLMag -= r;
2039 		break;
2040 	case IPL_DEX:
2041 		item[i]._iPLDex += r;
2042 		break;
2043 	case IPL_DEX_CURSE:
2044 		item[i]._iPLDex -= r;
2045 		break;
2046 	case IPL_VIT:
2047 		item[i]._iPLVit += r;
2048 		break;
2049 	case IPL_VIT_CURSE:
2050 		item[i]._iPLVit -= r;
2051 		break;
2052 	case IPL_ATTRIBS:
2053 		item[i]._iPLStr += r;
2054 		item[i]._iPLMag += r;
2055 		item[i]._iPLDex += r;
2056 		item[i]._iPLVit += r;
2057 		break;
2058 	case IPL_ATTRIBS_CURSE:
2059 		item[i]._iPLStr -= r;
2060 		item[i]._iPLMag -= r;
2061 		item[i]._iPLDex -= r;
2062 		item[i]._iPLVit -= r;
2063 		break;
2064 	case IPL_GETHIT_CURSE:
2065 		item[i]._iPLGetHit += r;
2066 		break;
2067 	case IPL_GETHIT:
2068 		item[i]._iPLGetHit -= r;
2069 		break;
2070 	case IPL_LIFE:
2071 		item[i]._iPLHP += r << 6;
2072 		break;
2073 	case IPL_LIFE_CURSE:
2074 		item[i]._iPLHP -= r << 6;
2075 		break;
2076 	case IPL_MANA:
2077 		item[i]._iPLMana += r << 6;
2078 		drawmanaflag = TRUE;
2079 		break;
2080 	case IPL_MANA_CURSE:
2081 		item[i]._iPLMana -= r << 6;
2082 		drawmanaflag = TRUE;
2083 		break;
2084 	case IPL_DUR:
2085 		r2 = r * item[i]._iMaxDur / 100;
2086 		item[i]._iMaxDur += r2;
2087 		item[i]._iDurability += r2;
2088 		break;
2089 	case IPL_CRYSTALLINE:
2090 		item[i]._iPLDam += 140 + r * 2;
2091 		// no break
2092 	case IPL_DUR_CURSE:
2093 		item[i]._iMaxDur -= r * item[i]._iMaxDur / 100;
2094 		if (item[i]._iMaxDur < 1)
2095 			item[i]._iMaxDur = 1;
2096 		item[i]._iDurability = item[i]._iMaxDur;
2097 		break;
2098 	case IPL_INDESTRUCTIBLE:
2099 		item[i]._iDurability = DUR_INDESTRUCTIBLE;
2100 		item[i]._iMaxDur = DUR_INDESTRUCTIBLE;
2101 		break;
2102 	case IPL_LIGHT:
2103 		item[i]._iPLLight += param1;
2104 		break;
2105 	case IPL_LIGHT_CURSE:
2106 		item[i]._iPLLight -= param1;
2107 		break;
2108 	case IPL_MULT_ARROWS:
2109 		item[i]._iFlags |= ISPL_MULT_ARROWS;
2110 		break;
2111 	case IPL_FIRE_ARROWS:
2112 		item[i]._iFlags |= ISPL_FIRE_ARROWS;
2113 		item[i]._iFlags &= ~ISPL_LIGHT_ARROWS;
2114 		item[i]._iFMinDam = param1;
2115 		item[i]._iFMaxDam = param2;
2116 		item[i]._iLMinDam = 0;
2117 		item[i]._iLMaxDam = 0;
2118 		break;
2119 	case IPL_LIGHT_ARROWS:
2120 		item[i]._iFlags |= ISPL_LIGHT_ARROWS;
2121 		item[i]._iFlags &= ~ISPL_FIRE_ARROWS;
2122 		item[i]._iLMinDam = param1;
2123 		item[i]._iLMaxDam = param2;
2124 		item[i]._iFMinDam = 0;
2125 		item[i]._iFMaxDam = 0;
2126 		break;
2127 	case IPL_FIREBALL:
2128 		item[i]._iFlags |= (ISPL_LIGHT_ARROWS | ISPL_FIRE_ARROWS);
2129 		item[i]._iFMinDam = param1;
2130 		item[i]._iFMaxDam = param2;
2131 		item[i]._iLMinDam = 0;
2132 		item[i]._iLMaxDam = 0;
2133 		break;
2134 	case IPL_THORNS:
2135 		item[i]._iFlags |= ISPL_THORNS;
2136 		break;
2137 	case IPL_NOMANA:
2138 		item[i]._iFlags |= ISPL_NOMANA;
2139 		drawmanaflag = TRUE;
2140 		break;
2141 	case IPL_NOHEALPLR:
2142 		item[i]._iFlags |= ISPL_NOHEALPLR;
2143 		break;
2144 	case IPL_ABSHALFTRAP:
2145 		item[i]._iFlags |= ISPL_ABSHALFTRAP;
2146 		break;
2147 	case IPL_KNOCKBACK:
2148 		item[i]._iFlags |= ISPL_KNOCKBACK;
2149 		break;
2150 	case IPL_3XDAMVDEM:
2151 		item[i]._iFlags |= ISPL_3XDAMVDEM;
2152 		break;
2153 	case IPL_ALLRESZERO:
2154 		item[i]._iFlags |= ISPL_ALLRESZERO;
2155 		break;
2156 	case IPL_NOHEALMON:
2157 		item[i]._iFlags |= ISPL_NOHEALMON;
2158 		break;
2159 	case IPL_STEALMANA:
2160 		if (param1 == 3)
2161 			item[i]._iFlags |= ISPL_STEALMANA_3;
2162 		if (param1 == 5)
2163 			item[i]._iFlags |= ISPL_STEALMANA_5;
2164 		drawmanaflag = TRUE;
2165 		break;
2166 	case IPL_STEALLIFE:
2167 		if (param1 == 3)
2168 			item[i]._iFlags |= ISPL_STEALLIFE_3;
2169 		if (param1 == 5)
2170 			item[i]._iFlags |= ISPL_STEALLIFE_5;
2171 		drawhpflag = TRUE;
2172 		break;
2173 	case IPL_TARGAC:
2174 		if (gbIsHellfire)
2175 			item[i]._iPLEnAc = param1;
2176 		else
2177 			item[i]._iPLEnAc += r;
2178 		break;
2179 	case IPL_FASTATTACK:
2180 		if (param1 == 1)
2181 			item[i]._iFlags |= ISPL_QUICKATTACK;
2182 		if (param1 == 2)
2183 			item[i]._iFlags |= ISPL_FASTATTACK;
2184 		if (param1 == 3)
2185 			item[i]._iFlags |= ISPL_FASTERATTACK;
2186 		if (param1 == 4)
2187 			item[i]._iFlags |= ISPL_FASTESTATTACK;
2188 		break;
2189 	case IPL_FASTRECOVER:
2190 		if (param1 == 1)
2191 			item[i]._iFlags |= ISPL_FASTRECOVER;
2192 		if (param1 == 2)
2193 			item[i]._iFlags |= ISPL_FASTERRECOVER;
2194 		if (param1 == 3)
2195 			item[i]._iFlags |= ISPL_FASTESTRECOVER;
2196 		break;
2197 	case IPL_FASTBLOCK:
2198 		item[i]._iFlags |= ISPL_FASTBLOCK;
2199 		break;
2200 	case IPL_DAMMOD:
2201 		item[i]._iPLDamMod += r;
2202 		break;
2203 	case IPL_RNDARROWVEL:
2204 		item[i]._iFlags |= ISPL_RNDARROWVEL;
2205 		break;
2206 	case IPL_SETDAM:
2207 		item[i]._iMinDam = param1;
2208 		item[i]._iMaxDam = param2;
2209 		break;
2210 	case IPL_SETDUR:
2211 		item[i]._iDurability = param1;
2212 		item[i]._iMaxDur = param1;
2213 		break;
2214 	case IPL_FASTSWING:
2215 		item[i]._iFlags |= ISPL_FASTERATTACK;
2216 		break;
2217 	case IPL_ONEHAND:
2218 		item[i]._iLoc = ILOC_ONEHAND;
2219 		break;
2220 	case IPL_DRAINLIFE:
2221 		item[i]._iFlags |= ISPL_DRAINLIFE;
2222 		break;
2223 	case IPL_RNDSTEALLIFE:
2224 		item[i]._iFlags |= ISPL_RNDSTEALLIFE;
2225 		break;
2226 	case IPL_INFRAVISION:
2227 		item[i]._iFlags |= ISPL_INFRAVISION;
2228 		break;
2229 	case IPL_NOMINSTR:
2230 		item[i]._iMinStr = 0;
2231 		break;
2232 	case IPL_INVCURS:
2233 		item[i]._iCurs = param1;
2234 		break;
2235 	case IPL_ADDACLIFE:
2236 		item[i]._iFlags |= (ISPL_LIGHT_ARROWS | ISPL_FIRE_ARROWS);
2237 		item[i]._iFMinDam = param1;
2238 		item[i]._iFMaxDam = param2;
2239 		item[i]._iLMinDam = 1;
2240 		item[i]._iLMaxDam = 0;
2241 		break;
2242 	case IPL_ADDMANAAC:
2243 		item[i]._iFlags |= (ISPL_LIGHTDAM | ISPL_FIREDAM);
2244 		item[i]._iFMinDam = param1;
2245 		item[i]._iFMaxDam = param2;
2246 		item[i]._iLMinDam = 2;
2247 		item[i]._iLMaxDam = 0;
2248 		break;
2249 	case IPL_FIRERESCLVL:
2250 		item[i]._iPLFR = 30 - plr[myplr]._pLevel;
2251 		if (item[i]._iPLFR < 0)
2252 			item[i]._iPLFR = 0;
2253 		break;
2254 	case IPL_FIRERES_CURSE:
2255 		item[i]._iPLFR -= r;
2256 		break;
2257 	case IPL_LIGHTRES_CURSE:
2258 		item[i]._iPLLR -= r;
2259 		break;
2260 	case IPL_MAGICRES_CURSE:
2261 		item[i]._iPLMR -= r;
2262 		break;
2263 	case IPL_ALLRES_CURSE:
2264 		item[i]._iPLFR -= r;
2265 		item[i]._iPLLR -= r;
2266 		item[i]._iPLMR -= r;
2267 		break;
2268 	case IPL_DEVASTATION:
2269 		item[i]._iDamAcFlags |= 0x01;
2270 		break;
2271 	case IPL_DECAY:
2272 		item[i]._iDamAcFlags |= 0x02;
2273 		item[i]._iPLDam += r;
2274 		break;
2275 	case IPL_PERIL:
2276 		item[i]._iDamAcFlags |= 0x04;
2277 		break;
2278 	case IPL_JESTERS:
2279 		item[i]._iDamAcFlags |= 0x08;
2280 		break;
2281 	case IPL_ACDEMON:
2282 		item[i]._iDamAcFlags |= 0x20;
2283 		break;
2284 	case IPL_ACUNDEAD:
2285 		item[i]._iDamAcFlags |= 0x40;
2286 		break;
2287 	case IPL_MANATOLIFE:
2288 		r2 = ((plr[myplr]._pMaxManaBase >> 6) * 50 / 100);
2289 		item[i]._iPLMana -= (r2 << 6);
2290 		item[i]._iPLHP += (r2 << 6);
2291 		break;
2292 	case IPL_LIFETOMANA:
2293 		r2 = ((plr[myplr]._pMaxHPBase >> 6) * 40 / 100);
2294 		item[i]._iPLHP -= (r2 << 6);
2295 		item[i]._iPLMana += (r2 << 6);
2296 		break;
2297 	}
2298 	if (item[i]._iVAdd1 || item[i]._iVMult1) {
2299 		item[i]._iVAdd2 = PLVal(r, param1, param2, minval, maxval);
2300 		item[i]._iVMult2 = multval;
2301 	} else {
2302 		item[i]._iVAdd1 = PLVal(r, param1, param2, minval, maxval);
2303 		item[i]._iVMult1 = multval;
2304 	}
2305 }
2306 
SaveItemSuffix(int i,int sufidx)2307 static void SaveItemSuffix(int i, int sufidx)
2308 {
2309 	int param1 = PL_Suffix[sufidx].PLParam1;
2310 	int param2 = PL_Suffix[sufidx].PLParam2;
2311 
2312 	if (!gbIsHellfire) {
2313 		if (sufidx >= 84 && sufidx <= 86) {
2314 			param1 = 2 << param1;
2315 			param2 = 6 << param2;
2316 		}
2317 	}
2318 
2319 	SaveItemPower(
2320 	    i,
2321 	    PL_Suffix[sufidx].PLPower,
2322 	    param1,
2323 	    param2,
2324 	    PL_Suffix[sufidx].PLMinVal,
2325 	    PL_Suffix[sufidx].PLMaxVal,
2326 	    PL_Suffix[sufidx].PLMultVal);
2327 }
2328 
GetItemPower(int i,int minlvl,int maxlvl,int flgs,BOOL onlygood)2329 void GetItemPower(int i, int minlvl, int maxlvl, int flgs, BOOL onlygood)
2330 {
2331 	int pre, post, nt, nl, j, preidx, sufidx;
2332 	int l[256];
2333 	char istr[128];
2334 	goodorevil goe;
2335 
2336 	pre = random_(23, 4);
2337 	post = random_(23, 3);
2338 	if (pre != 0 && post == 0) {
2339 		if (random_(23, 2) != 0)
2340 			post = 1;
2341 		else
2342 			pre = 0;
2343 	}
2344 	preidx = -1;
2345 	sufidx = -1;
2346 	goe = GOE_ANY;
2347 	if (!onlygood && random_(0, 3) != 0)
2348 		onlygood = TRUE;
2349 	if (pre == 0) {
2350 		nt = 0;
2351 		for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) {
2352 			if (IsPrefixValidForItemType(j, flgs)) {
2353 				if (PL_Prefix[j].PLMinLvl >= minlvl && PL_Prefix[j].PLMinLvl <= maxlvl && (!onlygood || PL_Prefix[j].PLOk) && (flgs != PLT_STAFF || PL_Prefix[j].PLPower != IPL_CHARGES)) {
2354 					l[nt] = j;
2355 					nt++;
2356 					if (PL_Prefix[j].PLDouble) {
2357 						l[nt] = j;
2358 						nt++;
2359 					}
2360 				}
2361 			}
2362 		}
2363 		if (nt != 0) {
2364 			preidx = l[random_(23, nt)];
2365 			sprintf(istr, "%s %s", PL_Prefix[preidx].PLName, item[i]._iIName);
2366 			strcpy(item[i]._iIName, istr);
2367 			item[i]._iMagical = ITEM_QUALITY_MAGIC;
2368 			SaveItemPower(
2369 			    i,
2370 			    PL_Prefix[preidx].PLPower,
2371 			    PL_Prefix[preidx].PLParam1,
2372 			    PL_Prefix[preidx].PLParam2,
2373 			    PL_Prefix[preidx].PLMinVal,
2374 			    PL_Prefix[preidx].PLMaxVal,
2375 			    PL_Prefix[preidx].PLMultVal);
2376 			item[i]._iPrePower = PL_Prefix[preidx].PLPower;
2377 			goe = PL_Prefix[preidx].PLGOE;
2378 		}
2379 	}
2380 	if (post != 0) {
2381 		nl = 0;
2382 		for (j = 0; PL_Suffix[j].PLPower != IPL_INVALID; j++) {
2383 			if (IsSuffixValidForItemType(j, flgs)
2384 			    && PL_Suffix[j].PLMinLvl >= minlvl && PL_Suffix[j].PLMinLvl <= maxlvl
2385 			    && !((goe == GOE_GOOD && PL_Suffix[j].PLGOE == GOE_EVIL) || (goe == GOE_EVIL && PL_Suffix[j].PLGOE == GOE_GOOD))
2386 			    && (!onlygood || PL_Suffix[j].PLOk)) {
2387 				l[nl] = j;
2388 				nl++;
2389 			}
2390 		}
2391 		if (nl != 0) {
2392 			sufidx = l[random_(23, nl)];
2393 			sprintf(istr, "%s of %s", item[i]._iIName, PL_Suffix[sufidx].PLName);
2394 			strcpy(item[i]._iIName, istr);
2395 			item[i]._iMagical = ITEM_QUALITY_MAGIC;
2396 			SaveItemSuffix(i, sufidx);
2397 			item[i]._iSufPower = PL_Suffix[sufidx].PLPower;
2398 		}
2399 	}
2400 	if (!control_WriteStringToBuffer((BYTE *)item[i]._iIName)) {
2401 		int aii = item[i].IDidx;
2402 		if (AllItemsList[aii].iSName)
2403 			strcpy(item[i]._iIName, AllItemsList[aii].iSName);
2404 		else
2405 			item[i]._iName[0] = 0;
2406 
2407 		if (preidx != -1) {
2408 			sprintf(istr, "%s %s", PL_Prefix[preidx].PLName, item[i]._iIName);
2409 			strcpy(item[i]._iIName, istr);
2410 		}
2411 		if (sufidx != -1) {
2412 			sprintf(istr, "%s of %s", item[i]._iIName, PL_Suffix[sufidx].PLName);
2413 			strcpy(item[i]._iIName, istr);
2414 		}
2415 	}
2416 	if (preidx != -1 || sufidx != -1)
2417 		CalcItemValue(i);
2418 }
2419 
GetItemBonus(int i,int idata,int minlvl,int maxlvl,BOOL onlygood,BOOLEAN allowspells)2420 void GetItemBonus(int i, int idata, int minlvl, int maxlvl, BOOL onlygood, BOOLEAN allowspells)
2421 {
2422 	if (minlvl > 25)
2423 		minlvl = 25;
2424 
2425 	switch (item[i]._itype) {
2426 	case ITYPE_SWORD:
2427 	case ITYPE_AXE:
2428 	case ITYPE_MACE:
2429 		GetItemPower(i, minlvl, maxlvl, PLT_WEAP, onlygood);
2430 		break;
2431 	case ITYPE_BOW:
2432 		GetItemPower(i, minlvl, maxlvl, PLT_BOW, onlygood);
2433 		break;
2434 	case ITYPE_SHIELD:
2435 		GetItemPower(i, minlvl, maxlvl, PLT_SHLD, onlygood);
2436 		break;
2437 	case ITYPE_LARMOR:
2438 	case ITYPE_HELM:
2439 	case ITYPE_MARMOR:
2440 	case ITYPE_HARMOR:
2441 		GetItemPower(i, minlvl, maxlvl, PLT_ARMO, onlygood);
2442 		break;
2443 	case ITYPE_STAFF:
2444 		if (allowspells)
2445 			GetStaffSpell(i, maxlvl, onlygood);
2446 		else
2447 			GetItemPower(i, minlvl, maxlvl, PLT_STAFF, onlygood);
2448 		break;
2449 	case ITYPE_RING:
2450 	case ITYPE_AMULET:
2451 		GetItemPower(i, minlvl, maxlvl, PLT_MISC, onlygood);
2452 		break;
2453 	case ITYPE_NONE:
2454 	case ITYPE_MISC:
2455 	case ITYPE_GOLD:
2456 		break;
2457 	}
2458 }
2459 
SetupItem(int i)2460 void SetupItem(int i)
2461 {
2462 	int it;
2463 
2464 	it = ItemCAnimTbl[item[i]._iCurs];
2465 	item[i]._iAnimData = itemanims[it];
2466 	item[i]._iAnimLen = ItemAnimLs[it];
2467 	item[i]._iAnimWidth = 96;
2468 	item[i]._iAnimWidth2 = 16;
2469 	item[i]._iIdentified = FALSE;
2470 	item[i]._iPostDraw = FALSE;
2471 
2472 	if (!plr[myplr].pLvlLoad) {
2473 		item[i]._iAnimFrame = 1;
2474 		item[i]._iAnimFlag = TRUE;
2475 		item[i]._iSelFlag = 0;
2476 	} else {
2477 		item[i]._iAnimFrame = item[i]._iAnimLen;
2478 		item[i]._iAnimFlag = FALSE;
2479 		item[i]._iSelFlag = 1;
2480 	}
2481 }
2482 
RndItem(int m)2483 int RndItem(int m)
2484 {
2485 	int i, ri, r;
2486 	int ril[512];
2487 
2488 	if ((monster[m].MData->mTreasure & 0x8000) != 0)
2489 		return -((monster[m].MData->mTreasure & 0xFFF) + 1);
2490 
2491 	if (monster[m].MData->mTreasure & 0x4000)
2492 		return 0;
2493 
2494 	if (random_(24, 100) > 40)
2495 		return 0;
2496 
2497 	if (random_(24, 100) > 25)
2498 		return IDI_GOLD + 1;
2499 
2500 	ri = 0;
2501 	for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
2502 		if (!IsItemAvailable(i))
2503 			continue;
2504 
2505 		if (AllItemsList[i].iRnd == IDROP_DOUBLE && monster[m].mLevel >= AllItemsList[i].iMinMLvl
2506 		    && ri < 512) {
2507 			ril[ri] = i;
2508 			ri++;
2509 		}
2510 		if (AllItemsList[i].iRnd != IDROP_NEVER && monster[m].mLevel >= AllItemsList[i].iMinMLvl
2511 		    && ri < 512) {
2512 			ril[ri] = i;
2513 			ri++;
2514 		}
2515 		if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer)
2516 			ri--;
2517 		if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer)
2518 			ri--;
2519 	}
2520 
2521 	r = random_(24, ri);
2522 	return ril[r] + 1;
2523 }
2524 
RndUItem(int m)2525 int RndUItem(int m)
2526 {
2527 	int i, ri;
2528 	int ril[512];
2529 	BOOL okflag;
2530 
2531 	if (m != -1 && (monster[m].MData->mTreasure & 0x8000) != 0 && !gbIsMultiplayer)
2532 		return -((monster[m].MData->mTreasure & 0xFFF) + 1);
2533 
2534 	int curlv = items_get_currlevel();
2535 	ri = 0;
2536 	for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
2537 		if (!IsItemAvailable(i))
2538 			continue;
2539 
2540 		okflag = TRUE;
2541 		if (AllItemsList[i].iRnd == IDROP_NEVER)
2542 			okflag = FALSE;
2543 		if (m != -1) {
2544 			if (monster[m].mLevel < AllItemsList[i].iMinMLvl)
2545 				okflag = FALSE;
2546 		} else {
2547 			if (2 * curlv < AllItemsList[i].iMinMLvl)
2548 				okflag = FALSE;
2549 		}
2550 		if (AllItemsList[i].itype == ITYPE_MISC)
2551 			okflag = FALSE;
2552 		if (AllItemsList[i].itype == ITYPE_GOLD)
2553 			okflag = FALSE;
2554 		if (AllItemsList[i].iMiscId == IMISC_BOOK)
2555 			okflag = TRUE;
2556 		if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer)
2557 			okflag = FALSE;
2558 		if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer)
2559 			okflag = FALSE;
2560 		if (okflag && ri < 512) {
2561 			ril[ri] = i;
2562 			ri++;
2563 		}
2564 	}
2565 
2566 	return ril[random_(25, ri)];
2567 }
2568 
RndAllItems()2569 int RndAllItems()
2570 {
2571 	int i, ri;
2572 	int ril[512];
2573 
2574 	if (random_(26, 100) > 25)
2575 		return 0;
2576 
2577 	int curlv = items_get_currlevel();
2578 	ri = 0;
2579 	for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
2580 		if (!IsItemAvailable(i))
2581 			continue;
2582 
2583 		if (AllItemsList[i].iRnd != IDROP_NEVER && 2 * curlv >= AllItemsList[i].iMinMLvl && ri < 512) {
2584 			ril[ri] = i;
2585 			ri++;
2586 		}
2587 		if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer)
2588 			ri--;
2589 		if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer)
2590 			ri--;
2591 	}
2592 
2593 	return ril[random_(26, ri)];
2594 }
2595 
RndTypeItems(int itype,int imid,int lvl)2596 int RndTypeItems(int itype, int imid, int lvl)
2597 {
2598 	int i, ri;
2599 	BOOL okflag;
2600 	int ril[512];
2601 
2602 	ri = 0;
2603 	for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
2604 		if (!IsItemAvailable(i))
2605 			continue;
2606 
2607 		okflag = TRUE;
2608 		if (AllItemsList[i].iRnd == IDROP_NEVER)
2609 			okflag = FALSE;
2610 		if (lvl << 1 < AllItemsList[i].iMinMLvl)
2611 			okflag = FALSE;
2612 		if (AllItemsList[i].itype != itype)
2613 			okflag = FALSE;
2614 		if (imid != -1 && AllItemsList[i].iMiscId != imid)
2615 			okflag = FALSE;
2616 		if (okflag && ri < 512) {
2617 			ril[ri] = i;
2618 			ri++;
2619 		}
2620 	}
2621 
2622 	return ril[random_(27, ri)];
2623 }
2624 
CheckUnique(int i,int lvl,int uper,BOOL recreate)2625 int CheckUnique(int i, int lvl, int uper, BOOL recreate)
2626 {
2627 	int j, idata, numu;
2628 	BOOLEAN uok[128];
2629 
2630 	if (random_(28, 100) > uper)
2631 		return UITYPE_INVALID;
2632 
2633 	numu = 0;
2634 	memset(uok, 0, sizeof(uok));
2635 	for (j = 0; UniqueItemList[j].UIItemId != UITYPE_INVALID; j++) {
2636 		if (!IsUniqueAvailable(j))
2637 			break;
2638 		if (UniqueItemList[j].UIItemId == AllItemsList[item[i].IDidx].iItemId
2639 		    && lvl >= UniqueItemList[j].UIMinLvl
2640 		    && (recreate || !UniqueItemFlag[j] || gbIsMultiplayer)) {
2641 			uok[j] = TRUE;
2642 			numu++;
2643 		}
2644 	}
2645 
2646 	if (numu == 0)
2647 		return UITYPE_INVALID;
2648 
2649 	random_(29, 10); /// BUGFIX: unused, last unique in array always gets chosen
2650 	idata = 0;
2651 	while (numu > 0) {
2652 		if (uok[idata])
2653 			numu--;
2654 		if (numu > 0) {
2655 			idata++;
2656 			if (idata == 128)
2657 				idata = 0;
2658 		}
2659 	}
2660 
2661 	return idata;
2662 }
2663 
GetUniqueItem(int i,int uid)2664 void GetUniqueItem(int i, int uid)
2665 {
2666 	UniqueItemFlag[uid] = TRUE;
2667 	SaveItemPower(i, UniqueItemList[uid].UIPower1, UniqueItemList[uid].UIParam1, UniqueItemList[uid].UIParam2, 0, 0, 1);
2668 
2669 	if (UniqueItemList[uid].UINumPL > 1)
2670 		SaveItemPower(i, UniqueItemList[uid].UIPower2, UniqueItemList[uid].UIParam3, UniqueItemList[uid].UIParam4, 0, 0, 1);
2671 	if (UniqueItemList[uid].UINumPL > 2)
2672 		SaveItemPower(i, UniqueItemList[uid].UIPower3, UniqueItemList[uid].UIParam5, UniqueItemList[uid].UIParam6, 0, 0, 1);
2673 	if (UniqueItemList[uid].UINumPL > 3)
2674 		SaveItemPower(i, UniqueItemList[uid].UIPower4, UniqueItemList[uid].UIParam7, UniqueItemList[uid].UIParam8, 0, 0, 1);
2675 	if (UniqueItemList[uid].UINumPL > 4)
2676 		SaveItemPower(i, UniqueItemList[uid].UIPower5, UniqueItemList[uid].UIParam9, UniqueItemList[uid].UIParam10, 0, 0, 1);
2677 	if (UniqueItemList[uid].UINumPL > 5)
2678 		SaveItemPower(i, UniqueItemList[uid].UIPower6, UniqueItemList[uid].UIParam11, UniqueItemList[uid].UIParam12, 0, 0, 1);
2679 
2680 	strcpy(item[i]._iIName, UniqueItemList[uid].UIName);
2681 	item[i]._iIvalue = UniqueItemList[uid].UIValue;
2682 
2683 	if (item[i]._iMiscId == IMISC_UNIQUE)
2684 		item[i]._iSeed = uid;
2685 
2686 	item[i]._iUid = uid;
2687 	item[i]._iMagical = ITEM_QUALITY_UNIQUE;
2688 	item[i]._iCreateInfo |= CF_UNIQUE;
2689 }
2690 
SpawnUnique(int uid,int x,int y)2691 void SpawnUnique(int uid, int x, int y)
2692 {
2693 	if (numitems >= MAXITEMS)
2694 		return;
2695 
2696 	int ii = AllocateItem();
2697 	GetSuperItemSpace(x, y, ii);
2698 	int curlv = items_get_currlevel();
2699 
2700 	int idx = 0;
2701 	while (AllItemsList[idx].iItemId != UniqueItemList[uid].UIItemId)
2702 		idx++;
2703 
2704 	GetItemAttrs(ii, idx, curlv);
2705 	GetUniqueItem(ii, uid);
2706 	SetupItem(ii);
2707 
2708 	return;
2709 }
2710 
ItemRndDur(int ii)2711 void ItemRndDur(int ii)
2712 {
2713 	if (item[ii]._iDurability && item[ii]._iDurability != DUR_INDESTRUCTIBLE)
2714 		item[ii]._iDurability = random_(0, item[ii]._iMaxDur >> 1) + (item[ii]._iMaxDur >> 2) + 1;
2715 }
2716 
SetupAllItems(int ii,int idx,int iseed,int lvl,int uper,BOOL onlygood,BOOL recreate,BOOL pregen)2717 void SetupAllItems(int ii, int idx, int iseed, int lvl, int uper, BOOL onlygood, BOOL recreate, BOOL pregen)
2718 {
2719 	int iblvl, uid;
2720 
2721 	item[ii]._iSeed = iseed;
2722 	SetRndSeed(iseed);
2723 	GetItemAttrs(ii, idx, lvl >> 1);
2724 	item[ii]._iCreateInfo = lvl;
2725 
2726 	if (pregen)
2727 		item[ii]._iCreateInfo |= CF_PREGEN;
2728 	if (onlygood)
2729 		item[ii]._iCreateInfo |= CF_ONLYGOOD;
2730 
2731 	if (uper == 15)
2732 		item[ii]._iCreateInfo |= CF_UPER15;
2733 	else if (uper == 1)
2734 		item[ii]._iCreateInfo |= CF_UPER1;
2735 
2736 	if (item[ii]._iMiscId != IMISC_UNIQUE) {
2737 		iblvl = -1;
2738 		if (random_(32, 100) <= 10 || random_(33, 100) <= lvl) {
2739 			iblvl = lvl;
2740 		}
2741 		if (iblvl == -1 && item[ii]._iMiscId == IMISC_STAFF) {
2742 			iblvl = lvl;
2743 		}
2744 		if (iblvl == -1 && item[ii]._iMiscId == IMISC_RING) {
2745 			iblvl = lvl;
2746 		}
2747 		if (iblvl == -1 && item[ii]._iMiscId == IMISC_AMULET) {
2748 			iblvl = lvl;
2749 		}
2750 		if (onlygood)
2751 			iblvl = lvl;
2752 		if (uper == 15)
2753 			iblvl = lvl + 4;
2754 		if (iblvl != -1) {
2755 			uid = CheckUnique(ii, iblvl, uper, recreate);
2756 			if (uid == UITYPE_INVALID) {
2757 				GetItemBonus(ii, idx, iblvl >> 1, iblvl, onlygood, TRUE);
2758 			} else {
2759 				GetUniqueItem(ii, uid);
2760 			}
2761 		}
2762 		if (item[ii]._iMagical != ITEM_QUALITY_UNIQUE)
2763 			ItemRndDur(ii);
2764 	} else {
2765 		if (item[ii]._iLoc != ILOC_UNEQUIPABLE) {
2766 			GetUniqueItem(ii, iseed); // uid is stored in iseed for uniques
2767 		}
2768 	}
2769 	SetupItem(ii);
2770 }
2771 
SpawnItem(int m,int x,int y,BOOL sendmsg)2772 void SpawnItem(int m, int x, int y, BOOL sendmsg)
2773 {
2774 	int idx;
2775 	BOOL onlygood;
2776 
2777 	if (monster[m]._uniqtype || ((monster[m].MData->mTreasure & 0x8000) && gbIsMultiplayer)) {
2778 		idx = RndUItem(m);
2779 		if (idx < 0) {
2780 			SpawnUnique(-(idx + 1), x, y);
2781 			return;
2782 		}
2783 		onlygood = TRUE;
2784 	} else if (quests[Q_MUSHROOM]._qactive != QUEST_ACTIVE || quests[Q_MUSHROOM]._qvar1 != QS_MUSHGIVEN) {
2785 		idx = RndItem(m);
2786 		if (!idx)
2787 			return;
2788 		if (idx > 0) {
2789 			idx--;
2790 			onlygood = FALSE;
2791 		} else {
2792 			SpawnUnique(-(idx + 1), x, y);
2793 			return;
2794 		}
2795 	} else {
2796 		idx = IDI_BRAIN;
2797 		quests[Q_MUSHROOM]._qvar1 = QS_BRAINSPAWNED;
2798 	}
2799 
2800 	if (numitems >= MAXITEMS)
2801 		return;
2802 
2803 	int ii = AllocateItem();
2804 	GetSuperItemSpace(x, y, ii);
2805 	int upper = monster[m]._uniqtype ? 15 : 1;
2806 
2807 	int mLevel = monster[m].MData->mLevel;
2808 	if (!gbIsHellfire && monster[m].MType->mtype == MT_DIABLO)
2809 		mLevel -= 15;
2810 
2811 	SetupAllItems(ii, idx, AdvanceRndSeed(), mLevel, upper, onlygood, FALSE, FALSE);
2812 
2813 	if (sendmsg)
2814 		NetSendCmdDItem(FALSE, ii);
2815 }
2816 
SetupBaseItem(Sint32 x,Sint32 y,Sint32 idx,bool onlygood,bool sendmsg,bool delta)2817 static void SetupBaseItem(Sint32 x, Sint32 y, Sint32 idx, bool onlygood, bool sendmsg, bool delta)
2818 {
2819 	if (numitems >= MAXITEMS)
2820 		return;
2821 
2822 	int ii = AllocateItem();
2823 	GetSuperItemSpace(x, y, ii);
2824 	int curlv = items_get_currlevel();
2825 
2826 	SetupAllItems(ii, idx, AdvanceRndSeed(), 2 * curlv, 1, onlygood, FALSE, delta);
2827 
2828 	if (sendmsg)
2829 		NetSendCmdDItem(FALSE, ii);
2830 	if (delta)
2831 		DeltaAddItem(ii);
2832 }
2833 
CreateRndItem(int x,int y,BOOL onlygood,BOOL sendmsg,BOOL delta)2834 void CreateRndItem(int x, int y, BOOL onlygood, BOOL sendmsg, BOOL delta)
2835 {
2836 	int idx = onlygood ? RndUItem(-1) : RndAllItems();
2837 
2838 	SetupBaseItem(x, y, idx, onlygood, sendmsg, delta);
2839 }
2840 
SetupAllUseful(int ii,int iseed,int lvl)2841 void SetupAllUseful(int ii, int iseed, int lvl)
2842 {
2843 	int idx;
2844 
2845 	item[ii]._iSeed = iseed;
2846 	SetRndSeed(iseed);
2847 
2848 	if (gbIsHellfire) {
2849 		idx = random_(34, 7);
2850 		switch (idx) {
2851 		case 0:
2852 			idx = IDI_PORTAL;
2853 			if ((lvl <= 1))
2854 				idx = IDI_HEAL;
2855 			break;
2856 		case 1:
2857 		case 2:
2858 			idx = IDI_HEAL;
2859 			break;
2860 		case 3:
2861 			idx = IDI_PORTAL;
2862 			if ((lvl <= 1))
2863 				idx = IDI_MANA;
2864 			break;
2865 		case 4:
2866 		case 5:
2867 			idx = IDI_MANA;
2868 			break;
2869 		default:
2870 			idx = IDI_OIL;
2871 			break;
2872 		}
2873 	} else {
2874 		if (random_(34, 2) != 0)
2875 			idx = IDI_HEAL;
2876 		else
2877 			idx = IDI_MANA;
2878 
2879 		if (lvl > 1 && random_(34, 3) == 0)
2880 			idx = IDI_PORTAL;
2881 	}
2882 
2883 	GetItemAttrs(ii, idx, lvl);
2884 	item[ii]._iCreateInfo = lvl | CF_USEFUL;
2885 	SetupItem(ii);
2886 }
2887 
CreateRndUseful(int pnum,int x,int y,BOOL sendmsg)2888 void CreateRndUseful(int pnum, int x, int y, BOOL sendmsg)
2889 {
2890 	if (numitems >= MAXITEMS)
2891 		return;
2892 
2893 	int ii = AllocateItem();
2894 	GetSuperItemSpace(x, y, ii);
2895 	int curlv = items_get_currlevel();
2896 
2897 	SetupAllUseful(ii, AdvanceRndSeed(), curlv);
2898 	if (sendmsg)
2899 		NetSendCmdDItem(FALSE, ii);
2900 }
2901 
CreateTypeItem(int x,int y,BOOL onlygood,int itype,int imisc,BOOL sendmsg,BOOL delta)2902 void CreateTypeItem(int x, int y, BOOL onlygood, int itype, int imisc, BOOL sendmsg, BOOL delta)
2903 {
2904 	int idx;
2905 
2906 	int curlv = items_get_currlevel();
2907 	if (itype != ITYPE_GOLD)
2908 		idx = RndTypeItems(itype, imisc, curlv);
2909 	else
2910 		idx = IDI_GOLD;
2911 
2912 	SetupBaseItem(x, y, idx, onlygood, sendmsg, delta);
2913 }
2914 
RecreateItem(int ii,int idx,WORD icreateinfo,int iseed,int ivalue,bool isHellfire)2915 void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue, bool isHellfire)
2916 {
2917 	bool _gbIsHellfire = gbIsHellfire;
2918 	gbIsHellfire = isHellfire;
2919 
2920 	if (idx == IDI_GOLD) {
2921 		SetPlrHandItem(&item[ii], IDI_GOLD);
2922 		item[ii]._iSeed = iseed;
2923 		item[ii]._iCreateInfo = icreateinfo;
2924 		item[ii]._ivalue = ivalue;
2925 		SetPlrHandGoldCurs(&item[ii]);
2926 		gbIsHellfire = _gbIsHellfire;
2927 		return;
2928 	}
2929 
2930 	if (icreateinfo == 0) {
2931 		SetPlrHandItem(&item[ii], idx);
2932 		SetPlrHandSeed(&item[ii], iseed);
2933 		gbIsHellfire = _gbIsHellfire;
2934 		return;
2935 	}
2936 
2937 	if ((icreateinfo & CF_UNIQUE) == 0) {
2938 		if (icreateinfo & CF_TOWN) {
2939 			RecreateTownItem(ii, idx, icreateinfo, iseed, ivalue);
2940 			gbIsHellfire = _gbIsHellfire;
2941 			return;
2942 		}
2943 
2944 		if ((icreateinfo & CF_USEFUL) == CF_USEFUL) {
2945 			SetupAllUseful(ii, iseed, icreateinfo & CF_LEVEL);
2946 			gbIsHellfire = _gbIsHellfire;
2947 			return;
2948 		}
2949 	}
2950 
2951 	int level = icreateinfo & CF_LEVEL;
2952 
2953 	int uper = 0;
2954 	if (icreateinfo & CF_UPER1)
2955 		uper = 1;
2956 	if (icreateinfo & CF_UPER15)
2957 		uper = 15;
2958 
2959 	bool onlygood = (icreateinfo & CF_ONLYGOOD) != 0;
2960 	bool recreate = (icreateinfo & CF_UNIQUE) != 0;
2961 	bool pregen = (icreateinfo & CF_PREGEN) != 0;
2962 
2963 	SetupAllItems(ii, idx, iseed, level, uper, onlygood, recreate, pregen);
2964 	gbIsHellfire = _gbIsHellfire;
2965 }
2966 
RecreateEar(int ii,WORD ic,int iseed,int Id,int dur,int mdur,int ch,int mch,int ivalue,int ibuff)2967 void RecreateEar(int ii, WORD ic, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, int ibuff)
2968 {
2969 	SetPlrHandItem(&item[ii], IDI_EAR);
2970 	tempstr[0] = (ic >> 8) & 0x7F;
2971 	tempstr[1] = ic & 0x7F;
2972 	tempstr[2] = (iseed >> 24) & 0x7F;
2973 	tempstr[3] = (iseed >> 16) & 0x7F;
2974 	tempstr[4] = (iseed >> 8) & 0x7F;
2975 	tempstr[5] = iseed & 0x7F;
2976 	tempstr[6] = Id & 0x7F;
2977 	tempstr[7] = dur & 0x7F;
2978 	tempstr[8] = mdur & 0x7F;
2979 	tempstr[9] = ch & 0x7F;
2980 	tempstr[10] = mch & 0x7F;
2981 	tempstr[11] = (ivalue >> 8) & 0x7F;
2982 	tempstr[12] = (ibuff >> 24) & 0x7F;
2983 	tempstr[13] = (ibuff >> 16) & 0x7F;
2984 	tempstr[14] = (ibuff >> 8) & 0x7F;
2985 	tempstr[15] = ibuff & 0x7F;
2986 	tempstr[16] = '\0';
2987 	sprintf(item[ii]._iName, "Ear of %s", tempstr);
2988 	item[ii]._iCurs = ((ivalue >> 6) & 3) + ICURS_EAR_SORCERER;
2989 	item[ii]._ivalue = ivalue & 0x3F;
2990 	item[ii]._iCreateInfo = ic;
2991 	item[ii]._iSeed = iseed;
2992 }
2993 
items_427A72()2994 void items_427A72()
2995 {
2996 	PkItemStruct id;
2997 	BYTE *buffer;
2998 
2999 	if (CornerStone.activated) {
3000 		if (!CornerStone.item.isEmpty()) {
3001 			PackItem(&id, &CornerStone.item);
3002 			buffer = (BYTE *)&id;
3003 			for (int i = 0; i < sizeof(PkItemStruct); i++) {
3004 				sprintf(&sgOptions.Hellfire.szItem[i * 2], "%02X", buffer[i]);
3005 			}
3006 		} else {
3007 			sgOptions.Hellfire.szItem[0] = '\0';
3008 		}
3009 	}
3010 }
3011 
char2int(char input)3012 int char2int(char input)
3013 {
3014 	if (input >= '0' && input <= '9')
3015 		return input - '0';
3016 	if (input >= 'A' && input <= 'F')
3017 		return input - 'A' + 10;
3018 	return 0;
3019 }
3020 
hex2bin(const char * src,int bytes,char * target)3021 void hex2bin(const char *src, int bytes, char *target)
3022 {
3023 	for (int i = 0; i < bytes; i++, src += 2) {
3024 		target[i] = (char2int(*src) << 4) | char2int(src[1]);
3025 	}
3026 }
3027 
items_427ABA(int x,int y)3028 void items_427ABA(int x, int y)
3029 {
3030 	PkItemStruct PkSItem;
3031 
3032 	if (CornerStone.activated || x == 0 || y == 0) {
3033 		return;
3034 	}
3035 
3036 	CornerStone.item._itype = ITYPE_NONE;
3037 	CornerStone.activated = TRUE;
3038 	if (dItem[x][y]) {
3039 		int ii = dItem[x][y] - 1;
3040 		for (int i = 0; i < numitems; i++) {
3041 			if (itemactive[i] == ii) {
3042 				DeleteItem(ii, i);
3043 				break;
3044 			}
3045 		}
3046 		dItem[x][y] = 0;
3047 	}
3048 
3049 	if (strlen(sgOptions.Hellfire.szItem) < sizeof(PkItemStruct) * 2)
3050 		return;
3051 
3052 	hex2bin(sgOptions.Hellfire.szItem, sizeof(PkItemStruct), (char *)&PkSItem);
3053 
3054 	int ii = AllocateItem();
3055 
3056 	dItem[x][y] = ii + 1;
3057 
3058 	UnPackItem(&PkSItem, &item[ii], (PkSItem.dwBuff & CF_HELLFIRE) != 0);
3059 	item[ii]._ix = x;
3060 	item[ii]._iy = y;
3061 	RespawnItem(ii, FALSE);
3062 	CornerStone.item = item[ii];
3063 }
3064 
SpawnQuestItem(int itemid,int x,int y,int randarea,int selflag)3065 void SpawnQuestItem(int itemid, int x, int y, int randarea, int selflag)
3066 {
3067 	if (randarea) {
3068 		int tries = 0;
3069 		while (1) {
3070 			tries++;
3071 			if (tries > 1000 && randarea > 1)
3072 				randarea--;
3073 
3074 			x = random_(0, MAXDUNX);
3075 			y = random_(0, MAXDUNY);
3076 
3077 			bool failed = false;
3078 			for (int i = 0; i < randarea && !failed; i++) {
3079 				for (int j = 0; j < randarea && !failed; j++) {
3080 					failed = !ItemSpaceOk(i + x, j + y);
3081 				}
3082 			}
3083 			if (!failed)
3084 				break;
3085 		}
3086 	}
3087 
3088 	if (numitems >= MAXITEMS)
3089 		return;
3090 
3091 	int ii = AllocateItem();
3092 
3093 	item[ii]._ix = x;
3094 	item[ii]._iy = y;
3095 
3096 	dItem[x][y] = ii + 1;
3097 
3098 	int curlv = items_get_currlevel();
3099 	GetItemAttrs(ii, itemid, curlv);
3100 
3101 	SetupItem(ii);
3102 	item[ii]._iPostDraw = TRUE;
3103 	if (selflag) {
3104 		item[ii]._iSelFlag = selflag;
3105 		item[ii]._iAnimFrame = item[ii]._iAnimLen;
3106 		item[ii]._iAnimFlag = FALSE;
3107 	}
3108 }
3109 
SpawnRock()3110 void SpawnRock()
3111 {
3112 	if (numitems >= MAXITEMS)
3113 		return;
3114 
3115 	int oi;
3116 	bool ostand = false;
3117 	for (int i = 0; i < nobjects && !ostand; i++) {
3118 		oi = objectactive[i];
3119 		ostand = object[oi]._otype == OBJ_STAND;
3120 	}
3121 
3122 	if (!ostand)
3123 		return;
3124 
3125 	int ii = AllocateItem();
3126 
3127 	int xx = object[oi]._ox;
3128 	int yy = object[oi]._oy;
3129 	item[ii]._ix = xx;
3130 	item[ii]._iy = yy;
3131 	dItem[xx][item[ii]._iy] = ii + 1;
3132 	int curlv = items_get_currlevel();
3133 	GetItemAttrs(ii, IDI_ROCK, curlv);
3134 	SetupItem(ii);
3135 	item[ii]._iSelFlag = 2;
3136 	item[ii]._iPostDraw = TRUE;
3137 	item[ii]._iAnimFrame = 11;
3138 }
3139 
SpawnRewardItem(int itemid,int xx,int yy)3140 void SpawnRewardItem(int itemid, int xx, int yy)
3141 {
3142 	if (numitems >= MAXITEMS)
3143 		return;
3144 
3145 	int ii = AllocateItem();
3146 
3147 	item[ii]._ix = xx;
3148 	item[ii]._iy = yy;
3149 	dItem[xx][yy] = ii + 1;
3150 	int curlv = items_get_currlevel();
3151 	GetItemAttrs(ii, itemid, curlv);
3152 	SetupItem(ii);
3153 	item[ii]._iSelFlag = 2;
3154 	item[ii]._iPostDraw = TRUE;
3155 	item[ii]._iAnimFrame = 1;
3156 	item[ii]._iAnimFlag = TRUE;
3157 	item[ii]._iIdentified = TRUE;
3158 }
3159 
SpawnMapOfDoom(int xx,int yy)3160 void SpawnMapOfDoom(int xx, int yy)
3161 {
3162 	SpawnRewardItem(IDI_MAPOFDOOM, xx, yy);
3163 }
3164 
SpawnRuneBomb(int xx,int yy)3165 void SpawnRuneBomb(int xx, int yy)
3166 {
3167 	SpawnRewardItem(IDI_RUNEBOMB, xx, yy);
3168 }
3169 
SpawnTheodore(int xx,int yy)3170 void SpawnTheodore(int xx, int yy)
3171 {
3172 	SpawnRewardItem(IDI_THEODORE, xx, yy);
3173 }
3174 
RespawnItem(int i,BOOL FlipFlag)3175 void RespawnItem(int i, BOOL FlipFlag)
3176 {
3177 	int it;
3178 
3179 	it = ItemCAnimTbl[item[i]._iCurs];
3180 	item[i]._iAnimData = itemanims[it];
3181 	item[i]._iAnimLen = ItemAnimLs[it];
3182 	item[i]._iAnimWidth = 96;
3183 	item[i]._iAnimWidth2 = 16;
3184 	item[i]._iPostDraw = FALSE;
3185 	item[i]._iRequest = FALSE;
3186 	if (FlipFlag) {
3187 		item[i]._iAnimFrame = 1;
3188 		item[i]._iAnimFlag = TRUE;
3189 		item[i]._iSelFlag = 0;
3190 	} else {
3191 		item[i]._iAnimFrame = item[i]._iAnimLen;
3192 		item[i]._iAnimFlag = FALSE;
3193 		item[i]._iSelFlag = 1;
3194 	}
3195 
3196 	if (item[i]._iCurs == ICURS_MAGIC_ROCK) {
3197 		item[i]._iSelFlag = 1;
3198 		PlaySfxLoc(ItemDropSnds[it], item[i]._ix, item[i]._iy);
3199 	}
3200 	if (item[i]._iCurs == ICURS_TAVERN_SIGN)
3201 		item[i]._iSelFlag = 1;
3202 	if (item[i]._iCurs == ICURS_ANVIL_OF_FURY)
3203 		item[i]._iSelFlag = 1;
3204 }
3205 
DeleteItem(int ii,int i)3206 void DeleteItem(int ii, int i)
3207 {
3208 	itemavail[MAXITEMS - numitems] = ii;
3209 	numitems--;
3210 	if (numitems > 0 && i != numitems)
3211 		itemactive[i] = itemactive[numitems];
3212 }
3213 
ItemDoppel()3214 void ItemDoppel()
3215 {
3216 	int idoppelx;
3217 	ItemStruct *i;
3218 
3219 	if (gbIsMultiplayer) {
3220 		for (idoppelx = 16; idoppelx < 96; idoppelx++) {
3221 			if (dItem[idoppelx][idoppely]) {
3222 				i = &item[dItem[idoppelx][idoppely] - 1];
3223 				if (i->_ix != idoppelx || i->_iy != idoppely)
3224 					dItem[idoppelx][idoppely] = 0;
3225 			}
3226 		}
3227 		idoppely++;
3228 		if (idoppely == 96)
3229 			idoppely = 16;
3230 	}
3231 }
3232 
ProcessItems()3233 void ProcessItems()
3234 {
3235 	int i, ii;
3236 
3237 	for (i = 0; i < numitems; i++) {
3238 		ii = itemactive[i];
3239 		if (item[ii]._iAnimFlag) {
3240 			item[ii]._iAnimFrame++;
3241 			if (item[ii]._iCurs == ICURS_MAGIC_ROCK) {
3242 				if (item[ii]._iSelFlag == 1 && item[ii]._iAnimFrame == 11)
3243 					item[ii]._iAnimFrame = 1;
3244 				if (item[ii]._iSelFlag == 2 && item[ii]._iAnimFrame == 21)
3245 					item[ii]._iAnimFrame = 11;
3246 			} else {
3247 				if (item[ii]._iAnimFrame == item[ii]._iAnimLen >> 1)
3248 					PlaySfxLoc(ItemDropSnds[ItemCAnimTbl[item[ii]._iCurs]], item[ii]._ix, item[ii]._iy);
3249 
3250 				if (item[ii]._iAnimFrame >= item[ii]._iAnimLen) {
3251 					item[ii]._iAnimFrame = item[ii]._iAnimLen;
3252 					item[ii]._iAnimFlag = FALSE;
3253 					item[ii]._iSelFlag = 1;
3254 				}
3255 			}
3256 		}
3257 	}
3258 	ItemDoppel();
3259 }
3260 
FreeItemGFX()3261 void FreeItemGFX()
3262 {
3263 	for (int i = 0; i < ITEMTYPES; i++) {
3264 		MemFreeDbg(itemanims[i]);
3265 	}
3266 }
3267 
GetItemFrm(int i)3268 void GetItemFrm(int i)
3269 {
3270 	item[i]._iAnimData = itemanims[ItemCAnimTbl[item[i]._iCurs]];
3271 }
3272 
GetItemStr(int i)3273 void GetItemStr(int i)
3274 {
3275 	int nGold;
3276 
3277 	if (item[i]._itype != ITYPE_GOLD) {
3278 		if (item[i]._iIdentified)
3279 			strcpy(infostr, item[i]._iIName);
3280 		else
3281 			strcpy(infostr, item[i]._iName);
3282 
3283 		if (item[i]._iMagical == ITEM_QUALITY_MAGIC)
3284 			infoclr = COL_BLUE;
3285 		if (item[i]._iMagical == ITEM_QUALITY_UNIQUE)
3286 			infoclr = COL_GOLD;
3287 	} else {
3288 		nGold = item[i]._ivalue;
3289 		sprintf(infostr, "%i gold %s", nGold, get_pieces_str(nGold));
3290 	}
3291 }
3292 
CheckIdentify(int pnum,int cii)3293 void CheckIdentify(int pnum, int cii)
3294 {
3295 	ItemStruct *pi;
3296 
3297 	if (cii >= NUM_INVLOC)
3298 		pi = &plr[pnum].InvList[cii - NUM_INVLOC];
3299 	else
3300 		pi = &plr[pnum].InvBody[cii];
3301 
3302 	pi->_iIdentified = TRUE;
3303 	CalcPlrInv(pnum, TRUE);
3304 
3305 	if (pnum == myplr)
3306 		SetCursor_(CURSOR_HAND);
3307 }
3308 
RepairItem(ItemStruct * i,int lvl)3309 static void RepairItem(ItemStruct *i, int lvl)
3310 {
3311 	int rep, d;
3312 
3313 	if (i->_iDurability == i->_iMaxDur) {
3314 		return;
3315 	}
3316 
3317 	if (i->_iMaxDur <= 0) {
3318 		i->_itype = ITYPE_NONE;
3319 		return;
3320 	}
3321 
3322 	rep = 0;
3323 	do {
3324 		rep += lvl + random_(37, lvl);
3325 		d = i->_iMaxDur / (lvl + 9);
3326 		if (d < 1)
3327 			d = 1;
3328 		i->_iMaxDur = i->_iMaxDur - d;
3329 		if (!i->_iMaxDur) {
3330 			i->_itype = ITYPE_NONE;
3331 			return;
3332 		}
3333 	} while (rep + i->_iDurability < i->_iMaxDur);
3334 
3335 	i->_iDurability += rep;
3336 	if (i->_iDurability > i->_iMaxDur)
3337 		i->_iDurability = i->_iMaxDur;
3338 }
3339 
DoRepair(int pnum,int cii)3340 void DoRepair(int pnum, int cii)
3341 {
3342 	PlayerStruct *p;
3343 	ItemStruct *pi;
3344 
3345 	p = &plr[pnum];
3346 	PlaySfxLoc(IS_REPAIR, p->_px, p->_py);
3347 
3348 	if (cii >= NUM_INVLOC) {
3349 		pi = &p->InvList[cii - NUM_INVLOC];
3350 	} else {
3351 		pi = &p->InvBody[cii];
3352 	}
3353 
3354 	RepairItem(pi, p->_pLevel);
3355 	CalcPlrInv(pnum, TRUE);
3356 
3357 	if (pnum == myplr)
3358 		SetCursor_(CURSOR_HAND);
3359 }
3360 
RechargeItem(ItemStruct * i,int r)3361 static void RechargeItem(ItemStruct *i, int r)
3362 {
3363 	if (i->_iCharges != i->_iMaxCharges) {
3364 		do {
3365 			i->_iMaxCharges--;
3366 			if (i->_iMaxCharges == 0) {
3367 				return;
3368 			}
3369 			i->_iCharges += r;
3370 		} while (i->_iCharges < i->_iMaxCharges);
3371 		if (i->_iCharges > i->_iMaxCharges)
3372 			i->_iCharges = i->_iMaxCharges;
3373 	}
3374 }
3375 
DoRecharge(int pnum,int cii)3376 void DoRecharge(int pnum, int cii)
3377 {
3378 	PlayerStruct *p;
3379 	ItemStruct *pi;
3380 	int r;
3381 
3382 	p = &plr[pnum];
3383 	if (cii >= NUM_INVLOC) {
3384 		pi = &p->InvList[cii - NUM_INVLOC];
3385 	} else {
3386 		pi = &p->InvBody[cii];
3387 	}
3388 	if (pi->_itype == ITYPE_STAFF && pi->_iSpell != SPL_NULL) {
3389 		r = GetSpellBookLevel(pi->_iSpell);
3390 		r = random_(38, p->_pLevel / r) + 1;
3391 		RechargeItem(pi, r);
3392 		CalcPlrInv(pnum, TRUE);
3393 	}
3394 
3395 	if (pnum == myplr)
3396 		SetCursor_(CURSOR_HAND);
3397 }
3398 
OilItem(ItemStruct * x,PlayerStruct * p)3399 static BOOL OilItem(ItemStruct *x, PlayerStruct *p)
3400 {
3401 	int dur, r;
3402 
3403 	if (x->_iClass == ICLASS_MISC) {
3404 		return FALSE;
3405 	}
3406 	if (x->_iClass == ICLASS_GOLD) {
3407 		return FALSE;
3408 	}
3409 	if (x->_iClass == ICLASS_QUEST) {
3410 		return FALSE;
3411 	}
3412 
3413 	switch (p->_pOilType) {
3414 	case IMISC_OILACC:
3415 	case IMISC_OILMAST:
3416 	case IMISC_OILSHARP:
3417 		if (x->_iClass == ICLASS_ARMOR) {
3418 			return FALSE;
3419 		}
3420 		break;
3421 	case IMISC_OILDEATH:
3422 		if (x->_iClass == ICLASS_ARMOR) {
3423 			return FALSE;
3424 		}
3425 		if (x->_itype == ITYPE_BOW) {
3426 			return FALSE;
3427 		}
3428 		break;
3429 	case IMISC_OILHARD:
3430 	case IMISC_OILIMP:
3431 		if (x->_iClass == ICLASS_WEAPON) {
3432 			return FALSE;
3433 		}
3434 		break;
3435 	default:
3436 		break;
3437 	}
3438 
3439 	switch (p->_pOilType) {
3440 	case IMISC_OILACC:
3441 		if (x->_iPLToHit < 50) {
3442 			x->_iPLToHit += random_(68, 2) + 1;
3443 		}
3444 		break;
3445 	case IMISC_OILMAST:
3446 		if (x->_iPLToHit < 100) {
3447 			x->_iPLToHit += random_(68, 3) + 3;
3448 		}
3449 		break;
3450 	case IMISC_OILSHARP:
3451 		if (x->_iMaxDam - x->_iMinDam < 30) {
3452 			x->_iMaxDam = x->_iMaxDam + 1;
3453 		}
3454 		break;
3455 	case IMISC_OILDEATH:
3456 		if (x->_iMaxDam - x->_iMinDam < 30) {
3457 			x->_iMinDam = x->_iMinDam + 1;
3458 			x->_iMaxDam = x->_iMaxDam + 2;
3459 		}
3460 		break;
3461 	case IMISC_OILSKILL:
3462 		r = random_(68, 6) + 5;
3463 		if (x->_iMinStr > r) {
3464 			x->_iMinStr = x->_iMinStr - r;
3465 		} else {
3466 			x->_iMinStr = 0;
3467 		}
3468 		if (x->_iMinMag > r) {
3469 			x->_iMinMag = x->_iMinMag - r;
3470 		} else {
3471 			x->_iMinMag = 0;
3472 		}
3473 		if (x->_iMinDex > r) {
3474 			x->_iMinDex = x->_iMinDex - r;
3475 		} else {
3476 			x->_iMinDex = 0;
3477 		}
3478 		break;
3479 	case IMISC_OILBSMTH:
3480 		if (x->_iMaxDur != 255) {
3481 			if (x->_iDurability < x->_iMaxDur) {
3482 				dur = (x->_iMaxDur + 4) / 5 + x->_iDurability;
3483 				if (dur > x->_iMaxDur) {
3484 					dur = x->_iMaxDur;
3485 				}
3486 			} else {
3487 				if (x->_iMaxDur >= 100) {
3488 					return TRUE;
3489 				}
3490 				dur = x->_iMaxDur + 1;
3491 				x->_iMaxDur = dur;
3492 			}
3493 			x->_iDurability = dur;
3494 		}
3495 		break;
3496 	case IMISC_OILFORT:
3497 		if (x->_iMaxDur != 255 && x->_iMaxDur < 200) {
3498 			r = random_(68, 41) + 10;
3499 			x->_iMaxDur += r;
3500 			x->_iDurability += r;
3501 		}
3502 		break;
3503 	case IMISC_OILPERM:
3504 		x->_iDurability = 255;
3505 		x->_iMaxDur = 255;
3506 		break;
3507 	case IMISC_OILHARD:
3508 		if (x->_iAC < 60) {
3509 			x->_iAC += random_(68, 2) + 1;
3510 		}
3511 		break;
3512 	case IMISC_OILIMP:
3513 		if (x->_iAC < 120) {
3514 			x->_iAC += random_(68, 3) + 3;
3515 		}
3516 		break;
3517 	default:
3518 		return FALSE;
3519 	}
3520 	return TRUE;
3521 }
3522 
DoOil(int pnum,int cii)3523 void DoOil(int pnum, int cii)
3524 {
3525 	PlayerStruct *p = &plr[pnum];
3526 
3527 	if (cii >= NUM_INVLOC || cii == INVLOC_HEAD || (cii > INVLOC_AMULET && cii <= INVLOC_CHEST)) {
3528 		if (OilItem(&p->InvBody[cii], p)) {
3529 			CalcPlrInv(pnum, TRUE);
3530 			if (pnum == myplr) {
3531 				SetCursor_(CURSOR_HAND);
3532 			}
3533 		}
3534 	}
3535 }
3536 
PrintItemOil(char IDidx)3537 void PrintItemOil(char IDidx)
3538 {
3539 	switch (IDidx) {
3540 	case IMISC_OILACC:
3541 		strcpy(tempstr, "increases a weapon's");
3542 		AddPanelString(tempstr, TRUE);
3543 		strcpy(tempstr, "chance to hit");
3544 		AddPanelString(tempstr, TRUE);
3545 		break;
3546 	case IMISC_OILMAST:
3547 		strcpy(tempstr, "greatly increases a");
3548 		AddPanelString(tempstr, TRUE);
3549 		strcpy(tempstr, "weapon's chance to hit");
3550 		AddPanelString(tempstr, TRUE);
3551 		break;
3552 	case IMISC_OILSHARP:
3553 		strcpy(tempstr, "increases a weapon's");
3554 		AddPanelString(tempstr, TRUE);
3555 		strcpy(tempstr, "damage potential");
3556 		AddPanelString(tempstr, TRUE);
3557 		break;
3558 	case IMISC_OILDEATH:
3559 		strcpy(tempstr, "greatly increases a weapon's");
3560 		AddPanelString(tempstr, TRUE);
3561 		strcpy(tempstr, "damage potential - not bows");
3562 		AddPanelString(tempstr, TRUE);
3563 		break;
3564 	case IMISC_OILSKILL:
3565 		strcpy(tempstr, "reduces attributes needed");
3566 		AddPanelString(tempstr, TRUE);
3567 		strcpy(tempstr, "to use armor or weapons");
3568 		AddPanelString(tempstr, TRUE);
3569 		break;
3570 	case IMISC_OILBSMTH:
3571 		strcpy(tempstr, "restores 20% of an");
3572 		AddPanelString(tempstr, TRUE);
3573 		strcpy(tempstr, "item's durability");
3574 		AddPanelString(tempstr, TRUE);
3575 		break;
3576 	case IMISC_OILFORT:
3577 		strcpy(tempstr, "increases an item's");
3578 		AddPanelString(tempstr, TRUE);
3579 		strcpy(tempstr, "current and max durability");
3580 		AddPanelString(tempstr, TRUE);
3581 		break;
3582 	case IMISC_OILPERM:
3583 		strcpy(tempstr, "makes an item indestructible");
3584 		AddPanelString(tempstr, TRUE);
3585 		break;
3586 	case IMISC_OILHARD:
3587 		strcpy(tempstr, "increases the armor class");
3588 		AddPanelString(tempstr, TRUE);
3589 		strcpy(tempstr, "of armor and shields");
3590 		AddPanelString(tempstr, TRUE);
3591 		break;
3592 	case IMISC_OILIMP:
3593 		strcpy(tempstr, "greatly increases the armor");
3594 		AddPanelString(tempstr, TRUE);
3595 		strcpy(tempstr, "class of armor and shields");
3596 		AddPanelString(tempstr, TRUE);
3597 		break;
3598 	case IMISC_RUNEF:
3599 		strcpy(tempstr, "sets fire trap");
3600 		AddPanelString(tempstr, TRUE);
3601 		break;
3602 	case IMISC_RUNEL:
3603 		strcpy(tempstr, "sets lightning trap");
3604 		AddPanelString(tempstr, TRUE);
3605 		break;
3606 	case IMISC_GR_RUNEL:
3607 		strcpy(tempstr, "sets lightning trap");
3608 		AddPanelString(tempstr, TRUE);
3609 		break;
3610 	case IMISC_GR_RUNEF:
3611 		strcpy(tempstr, "sets fire trap");
3612 		AddPanelString(tempstr, TRUE);
3613 		break;
3614 	case IMISC_RUNES:
3615 		strcpy(tempstr, "sets petrification trap");
3616 		AddPanelString(tempstr, TRUE);
3617 		break;
3618 	case IMISC_FULLHEAL:
3619 		strcpy(tempstr, "fully recover life");
3620 		AddPanelString(tempstr, TRUE);
3621 		break;
3622 	case IMISC_HEAL:
3623 		strcpy(tempstr, "recover partial life");
3624 		AddPanelString(tempstr, TRUE);
3625 		break;
3626 	case IMISC_OLDHEAL:
3627 		strcpy(tempstr, "recover life");
3628 		AddPanelString(tempstr, TRUE);
3629 		break;
3630 	case IMISC_DEADHEAL:
3631 		strcpy(tempstr, "deadly heal");
3632 		AddPanelString(tempstr, TRUE);
3633 		break;
3634 	case IMISC_MANA:
3635 		strcpy(tempstr, "recover mana");
3636 		AddPanelString(tempstr, TRUE);
3637 		break;
3638 	case IMISC_FULLMANA:
3639 		strcpy(tempstr, "fully recover mana");
3640 		AddPanelString(tempstr, TRUE);
3641 		break;
3642 	case IMISC_ELIXSTR:
3643 		strcpy(tempstr, "increase strength");
3644 		AddPanelString(tempstr, TRUE);
3645 		break;
3646 	case IMISC_ELIXMAG:
3647 		strcpy(tempstr, "increase magic");
3648 		AddPanelString(tempstr, TRUE);
3649 		break;
3650 	case IMISC_ELIXDEX:
3651 		strcpy(tempstr, "increase dexterity");
3652 		AddPanelString(tempstr, TRUE);
3653 		break;
3654 	case IMISC_ELIXVIT:
3655 		strcpy(tempstr, "increase vitality");
3656 		AddPanelString(tempstr, TRUE);
3657 		break;
3658 	case IMISC_ELIXWEAK:
3659 		strcpy(tempstr, "decrease strength");
3660 		AddPanelString(tempstr, TRUE);
3661 		break;
3662 	case IMISC_ELIXDIS:
3663 		strcpy(tempstr, "decrease strength");
3664 		AddPanelString(tempstr, TRUE);
3665 		break;
3666 	case IMISC_ELIXCLUM:
3667 		strcpy(tempstr, "decrease dexterity");
3668 		AddPanelString(tempstr, TRUE);
3669 		break;
3670 	case IMISC_ELIXSICK:
3671 		strcpy(tempstr, "decrease vitality");
3672 		AddPanelString(tempstr, TRUE);
3673 		break;
3674 	case IMISC_REJUV:
3675 		strcpy(tempstr, "recover life and mana");
3676 		AddPanelString(tempstr, TRUE);
3677 		break;
3678 	case IMISC_FULLREJUV:
3679 		strcpy(tempstr, "fully recover life and mana");
3680 		AddPanelString(tempstr, TRUE);
3681 		break;
3682 	}
3683 }
3684 
PrintItemPower(char plidx,ItemStruct * x)3685 void PrintItemPower(char plidx, ItemStruct *x)
3686 {
3687 	switch (plidx) {
3688 	case IPL_TOHIT:
3689 	case IPL_TOHIT_CURSE:
3690 		sprintf(tempstr, "chance to hit: %+i%%", x->_iPLToHit);
3691 		break;
3692 	case IPL_DAMP:
3693 	case IPL_DAMP_CURSE:
3694 		sprintf(tempstr, "%+i%% damage", x->_iPLDam);
3695 		break;
3696 	case IPL_TOHIT_DAMP:
3697 	case IPL_TOHIT_DAMP_CURSE:
3698 		sprintf(tempstr, "to hit: %+i%%, %+i%% damage", x->_iPLToHit, x->_iPLDam);
3699 		break;
3700 	case IPL_ACP:
3701 	case IPL_ACP_CURSE:
3702 		sprintf(tempstr, "%+i%% armor", x->_iPLAC);
3703 		break;
3704 	case IPL_SETAC:
3705 		sprintf(tempstr, "armor class: %i", x->_iAC);
3706 		break;
3707 	case IPL_AC_CURSE:
3708 		sprintf(tempstr, "armor class: %i", x->_iAC);
3709 		break;
3710 	case IPL_FIRERES:
3711 	case IPL_FIRERES_CURSE:
3712 		if (x->_iPLFR < 75)
3713 			sprintf(tempstr, "Resist Fire: %+i%%", x->_iPLFR);
3714 		else
3715 			sprintf(tempstr, "Resist Fire: 75%% MAX");
3716 		break;
3717 	case IPL_LIGHTRES:
3718 	case IPL_LIGHTRES_CURSE:
3719 		if (x->_iPLLR < 75)
3720 			sprintf(tempstr, "Resist Lightning: %+i%%", x->_iPLLR);
3721 		else
3722 			sprintf(tempstr, "Resist Lightning: 75%% MAX");
3723 		break;
3724 	case IPL_MAGICRES:
3725 	case IPL_MAGICRES_CURSE:
3726 		if (x->_iPLMR < 75)
3727 			sprintf(tempstr, "Resist Magic: %+i%%", x->_iPLMR);
3728 		else
3729 			sprintf(tempstr, "Resist Magic: 75%% MAX");
3730 		break;
3731 	case IPL_ALLRES:
3732 	case IPL_ALLRES_CURSE:
3733 		if (x->_iPLFR < 75)
3734 			sprintf(tempstr, "Resist All: %+i%%", x->_iPLFR);
3735 		if (x->_iPLFR >= 75)
3736 			sprintf(tempstr, "Resist All: 75%% MAX");
3737 		break;
3738 	case IPL_SPLLVLADD:
3739 		if (x->_iSplLvlAdd == 1)
3740 			strcpy(tempstr, "spells are increased 1 level");
3741 		else if (x->_iSplLvlAdd > 1)
3742 			sprintf(tempstr, "spells are increased %i levels", x->_iSplLvlAdd);
3743 		else if (x->_iSplLvlAdd == -1)
3744 			strcpy(tempstr, "spells are decreased 1 level");
3745 		else if (x->_iSplLvlAdd < -1)
3746 			sprintf(tempstr, "spells are decreased %i levels", -x->_iSplLvlAdd);
3747 		else if (x->_iSplLvlAdd == 0)
3748 			strcpy(tempstr, "spell levels unchanged (?)");
3749 		break;
3750 	case IPL_CHARGES:
3751 		strcpy(tempstr, "Extra charges");
3752 		break;
3753 	case IPL_SPELL:
3754 		sprintf(tempstr, "%i %s charges", x->_iMaxCharges, spelldata[x->_iSpell].sNameText);
3755 		break;
3756 	case IPL_FIREDAM:
3757 		if (x->_iFMinDam == x->_iFMaxDam)
3758 			sprintf(tempstr, "Fire hit damage: %i", x->_iFMinDam);
3759 		else
3760 			sprintf(tempstr, "Fire hit damage: %i-%i", x->_iFMinDam, x->_iFMaxDam);
3761 		break;
3762 	case IPL_LIGHTDAM:
3763 		if (x->_iLMinDam == x->_iLMaxDam)
3764 			sprintf(tempstr, "Lightning hit damage: %i", x->_iLMinDam);
3765 		else
3766 			sprintf(tempstr, "Lightning hit damage: %i-%i", x->_iLMinDam, x->_iLMaxDam);
3767 		break;
3768 	case IPL_STR:
3769 	case IPL_STR_CURSE:
3770 		sprintf(tempstr, "%+i to strength", x->_iPLStr);
3771 		break;
3772 	case IPL_MAG:
3773 	case IPL_MAG_CURSE:
3774 		sprintf(tempstr, "%+i to magic", x->_iPLMag);
3775 		break;
3776 	case IPL_DEX:
3777 	case IPL_DEX_CURSE:
3778 		sprintf(tempstr, "%+i to dexterity", x->_iPLDex);
3779 		break;
3780 	case IPL_VIT:
3781 	case IPL_VIT_CURSE:
3782 		sprintf(tempstr, "%+i to vitality", x->_iPLVit);
3783 		break;
3784 	case IPL_ATTRIBS:
3785 	case IPL_ATTRIBS_CURSE:
3786 		sprintf(tempstr, "%+i to all attributes", x->_iPLStr);
3787 		break;
3788 	case IPL_GETHIT_CURSE:
3789 	case IPL_GETHIT:
3790 		sprintf(tempstr, "%+i damage from enemies", x->_iPLGetHit);
3791 		break;
3792 	case IPL_LIFE:
3793 	case IPL_LIFE_CURSE:
3794 		sprintf(tempstr, "Hit Points: %+i", x->_iPLHP >> 6);
3795 		break;
3796 	case IPL_MANA:
3797 	case IPL_MANA_CURSE:
3798 		sprintf(tempstr, "Mana: %+i", x->_iPLMana >> 6);
3799 		break;
3800 	case IPL_DUR:
3801 		strcpy(tempstr, "high durability");
3802 		break;
3803 	case IPL_DUR_CURSE:
3804 		strcpy(tempstr, "decreased durability");
3805 		break;
3806 	case IPL_INDESTRUCTIBLE:
3807 		strcpy(tempstr, "indestructible");
3808 		break;
3809 	case IPL_LIGHT:
3810 		sprintf(tempstr, "+%i%% light radius", 10 * x->_iPLLight);
3811 		break;
3812 	case IPL_LIGHT_CURSE:
3813 		sprintf(tempstr, "-%i%% light radius", -10 * x->_iPLLight);
3814 		break;
3815 	case IPL_MULT_ARROWS:
3816 		sprintf(tempstr, "multiple arrows per shot");
3817 		break;
3818 	case IPL_FIRE_ARROWS:
3819 		if (x->_iFMinDam == x->_iFMaxDam)
3820 			sprintf(tempstr, "fire arrows damage: %i", x->_iFMinDam);
3821 		else
3822 			sprintf(tempstr, "fire arrows damage: %i-%i", x->_iFMinDam, x->_iFMaxDam);
3823 		break;
3824 	case IPL_LIGHT_ARROWS:
3825 		if (x->_iLMinDam == x->_iLMaxDam)
3826 			sprintf(tempstr, "lightning arrows damage %i", x->_iLMinDam);
3827 		else
3828 			sprintf(tempstr, "lightning arrows damage %i-%i", x->_iLMinDam, x->_iLMaxDam);
3829 		break;
3830 	case IPL_FIREBALL:
3831 		if (x->_iFMinDam == x->_iFMaxDam)
3832 			sprintf(tempstr, "fireball damage: %i", x->_iFMinDam);
3833 		else
3834 			sprintf(tempstr, "fireball damage: %i-%i", x->_iFMinDam, x->_iFMaxDam);
3835 		break;
3836 	case IPL_THORNS:
3837 		strcpy(tempstr, "attacker takes 1-3 damage");
3838 		break;
3839 	case IPL_NOMANA:
3840 		strcpy(tempstr, "user loses all mana");
3841 		break;
3842 	case IPL_NOHEALPLR:
3843 		strcpy(tempstr, "you can't heal");
3844 		break;
3845 	case IPL_ABSHALFTRAP:
3846 		strcpy(tempstr, "absorbs half of trap damage");
3847 		break;
3848 	case IPL_KNOCKBACK:
3849 		strcpy(tempstr, "knocks target back");
3850 		break;
3851 	case IPL_3XDAMVDEM:
3852 		strcpy(tempstr, "+200% damage vs. demons");
3853 		break;
3854 	case IPL_ALLRESZERO:
3855 		strcpy(tempstr, "All Resistance equals 0");
3856 		break;
3857 	case IPL_NOHEALMON:
3858 		strcpy(tempstr, "hit monster doesn't heal");
3859 		break;
3860 	case IPL_STEALMANA:
3861 		if (x->_iFlags & ISPL_STEALMANA_3)
3862 			strcpy(tempstr, "hit steals 3% mana");
3863 		if (x->_iFlags & ISPL_STEALMANA_5)
3864 			strcpy(tempstr, "hit steals 5% mana");
3865 		break;
3866 	case IPL_STEALLIFE:
3867 		if (x->_iFlags & ISPL_STEALLIFE_3)
3868 			strcpy(tempstr, "hit steals 3% life");
3869 		if (x->_iFlags & ISPL_STEALLIFE_5)
3870 			strcpy(tempstr, "hit steals 5% life");
3871 		break;
3872 	case IPL_TARGAC:
3873 		strcpy(tempstr, "penetrates target's armor");
3874 		break;
3875 	case IPL_FASTATTACK:
3876 		if (x->_iFlags & ISPL_QUICKATTACK)
3877 			strcpy(tempstr, "quick attack");
3878 		if (x->_iFlags & ISPL_FASTATTACK)
3879 			strcpy(tempstr, "fast attack");
3880 		if (x->_iFlags & ISPL_FASTERATTACK)
3881 			strcpy(tempstr, "faster attack");
3882 		if (x->_iFlags & ISPL_FASTESTATTACK)
3883 			strcpy(tempstr, "fastest attack");
3884 		break;
3885 	case IPL_FASTRECOVER:
3886 		if (x->_iFlags & ISPL_FASTRECOVER)
3887 			strcpy(tempstr, "fast hit recovery");
3888 		if (x->_iFlags & ISPL_FASTERRECOVER)
3889 			strcpy(tempstr, "faster hit recovery");
3890 		if (x->_iFlags & ISPL_FASTESTRECOVER)
3891 			strcpy(tempstr, "fastest hit recovery");
3892 		break;
3893 	case IPL_FASTBLOCK:
3894 		strcpy(tempstr, "fast block");
3895 		break;
3896 	case IPL_DAMMOD:
3897 		sprintf(tempstr, "adds %i points to damage", x->_iPLDamMod);
3898 		break;
3899 	case IPL_RNDARROWVEL:
3900 		strcpy(tempstr, "fires random speed arrows");
3901 		break;
3902 	case IPL_SETDAM:
3903 		sprintf(tempstr, "unusual item damage");
3904 		break;
3905 	case IPL_SETDUR:
3906 		strcpy(tempstr, "altered durability");
3907 		break;
3908 	case IPL_FASTSWING:
3909 		strcpy(tempstr, "Faster attack swing");
3910 		break;
3911 	case IPL_ONEHAND:
3912 		strcpy(tempstr, "one handed sword");
3913 		break;
3914 	case IPL_DRAINLIFE:
3915 		strcpy(tempstr, "constantly lose hit points");
3916 		break;
3917 	case IPL_RNDSTEALLIFE:
3918 		strcpy(tempstr, "life stealing");
3919 		break;
3920 	case IPL_NOMINSTR:
3921 		strcpy(tempstr, "no strength requirement");
3922 		break;
3923 	case IPL_INFRAVISION:
3924 		strcpy(tempstr, "see with infravision");
3925 		break;
3926 	case IPL_INVCURS:
3927 		strcpy(tempstr, " ");
3928 		break;
3929 	case IPL_ADDACLIFE:
3930 		if (x->_iFMinDam == x->_iFMaxDam)
3931 			sprintf(tempstr, "lightning damage: %i", x->_iFMinDam);
3932 		else
3933 			sprintf(tempstr, "lightning damage: %i-%i", x->_iFMinDam, x->_iFMaxDam);
3934 		break;
3935 	case IPL_ADDMANAAC:
3936 		strcpy(tempstr, "charged bolts on hits");
3937 		break;
3938 	case IPL_FIRERESCLVL:
3939 		if (x->_iPLFR <= 0)
3940 			sprintf(tempstr, " ");
3941 		else if (x->_iPLFR >= 1)
3942 			sprintf(tempstr, "Resist Fire: %+i%%", x->_iPLFR);
3943 		break;
3944 	case IPL_DEVASTATION:
3945 		strcpy(tempstr, "occasional triple damage");
3946 		break;
3947 	case IPL_DECAY:
3948 		sprintf(tempstr, "decaying %+i%% damage", x->_iPLDam);
3949 		break;
3950 	case IPL_PERIL:
3951 		strcpy(tempstr, "2x dmg to monst, 1x to you");
3952 		break;
3953 	case IPL_JESTERS:
3954 		strcpy(tempstr, "Random 0 - 500% damage");
3955 		break;
3956 	case IPL_CRYSTALLINE:
3957 		sprintf(tempstr, "low dur, %+i%% damage", x->_iPLDam);
3958 		break;
3959 	case IPL_DOPPELGANGER:
3960 		sprintf(tempstr, "to hit: %+i%%, %+i%% damage", x->_iPLToHit, x->_iPLDam);
3961 		break;
3962 	case IPL_ACDEMON:
3963 		sprintf(tempstr, "extra AC vs demons");
3964 		break;
3965 	case IPL_ACUNDEAD:
3966 		sprintf(tempstr, "extra AC vs undead");
3967 		break;
3968 	case IPL_MANATOLIFE:
3969 		sprintf(tempstr, "50%% Mana moved to Health");
3970 		break;
3971 	case IPL_LIFETOMANA:
3972 		sprintf(tempstr, "40%% Health moved to Mana");
3973 		break;
3974 	default:
3975 		strcpy(tempstr, "Another ability (NW)");
3976 		break;
3977 	}
3978 }
3979 
DrawUTextBack(CelOutputBuffer out)3980 static void DrawUTextBack(CelOutputBuffer out)
3981 {
3982 	CelDrawTo(out, RIGHT_PANEL_X - SPANEL_WIDTH + 24, 327, pSTextBoxCels, 1, 271);
3983 	DrawHalfTransparentRectTo(out, RIGHT_PANEL_X - SPANEL_WIDTH + 27, 28, 265, 297);
3984 }
3985 
PrintUString(CelOutputBuffer out,int x,int y,BOOL cjustflag,const char * str,text_color col)3986 void PrintUString(CelOutputBuffer out, int x, int y, BOOL cjustflag, const char *str, text_color col)
3987 {
3988 	int len, width, sx, sy, i, k;
3989 	BYTE c;
3990 
3991 	sx = x + 32;
3992 	sy = y * 12 + 44;
3993 	len = strlen(str);
3994 	k = 0;
3995 	if (cjustflag) {
3996 		width = 0;
3997 		for (i = 0; i < len; i++)
3998 			width += fontkern[fontframe[gbFontTransTbl[(BYTE)str[i]]]] + 1;
3999 		if (width < 257)
4000 			k = (257 - width) >> 1;
4001 		sx += k;
4002 	}
4003 
4004 	for (i = 0; i < len; i++) {
4005 		c = fontframe[gbFontTransTbl[(BYTE)str[i]]];
4006 		k += fontkern[c] + 1;
4007 		if (c && k <= 257) {
4008 			PrintChar(out, sx, sy, c, col);
4009 		}
4010 		sx += fontkern[c] + 1;
4011 	}
4012 }
4013 
DrawULine(CelOutputBuffer out,int y)4014 static void DrawULine(CelOutputBuffer out, int y)
4015 {
4016 	BYTE *src = out.at(26 + RIGHT_PANEL - SPANEL_WIDTH, 25);
4017 	BYTE *dst = out.at(26 + RIGHT_PANEL_X - SPANEL_WIDTH, y * 12 + 38);
4018 
4019 	for (int i = 0; i < 3; i++, src += out.pitch(), dst += out.pitch())
4020 		memcpy(dst, src, 267); // BUGFIX: should be 267 (fixed)
4021 }
4022 
DrawUniqueInfo(CelOutputBuffer out)4023 void DrawUniqueInfo(CelOutputBuffer out)
4024 {
4025 	int uid, y;
4026 
4027 	if ((!chrflag && !questlog) || gnScreenWidth >= SPANEL_WIDTH * 3) {
4028 		uid = curruitem._iUid;
4029 		DrawUTextBack(GlobalBackBuffer());
4030 		PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, 2, TRUE, UniqueItemList[uid].UIName, COL_GOLD);
4031 		DrawULine(out, 5);
4032 		PrintItemPower(UniqueItemList[uid].UIPower1, &curruitem);
4033 		y = 6 - UniqueItemList[uid].UINumPL + 8;
4034 		PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y, TRUE, tempstr, COL_WHITE);
4035 		if (UniqueItemList[uid].UINumPL > 1) {
4036 			PrintItemPower(UniqueItemList[uid].UIPower2, &curruitem);
4037 			PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 2, TRUE, tempstr, COL_WHITE);
4038 		}
4039 		if (UniqueItemList[uid].UINumPL > 2) {
4040 			PrintItemPower(UniqueItemList[uid].UIPower3, &curruitem);
4041 			PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 4, TRUE, tempstr, COL_WHITE);
4042 		}
4043 		if (UniqueItemList[uid].UINumPL > 3) {
4044 			PrintItemPower(UniqueItemList[uid].UIPower4, &curruitem);
4045 			PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 6, TRUE, tempstr, COL_WHITE);
4046 		}
4047 		if (UniqueItemList[uid].UINumPL > 4) {
4048 			PrintItemPower(UniqueItemList[uid].UIPower5, &curruitem);
4049 			PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 8, TRUE, tempstr, COL_WHITE);
4050 		}
4051 		if (UniqueItemList[uid].UINumPL > 5) {
4052 			PrintItemPower(UniqueItemList[uid].UIPower6, &curruitem);
4053 			PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 10, TRUE, tempstr, COL_WHITE);
4054 		}
4055 	}
4056 }
4057 
PrintItemMisc(ItemStruct * x)4058 void PrintItemMisc(ItemStruct *x)
4059 {
4060 	if (x->_iMiscId == IMISC_SCROLL) {
4061 		strcpy(tempstr, "Right-click to read");
4062 		AddPanelString(tempstr, TRUE);
4063 	}
4064 	if (x->_iMiscId == IMISC_SCROLLT) {
4065 		strcpy(tempstr, "Right-click to read, then");
4066 		AddPanelString(tempstr, TRUE);
4067 		strcpy(tempstr, "left-click to target");
4068 		AddPanelString(tempstr, TRUE);
4069 	}
4070 	if (x->_iMiscId >= IMISC_USEFIRST && x->_iMiscId <= IMISC_USELAST) {
4071 		PrintItemOil(x->_iMiscId);
4072 		strcpy(tempstr, "Right-click to use");
4073 		AddPanelString(tempstr, TRUE);
4074 	}
4075 	if (x->_iMiscId > IMISC_OILFIRST && x->_iMiscId < IMISC_OILLAST) {
4076 		PrintItemOil(x->_iMiscId);
4077 		strcpy(tempstr, "Right click to use");
4078 		AddPanelString(tempstr, TRUE);
4079 	}
4080 	if (x->_iMiscId > IMISC_RUNEFIRST && x->_iMiscId < IMISC_RUNELAST) {
4081 		PrintItemOil(x->_iMiscId);
4082 		strcpy(tempstr, "Right click to use");
4083 		AddPanelString(tempstr, TRUE);
4084 	}
4085 	if (x->_iMiscId == IMISC_BOOK) {
4086 		strcpy(tempstr, "Right-click to read");
4087 		AddPanelString(tempstr, TRUE);
4088 	}
4089 	if (x->_iMiscId == IMISC_NOTE) {
4090 		strcpy(tempstr, "Right click to read");
4091 		AddPanelString(tempstr, TRUE);
4092 	}
4093 	if (x->_iMiscId == IMISC_MAPOFDOOM) {
4094 		strcpy(tempstr, "Right-click to view");
4095 		AddPanelString(tempstr, TRUE);
4096 	}
4097 	if (x->_iMiscId == IMISC_EAR) {
4098 		sprintf(tempstr, "Level: %i", x->_ivalue);
4099 		AddPanelString(tempstr, TRUE);
4100 	}
4101 	if (x->_iMiscId == IMISC_AURIC) {
4102 		sprintf(tempstr, "Doubles gold capacity");
4103 		AddPanelString(tempstr, TRUE);
4104 	}
4105 }
4106 
PrintItemInfo(ItemStruct * x)4107 static void PrintItemInfo(ItemStruct *x)
4108 {
4109 	PrintItemMisc(x);
4110 	Uint8 str = x->_iMinStr;
4111 	Uint8 dex = x->_iMinDex;
4112 	Uint8 mag = x->_iMinMag;
4113 	if (str != 0 || mag != 0 || dex != 0) {
4114 		strcpy(tempstr, "Required:");
4115 		if (str)
4116 			sprintf(tempstr + strlen(tempstr), " %i Str", str);
4117 		if (mag)
4118 			sprintf(tempstr + strlen(tempstr), " %i Mag", mag);
4119 		if (dex)
4120 			sprintf(tempstr + strlen(tempstr), " %i Dex", dex);
4121 		AddPanelString(tempstr, TRUE);
4122 	}
4123 	pinfoflag = TRUE;
4124 }
4125 
PrintItemDetails(ItemStruct * x)4126 void PrintItemDetails(ItemStruct *x)
4127 {
4128 	if (x->_iClass == ICLASS_WEAPON) {
4129 		if (x->_iMinDam == x->_iMaxDam) {
4130 			if (x->_iMaxDur == DUR_INDESTRUCTIBLE)
4131 				sprintf(tempstr, "damage: %i  Indestructible", x->_iMinDam);
4132 			else
4133 				sprintf(tempstr, "damage: %i  Dur: %i/%i", x->_iMinDam, x->_iDurability, x->_iMaxDur);
4134 		} else {
4135 			if (x->_iMaxDur == DUR_INDESTRUCTIBLE)
4136 				sprintf(tempstr, "damage: %i-%i  Indestructible", x->_iMinDam, x->_iMaxDam);
4137 			else
4138 				sprintf(tempstr, "damage: %i-%i  Dur: %i/%i", x->_iMinDam, x->_iMaxDam, x->_iDurability, x->_iMaxDur);
4139 		}
4140 		AddPanelString(tempstr, TRUE);
4141 	}
4142 	if (x->_iClass == ICLASS_ARMOR) {
4143 		if (x->_iMaxDur == DUR_INDESTRUCTIBLE)
4144 			sprintf(tempstr, "armor: %i  Indestructible", x->_iAC);
4145 		else
4146 			sprintf(tempstr, "armor: %i  Dur: %i/%i", x->_iAC, x->_iDurability, x->_iMaxDur);
4147 		AddPanelString(tempstr, TRUE);
4148 	}
4149 	if (x->_iMiscId == IMISC_STAFF && x->_iMaxCharges) {
4150 		if (x->_iMinDam == x->_iMaxDam)
4151 			sprintf(tempstr, "dam: %i  Dur: %i/%i", x->_iMinDam, x->_iDurability, x->_iMaxDur);
4152 		else
4153 			sprintf(tempstr, "dam: %i-%i  Dur: %i/%i", x->_iMinDam, x->_iMaxDam, x->_iDurability, x->_iMaxDur);
4154 		sprintf(tempstr, "Charges: %i/%i", x->_iCharges, x->_iMaxCharges);
4155 		AddPanelString(tempstr, TRUE);
4156 	}
4157 	if (x->_iPrePower != -1) {
4158 		PrintItemPower(x->_iPrePower, x);
4159 		AddPanelString(tempstr, TRUE);
4160 	}
4161 	if (x->_iSufPower != -1) {
4162 		PrintItemPower(x->_iSufPower, x);
4163 		AddPanelString(tempstr, TRUE);
4164 	}
4165 	if (x->_iMagical == ITEM_QUALITY_UNIQUE) {
4166 		AddPanelString("unique item", TRUE);
4167 		uitemflag = TRUE;
4168 		curruitem = *x;
4169 	}
4170 	PrintItemInfo(x);
4171 }
4172 
PrintItemDur(ItemStruct * x)4173 void PrintItemDur(ItemStruct *x)
4174 {
4175 	if (x->_iClass == ICLASS_WEAPON) {
4176 		if (x->_iMinDam == x->_iMaxDam) {
4177 			if (x->_iMaxDur == DUR_INDESTRUCTIBLE)
4178 				sprintf(tempstr, "damage: %i  Indestructible", x->_iMinDam);
4179 			else
4180 				sprintf(tempstr, "damage: %i  Dur: %i/%i", x->_iMinDam, x->_iDurability, x->_iMaxDur);
4181 		} else {
4182 			if (x->_iMaxDur == DUR_INDESTRUCTIBLE)
4183 				sprintf(tempstr, "damage: %i-%i  Indestructible", x->_iMinDam, x->_iMaxDam);
4184 			else
4185 				sprintf(tempstr, "damage: %i-%i  Dur: %i/%i", x->_iMinDam, x->_iMaxDam, x->_iDurability, x->_iMaxDur);
4186 		}
4187 		AddPanelString(tempstr, TRUE);
4188 		if (x->_iMiscId == IMISC_STAFF && x->_iMaxCharges) {
4189 			sprintf(tempstr, "Charges: %i/%i", x->_iCharges, x->_iMaxCharges);
4190 			AddPanelString(tempstr, TRUE);
4191 		}
4192 		if (x->_iMagical != ITEM_QUALITY_NORMAL)
4193 			AddPanelString("Not Identified", TRUE);
4194 	}
4195 	if (x->_iClass == ICLASS_ARMOR) {
4196 		if (x->_iMaxDur == DUR_INDESTRUCTIBLE)
4197 			sprintf(tempstr, "armor: %i  Indestructible", x->_iAC);
4198 		else
4199 			sprintf(tempstr, "armor: %i  Dur: %i/%i", x->_iAC, x->_iDurability, x->_iMaxDur);
4200 		AddPanelString(tempstr, TRUE);
4201 		if (x->_iMagical != ITEM_QUALITY_NORMAL)
4202 			AddPanelString("Not Identified", TRUE);
4203 		if (x->_iMiscId == IMISC_STAFF && x->_iMaxCharges) {
4204 			sprintf(tempstr, "Charges: %i/%i", x->_iCharges, x->_iMaxCharges);
4205 			AddPanelString(tempstr, TRUE);
4206 		}
4207 	}
4208 	if (x->_itype == ITYPE_RING || x->_itype == ITYPE_AMULET)
4209 		AddPanelString("Not Identified", TRUE);
4210 	PrintItemInfo(x);
4211 }
4212 
UseItem(int p,item_misc_id Mid,spell_id spl)4213 void UseItem(int p, item_misc_id Mid, spell_id spl)
4214 {
4215 	int l, j;
4216 
4217 	switch (Mid) {
4218 	case IMISC_HEAL:
4219 	case IMISC_FOOD:
4220 		j = plr[p]._pMaxHP >> 8;
4221 		l = ((j >> 1) + random_(39, j)) << 6;
4222 		if (plr[p]._pClass == PC_WARRIOR || plr[p]._pClass == PC_BARBARIAN)
4223 			l <<= 1;
4224 		if (plr[p]._pClass == PC_ROGUE || plr[p]._pClass == PC_MONK || plr[p]._pClass == PC_BARD)
4225 			l += l >> 1;
4226 		plr[p]._pHitPoints += l;
4227 		if (plr[p]._pHitPoints > plr[p]._pMaxHP)
4228 			plr[p]._pHitPoints = plr[p]._pMaxHP;
4229 		plr[p]._pHPBase += l;
4230 		if (plr[p]._pHPBase > plr[p]._pMaxHPBase)
4231 			plr[p]._pHPBase = plr[p]._pMaxHPBase;
4232 		drawhpflag = TRUE;
4233 		break;
4234 	case IMISC_FULLHEAL:
4235 		plr[p]._pHitPoints = plr[p]._pMaxHP;
4236 		plr[p]._pHPBase = plr[p]._pMaxHPBase;
4237 		drawhpflag = TRUE;
4238 		break;
4239 	case IMISC_MANA:
4240 		j = plr[p]._pMaxMana >> 8;
4241 		l = ((j >> 1) + random_(40, j)) << 6;
4242 		if (plr[p]._pClass == PC_SORCERER)
4243 			l <<= 1;
4244 		if (plr[p]._pClass == PC_ROGUE || plr[p]._pClass == PC_MONK || plr[p]._pClass == PC_BARD)
4245 			l += l >> 1;
4246 		if (!(plr[p]._pIFlags & ISPL_NOMANA)) {
4247 			plr[p]._pMana += l;
4248 			if (plr[p]._pMana > plr[p]._pMaxMana)
4249 				plr[p]._pMana = plr[p]._pMaxMana;
4250 			plr[p]._pManaBase += l;
4251 			if (plr[p]._pManaBase > plr[p]._pMaxManaBase)
4252 				plr[p]._pManaBase = plr[p]._pMaxManaBase;
4253 			drawmanaflag = TRUE;
4254 		}
4255 		break;
4256 	case IMISC_FULLMANA:
4257 		if (!(plr[p]._pIFlags & ISPL_NOMANA)) {
4258 			plr[p]._pMana = plr[p]._pMaxMana;
4259 			plr[p]._pManaBase = plr[p]._pMaxManaBase;
4260 			drawmanaflag = TRUE;
4261 		}
4262 		break;
4263 	case IMISC_ELIXSTR:
4264 		ModifyPlrStr(p, 1);
4265 		break;
4266 	case IMISC_ELIXMAG:
4267 		ModifyPlrMag(p, 1);
4268 		if (gbIsHellfire) {
4269 			plr[p]._pMana = plr[p]._pMaxMana;
4270 			plr[p]._pManaBase = plr[p]._pMaxManaBase;
4271 			drawmanaflag = TRUE;
4272 		}
4273 		break;
4274 	case IMISC_ELIXDEX:
4275 		ModifyPlrDex(p, 1);
4276 		break;
4277 	case IMISC_ELIXVIT:
4278 		ModifyPlrVit(p, 1);
4279 		if (gbIsHellfire) {
4280 			plr[p]._pHitPoints = plr[p]._pMaxHP;
4281 			plr[p]._pHPBase = plr[p]._pMaxHPBase;
4282 			drawhpflag = TRUE;
4283 		}
4284 		break;
4285 	case IMISC_REJUV:
4286 		j = plr[p]._pMaxHP >> 8;
4287 		l = ((j >> 1) + random_(39, j)) << 6;
4288 		if (plr[p]._pClass == PC_WARRIOR || plr[p]._pClass == PC_BARBARIAN)
4289 			l <<= 1;
4290 		if (plr[p]._pClass == PC_ROGUE)
4291 			l += l >> 1;
4292 		plr[p]._pHitPoints += l;
4293 		if (plr[p]._pHitPoints > plr[p]._pMaxHP)
4294 			plr[p]._pHitPoints = plr[p]._pMaxHP;
4295 		plr[p]._pHPBase += l;
4296 		if (plr[p]._pHPBase > plr[p]._pMaxHPBase)
4297 			plr[p]._pHPBase = plr[p]._pMaxHPBase;
4298 		drawhpflag = TRUE;
4299 		j = plr[p]._pMaxMana >> 8;
4300 		l = ((j >> 1) + random_(40, j)) << 6;
4301 		if (plr[p]._pClass == PC_SORCERER)
4302 			l <<= 1;
4303 		if (plr[p]._pClass == PC_ROGUE)
4304 			l += l >> 1;
4305 		if (!(plr[p]._pIFlags & ISPL_NOMANA)) {
4306 			plr[p]._pMana += l;
4307 			if (plr[p]._pMana > plr[p]._pMaxMana)
4308 				plr[p]._pMana = plr[p]._pMaxMana;
4309 			plr[p]._pManaBase += l;
4310 			if (plr[p]._pManaBase > plr[p]._pMaxManaBase)
4311 				plr[p]._pManaBase = plr[p]._pMaxManaBase;
4312 			drawmanaflag = TRUE;
4313 		}
4314 		break;
4315 	case IMISC_FULLREJUV:
4316 		plr[p]._pHitPoints = plr[p]._pMaxHP;
4317 		plr[p]._pHPBase = plr[p]._pMaxHPBase;
4318 		drawhpflag = TRUE;
4319 		if (!(plr[p]._pIFlags & ISPL_NOMANA)) {
4320 			plr[p]._pMana = plr[p]._pMaxMana;
4321 			plr[p]._pManaBase = plr[p]._pMaxManaBase;
4322 			drawmanaflag = TRUE;
4323 		}
4324 		break;
4325 	case IMISC_SCROLL:
4326 		if (spelldata[spl].sTargeted) {
4327 			plr[p]._pTSpell = spl;
4328 			plr[p]._pTSplType = RSPLTYPE_INVALID;
4329 			if (p == myplr)
4330 				NewCursor(CURSOR_TELEPORT);
4331 		} else {
4332 			ClrPlrPath(p);
4333 			plr[p]._pSpell = spl;
4334 			plr[p]._pSplType = RSPLTYPE_INVALID;
4335 			plr[p]._pSplFrom = 3;
4336 			plr[p].destAction = ACTION_SPELL;
4337 			plr[p].destParam1 = cursmx;
4338 			plr[p].destParam2 = cursmy;
4339 			if (p == myplr && spl == SPL_NOVA)
4340 				NetSendCmdLoc(TRUE, CMD_NOVA, cursmx, cursmy);
4341 		}
4342 		break;
4343 	case IMISC_SCROLLT:
4344 		if (spelldata[spl].sTargeted) {
4345 			plr[p]._pTSpell = spl;
4346 			plr[p]._pTSplType = RSPLTYPE_INVALID;
4347 			if (p == myplr)
4348 				NewCursor(CURSOR_TELEPORT);
4349 		} else {
4350 			ClrPlrPath(p);
4351 			plr[p]._pSpell = spl;
4352 			plr[p]._pSplType = RSPLTYPE_INVALID;
4353 			plr[p]._pSplFrom = 3;
4354 			plr[p].destAction = ACTION_SPELL;
4355 			plr[p].destParam1 = cursmx;
4356 			plr[p].destParam2 = cursmy;
4357 		}
4358 		break;
4359 	case IMISC_BOOK:
4360 		plr[p]._pMemSpells |= GetSpellBitmask(spl);
4361 		if (plr[p]._pSplLvl[spl] < MAX_SPELL_LEVEL)
4362 			plr[p]._pSplLvl[spl]++;
4363 		if (!(plr[p]._pIFlags & ISPL_NOMANA)) {
4364 			plr[p]._pMana += spelldata[spl].sManaCost << 6;
4365 			if (plr[p]._pMana > plr[p]._pMaxMana)
4366 				plr[p]._pMana = plr[p]._pMaxMana;
4367 			plr[p]._pManaBase += spelldata[spl].sManaCost << 6;
4368 			if (plr[p]._pManaBase > plr[p]._pMaxManaBase)
4369 				plr[p]._pManaBase = plr[p]._pMaxManaBase;
4370 		}
4371 		if (p == myplr)
4372 			CalcPlrBookVals(p);
4373 		drawmanaflag = TRUE;
4374 		break;
4375 	case IMISC_MAPOFDOOM:
4376 		doom_init();
4377 		break;
4378 	case IMISC_OILACC:
4379 	case IMISC_OILMAST:
4380 	case IMISC_OILSHARP:
4381 	case IMISC_OILDEATH:
4382 	case IMISC_OILSKILL:
4383 	case IMISC_OILBSMTH:
4384 	case IMISC_OILFORT:
4385 	case IMISC_OILPERM:
4386 	case IMISC_OILHARD:
4387 	case IMISC_OILIMP:
4388 		plr[p]._pOilType = Mid;
4389 		if (p != myplr) {
4390 			return;
4391 		}
4392 		if (sbookflag) {
4393 			sbookflag = FALSE;
4394 		}
4395 		if (!invflag) {
4396 			invflag = TRUE;
4397 		}
4398 		NewCursor(CURSOR_OIL);
4399 		break;
4400 	case IMISC_SPECELIX:
4401 		ModifyPlrStr(p, 3);
4402 		ModifyPlrMag(p, 3);
4403 		ModifyPlrDex(p, 3);
4404 		ModifyPlrVit(p, 3);
4405 		break;
4406 	case IMISC_RUNEF:
4407 		plr[p]._pTSpell = SPL_RUNEFIRE;
4408 		plr[p]._pTSplType = RSPLTYPE_INVALID;
4409 		if (p == myplr)
4410 			NewCursor(CURSOR_TELEPORT);
4411 		break;
4412 	case IMISC_RUNEL:
4413 		plr[p]._pTSpell = SPL_RUNELIGHT;
4414 		plr[p]._pTSplType = RSPLTYPE_INVALID;
4415 		if (p == myplr)
4416 			NewCursor(CURSOR_TELEPORT);
4417 		break;
4418 	case IMISC_GR_RUNEL:
4419 		plr[p]._pTSpell = SPL_RUNENOVA;
4420 		plr[p]._pTSplType = RSPLTYPE_INVALID;
4421 		if (p == myplr)
4422 			NewCursor(CURSOR_TELEPORT);
4423 		break;
4424 	case IMISC_GR_RUNEF:
4425 		plr[p]._pTSpell = SPL_RUNEIMMOLAT;
4426 		plr[p]._pTSplType = RSPLTYPE_INVALID;
4427 		if (p == myplr)
4428 			NewCursor(CURSOR_TELEPORT);
4429 		break;
4430 	case IMISC_RUNES:
4431 		plr[p]._pTSpell = SPL_RUNESTONE;
4432 		plr[p]._pTSplType = RSPLTYPE_INVALID;
4433 		if (p == myplr)
4434 			NewCursor(CURSOR_TELEPORT);
4435 		break;
4436 	default:
4437 		break;
4438 	}
4439 }
4440 
StoreStatOk(ItemStruct * h)4441 BOOL StoreStatOk(ItemStruct *h)
4442 {
4443 	BOOL sf;
4444 
4445 	sf = TRUE;
4446 	if (plr[myplr]._pStrength < h->_iMinStr)
4447 		sf = FALSE;
4448 	if (plr[myplr]._pMagic < h->_iMinMag)
4449 		sf = FALSE;
4450 	if (plr[myplr]._pDexterity < h->_iMinDex)
4451 		sf = FALSE;
4452 
4453 	return sf;
4454 }
4455 
SmithItemOk(int i)4456 BOOL SmithItemOk(int i)
4457 {
4458 	BOOL rv;
4459 
4460 	rv = TRUE;
4461 	if (AllItemsList[i].itype == ITYPE_MISC)
4462 		rv = FALSE;
4463 	if (AllItemsList[i].itype == ITYPE_GOLD)
4464 		rv = FALSE;
4465 	if (AllItemsList[i].itype == ITYPE_STAFF && (!gbIsHellfire || AllItemsList[i].iSpell))
4466 		rv = FALSE;
4467 	if (AllItemsList[i].itype == ITYPE_RING)
4468 		rv = FALSE;
4469 	if (AllItemsList[i].itype == ITYPE_AMULET)
4470 		rv = FALSE;
4471 
4472 	return rv;
4473 }
4474 
RndSmithItem(int lvl)4475 int RndSmithItem(int lvl)
4476 {
4477 	int i, ri;
4478 	int ril[512];
4479 
4480 	ri = 0;
4481 	for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
4482 		if (!IsItemAvailable(i))
4483 			continue;
4484 
4485 		if (AllItemsList[i].iRnd != IDROP_NEVER && SmithItemOk(i) && lvl >= AllItemsList[i].iMinMLvl
4486 		    && ri < 512) {
4487 			ril[ri] = i;
4488 			ri++;
4489 			if (AllItemsList[i].iRnd == IDROP_DOUBLE
4490 			    && ri < 512) {
4491 				ril[ri] = i;
4492 				ri++;
4493 			}
4494 		}
4495 	}
4496 
4497 	return ril[random_(50, ri)] + 1;
4498 }
4499 
BubbleSwapItem(ItemStruct * a,ItemStruct * b)4500 void BubbleSwapItem(ItemStruct *a, ItemStruct *b)
4501 {
4502 	ItemStruct h;
4503 
4504 	h = *a;
4505 	*a = *b;
4506 	*b = h;
4507 }
4508 
SortSmith()4509 void SortSmith()
4510 {
4511 	int j, k;
4512 	BOOL sorted;
4513 
4514 	j = 0;
4515 	while (!smithitem[j + 1].isEmpty()) {
4516 		j++;
4517 	}
4518 
4519 	sorted = FALSE;
4520 	while (j > 0 && !sorted) {
4521 		sorted = TRUE;
4522 		for (k = 0; k < j; k++) {
4523 			if (smithitem[k].IDidx > smithitem[k + 1].IDidx) {
4524 				BubbleSwapItem(&smithitem[k], &smithitem[k + 1]);
4525 				sorted = FALSE;
4526 			}
4527 		}
4528 		j--;
4529 	}
4530 }
4531 
SpawnSmith(int lvl)4532 void SpawnSmith(int lvl)
4533 {
4534 	int i, iCnt, idata;
4535 	int maxValue, maxItems;
4536 
4537 	ItemStruct holditem;
4538 	holditem = item[0];
4539 
4540 	if (gbIsHellfire) {
4541 		maxValue = 200000;
4542 		maxItems = 25;
4543 	} else {
4544 		maxValue = 140000;
4545 		maxItems = 20;
4546 	}
4547 
4548 	iCnt = random_(50, maxItems - 10) + 10;
4549 	for (i = 0; i < iCnt; i++) {
4550 		do {
4551 			memset(&item[0], 0, sizeof(*item));
4552 			item[0]._iSeed = AdvanceRndSeed();
4553 			SetRndSeed(item[0]._iSeed);
4554 			idata = RndSmithItem(lvl) - 1;
4555 			GetItemAttrs(0, idata, lvl);
4556 		} while (item[0]._iIvalue > maxValue);
4557 		smithitem[i] = item[0];
4558 		smithitem[i]._iCreateInfo = lvl | CF_SMITH;
4559 		smithitem[i]._iIdentified = TRUE;
4560 		smithitem[i]._iStatFlag = StoreStatOk(&smithitem[i]);
4561 	}
4562 	for (i = iCnt; i < SMITH_ITEMS; i++)
4563 		smithitem[i]._itype = ITYPE_NONE;
4564 
4565 	SortSmith();
4566 	item[0] = holditem;
4567 }
4568 
PremiumItemOk(int i)4569 BOOL PremiumItemOk(int i)
4570 {
4571 	BOOL rv;
4572 
4573 	rv = TRUE;
4574 	if (AllItemsList[i].itype == ITYPE_MISC)
4575 		rv = FALSE;
4576 	if (AllItemsList[i].itype == ITYPE_GOLD)
4577 		rv = FALSE;
4578 	if (!gbIsHellfire && AllItemsList[i].itype == ITYPE_STAFF)
4579 		rv = FALSE;
4580 
4581 	if (gbIsMultiplayer) {
4582 		if (AllItemsList[i].iMiscId == IMISC_OILOF)
4583 			rv = FALSE;
4584 		if (AllItemsList[i].itype == ITYPE_RING)
4585 			rv = FALSE;
4586 		if (AllItemsList[i].itype == ITYPE_AMULET)
4587 			rv = FALSE;
4588 	}
4589 
4590 	return rv;
4591 }
4592 
RndPremiumItem(int minlvl,int maxlvl)4593 int RndPremiumItem(int minlvl, int maxlvl)
4594 {
4595 	int i, ri;
4596 	int ril[512];
4597 
4598 	ri = 0;
4599 	for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
4600 		if (!IsItemAvailable(i))
4601 			continue;
4602 
4603 		if (AllItemsList[i].iRnd != IDROP_NEVER) {
4604 			if (PremiumItemOk(i)) {
4605 				if (AllItemsList[i].iMinMLvl >= minlvl && AllItemsList[i].iMinMLvl <= maxlvl && ri < 512) {
4606 					ril[ri] = i;
4607 					ri++;
4608 				}
4609 			}
4610 		}
4611 	}
4612 
4613 	return ril[random_(50, ri)] + 1;
4614 }
4615 
SpawnOnePremium(int i,int plvl,int myplr)4616 static void SpawnOnePremium(int i, int plvl, int myplr)
4617 {
4618 	int ivalue;
4619 	bool keepgoing = false;
4620 	ItemStruct holditem = item[0];
4621 
4622 	int strength = get_max_strength(plr[myplr]._pClass);
4623 	if (strength < plr[myplr]._pStrength) {
4624 		strength = plr[myplr]._pStrength;
4625 	}
4626 	strength *= 1.2;
4627 
4628 	int dexterity = get_max_dexterity(plr[myplr]._pClass);
4629 	if (dexterity < plr[myplr]._pDexterity) {
4630 		dexterity = plr[myplr]._pDexterity;
4631 	}
4632 	dexterity *= 1.2;
4633 
4634 	int magic = get_max_magic(plr[myplr]._pClass);
4635 	if (magic < plr[myplr]._pMagic) {
4636 		magic = plr[myplr]._pMagic;
4637 	}
4638 	magic *= 1.2;
4639 
4640 	if (plvl > 30)
4641 		plvl = 30;
4642 	if (plvl < 1)
4643 		plvl = 1;
4644 
4645 	int count = 0;
4646 
4647 	do {
4648 		keepgoing = false;
4649 		memset(&item[0], 0, sizeof(*item));
4650 		item[0]._iSeed = AdvanceRndSeed();
4651 		SetRndSeed(item[0]._iSeed);
4652 		int itype = RndPremiumItem(plvl >> 2, plvl) - 1;
4653 		GetItemAttrs(0, itype, plvl);
4654 		GetItemBonus(0, itype, plvl >> 1, plvl, TRUE, !gbIsHellfire);
4655 
4656 		if (!gbIsHellfire) {
4657 			if (item[0]._iIvalue > 140000) {
4658 				keepgoing = true; // prevent breaking the do/while loop too early by failing hellfire's condition in while
4659 				continue;
4660 			}
4661 			break;
4662 		}
4663 
4664 		switch (item[0]._itype) {
4665 		case ITYPE_LARMOR:
4666 		case ITYPE_MARMOR:
4667 		case ITYPE_HARMOR:
4668 			ivalue = get_armor_max_value(myplr);
4669 			break;
4670 		case ITYPE_SHIELD:
4671 			ivalue = get_shield_max_value(myplr);
4672 			break;
4673 		case ITYPE_AXE:
4674 			ivalue = get_axe_max_value(myplr);
4675 			break;
4676 		case ITYPE_BOW:
4677 			ivalue = get_bow_max_value(myplr);
4678 			break;
4679 		case ITYPE_MACE:
4680 			ivalue = get_mace_max_value(myplr);
4681 			break;
4682 		case ITYPE_SWORD:
4683 			ivalue = get_sword_max_value(myplr);
4684 			break;
4685 		case ITYPE_HELM:
4686 			ivalue = get_helm_max_value(myplr);
4687 			break;
4688 		case ITYPE_STAFF:
4689 			ivalue = get_staff_max_value(myplr);
4690 			break;
4691 		case ITYPE_RING:
4692 			ivalue = get_ring_max_value(myplr);
4693 			break;
4694 		case ITYPE_AMULET:
4695 			ivalue = get_amulet_max_value(myplr);
4696 			break;
4697 		default:
4698 			ivalue = 0;
4699 			break;
4700 		}
4701 		ivalue *= 0.8;
4702 
4703 		count++;
4704 	} while (keepgoing || ((item[0]._iIvalue > 200000
4705 	             || item[0]._iMinStr > strength
4706 	             || item[0]._iMinMag > magic
4707 	             || item[0]._iMinDex > dexterity
4708 	             || item[0]._iIvalue < ivalue)
4709 	    && count < 150));
4710 	premiumitem[i] = item[0];
4711 	premiumitem[i]._iCreateInfo = plvl | CF_SMITHPREMIUM;
4712 	premiumitem[i]._iIdentified = TRUE;
4713 	premiumitem[i]._iStatFlag = StoreStatOk(&premiumitem[i]);
4714 	item[0] = holditem;
4715 }
4716 
SpawnPremium(int pnum)4717 void SpawnPremium(int pnum)
4718 {
4719 	int i;
4720 
4721 	int lvl = plr[pnum]._pLevel;
4722 	int maxItems = gbIsHellfire ? SMITH_PREMIUM_ITEMS : 6;
4723 	if (numpremium < maxItems) {
4724 		for (i = 0; i < maxItems; i++) {
4725 			if (premiumitem[i].isEmpty()) {
4726 				int plvl = premiumlevel + (gbIsHellfire ? premiumLvlAddHellfire[i] : premiumlvladd[i]);
4727 				SpawnOnePremium(i, plvl, pnum);
4728 			}
4729 		}
4730 		numpremium = maxItems;
4731 	}
4732 	while (premiumlevel < lvl) {
4733 		premiumlevel++;
4734 		if (gbIsHellfire) {
4735 			premiumitem[0] = premiumitem[3];
4736 			premiumitem[1] = premiumitem[4];
4737 			premiumitem[2] = premiumitem[5];
4738 			premiumitem[3] = premiumitem[6];
4739 			premiumitem[4] = premiumitem[7];
4740 			premiumitem[5] = premiumitem[8];
4741 			premiumitem[6] = premiumitem[9];
4742 			premiumitem[7] = premiumitem[10];
4743 			premiumitem[8] = premiumitem[11];
4744 			premiumitem[9] = premiumitem[12];
4745 			SpawnOnePremium(10, premiumlevel + premiumLvlAddHellfire[10], pnum);
4746 			premiumitem[11] = premiumitem[13];
4747 			SpawnOnePremium(12, premiumlevel + premiumLvlAddHellfire[12], pnum);
4748 			premiumitem[13] = premiumitem[14];
4749 			SpawnOnePremium(14, premiumlevel + premiumLvlAddHellfire[14], pnum);
4750 		} else {
4751 			premiumitem[0] = premiumitem[2];
4752 			premiumitem[1] = premiumitem[3];
4753 			premiumitem[2] = premiumitem[4];
4754 			SpawnOnePremium(3, premiumlevel + premiumlvladd[3], pnum);
4755 			premiumitem[4] = premiumitem[5];
4756 			SpawnOnePremium(5, premiumlevel + premiumlvladd[5], pnum);
4757 		}
4758 	}
4759 }
4760 
WitchItemOk(int i)4761 BOOL WitchItemOk(int i)
4762 {
4763 	BOOL rv;
4764 
4765 	rv = FALSE;
4766 	if (AllItemsList[i].itype == ITYPE_MISC)
4767 		rv = TRUE;
4768 	if (AllItemsList[i].itype == ITYPE_STAFF)
4769 		rv = TRUE;
4770 	if (AllItemsList[i].iMiscId == IMISC_MANA)
4771 		rv = FALSE;
4772 	if (AllItemsList[i].iMiscId == IMISC_FULLMANA)
4773 		rv = FALSE;
4774 	if (AllItemsList[i].iSpell == SPL_TOWN)
4775 		rv = FALSE;
4776 	if (AllItemsList[i].iMiscId == IMISC_FULLHEAL)
4777 		rv = FALSE;
4778 	if (AllItemsList[i].iMiscId == IMISC_HEAL)
4779 		rv = FALSE;
4780 	if (AllItemsList[i].iMiscId > IMISC_OILFIRST && AllItemsList[i].iMiscId < IMISC_OILLAST)
4781 		rv = FALSE;
4782 	if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer)
4783 		rv = FALSE;
4784 	if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer)
4785 		rv = FALSE;
4786 
4787 	return rv;
4788 }
4789 
RndWitchItem(int lvl)4790 int RndWitchItem(int lvl)
4791 {
4792 	int i, ri;
4793 	int ril[512];
4794 
4795 	ri = 0;
4796 	for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
4797 		if (!IsItemAvailable(i))
4798 			continue;
4799 
4800 		if (AllItemsList[i].iRnd != IDROP_NEVER && WitchItemOk(i) && lvl >= AllItemsList[i].iMinMLvl
4801 		    && ri < 512) {
4802 
4803 			ril[ri] = i;
4804 			ri++;
4805 		}
4806 	}
4807 
4808 	return ril[random_(51, ri)] + 1;
4809 }
4810 
SortWitch()4811 void SortWitch()
4812 {
4813 	int j, k;
4814 	BOOL sorted;
4815 
4816 	j = 3;
4817 	while (!witchitem[j + 1].isEmpty()) {
4818 		j++;
4819 	}
4820 
4821 	sorted = FALSE;
4822 	while (j > 3 && !sorted) {
4823 		sorted = TRUE;
4824 		for (k = 3; k < j; k++) {
4825 			if (witchitem[k].IDidx > witchitem[k + 1].IDidx) {
4826 				BubbleSwapItem(&witchitem[k], &witchitem[k + 1]);
4827 				sorted = FALSE;
4828 			}
4829 		}
4830 		j--;
4831 	}
4832 }
4833 
WitchBookLevel(int ii)4834 void WitchBookLevel(int ii)
4835 {
4836 	int slvl;
4837 
4838 	if (witchitem[ii]._iMiscId == IMISC_BOOK) {
4839 		witchitem[ii]._iMinMag = spelldata[witchitem[ii]._iSpell].sMinInt;
4840 		slvl = plr[myplr]._pSplLvl[witchitem[ii]._iSpell];
4841 		while (slvl) {
4842 			witchitem[ii]._iMinMag += 20 * witchitem[ii]._iMinMag / 100;
4843 			slvl--;
4844 			if (witchitem[ii]._iMinMag + 20 * witchitem[ii]._iMinMag / 100 > 255) {
4845 				witchitem[ii]._iMinMag = 255;
4846 				slvl = 0;
4847 			}
4848 		}
4849 	}
4850 }
4851 
SpawnWitch(int lvl)4852 void SpawnWitch(int lvl)
4853 {
4854 	int i, j, iCnt;
4855 	int idata, maxlvl, maxValue;
4856 
4857 	j = 3;
4858 
4859 	memset(&item[0], 0, sizeof(*item));
4860 	GetItemAttrs(0, IDI_MANA, 1);
4861 	witchitem[0] = item[0];
4862 	witchitem[0]._iCreateInfo = lvl;
4863 	witchitem[0]._iStatFlag = TRUE;
4864 	memset(&item[0], 0, sizeof(*item));
4865 	GetItemAttrs(0, IDI_FULLMANA, 1);
4866 	witchitem[1] = item[0];
4867 	witchitem[1]._iCreateInfo = lvl;
4868 	witchitem[1]._iStatFlag = TRUE;
4869 	memset(&item[0], 0, sizeof(*item));
4870 	GetItemAttrs(0, IDI_PORTAL, 1);
4871 	witchitem[2] = item[0];
4872 	witchitem[2]._iCreateInfo = lvl;
4873 	witchitem[2]._iStatFlag = TRUE;
4874 
4875 	if (gbIsHellfire) {
4876 		iCnt = random_(51, WITCH_ITEMS - 10) + 10;
4877 		maxValue = 200000;
4878 
4879 		int bCnt;
4880 		int books = random_(3, 4);
4881 		for (i = 114, bCnt = 0; i <= 117 && bCnt < books; ++i) {
4882 			if (WitchItemOk(i)
4883 			    && lvl >= AllItemsList[i].iMinMLvl) {
4884 				memset(&item[0], 0, sizeof(*item));
4885 				item[0]._iSeed = AdvanceRndSeed();
4886 				SetRndSeed(item[0]._iSeed);
4887 				random_(0, 1);
4888 
4889 				GetItemAttrs(0, i, lvl);
4890 				witchitem[j] = item[0];
4891 				witchitem[j]._iCreateInfo = lvl | CF_WITCH;
4892 				witchitem[j]._iIdentified = TRUE;
4893 				WitchBookLevel(j);
4894 				witchitem[j]._iStatFlag = StoreStatOk(&witchitem[j]);
4895 				j++;
4896 				bCnt++;
4897 			}
4898 		}
4899 	} else {
4900 		iCnt = random_(51, WITCH_ITEMS - 12) + 10;
4901 		maxValue = 140000;
4902 	}
4903 
4904 	for (i = j; i < iCnt; i++) {
4905 		do {
4906 			memset(&item[0], 0, sizeof(*item));
4907 			item[0]._iSeed = AdvanceRndSeed();
4908 			SetRndSeed(item[0]._iSeed);
4909 			idata = RndWitchItem(lvl) - 1;
4910 			GetItemAttrs(0, idata, lvl);
4911 			maxlvl = -1;
4912 			if (random_(51, 100) <= 5)
4913 				maxlvl = 2 * lvl;
4914 			if (maxlvl == -1 && item[0]._iMiscId == IMISC_STAFF)
4915 				maxlvl = 2 * lvl;
4916 			if (maxlvl != -1)
4917 				GetItemBonus(0, idata, maxlvl >> 1, maxlvl, TRUE, TRUE);
4918 		} while (item[0]._iIvalue > maxValue);
4919 		witchitem[i] = item[0];
4920 		witchitem[i]._iCreateInfo = lvl | CF_WITCH;
4921 		witchitem[i]._iIdentified = TRUE;
4922 		WitchBookLevel(i);
4923 		witchitem[i]._iStatFlag = StoreStatOk(&witchitem[i]);
4924 	}
4925 
4926 	for (i = iCnt; i < WITCH_ITEMS; i++)
4927 		witchitem[i]._itype = ITYPE_NONE;
4928 
4929 	SortWitch();
4930 }
4931 
RndBoyItem(int lvl)4932 int RndBoyItem(int lvl)
4933 {
4934 	int i, ri;
4935 	int ril[512];
4936 
4937 	ri = 0;
4938 	for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
4939 		if (!IsItemAvailable(i))
4940 			continue;
4941 
4942 		if (AllItemsList[i].iRnd != IDROP_NEVER && PremiumItemOk(i) && lvl >= AllItemsList[i].iMinMLvl
4943 		    && ri < 512) {
4944 			ril[ri] = i;
4945 			ri++;
4946 		}
4947 	}
4948 
4949 	return ril[random_(49, ri)] + 1;
4950 }
4951 
SpawnBoy(int lvl)4952 void SpawnBoy(int lvl)
4953 {
4954 	int itype;
4955 
4956 	int ivalue;
4957 	bool keepgoing = false;
4958 	int count = 0;
4959 
4960 	int strength = get_max_strength(plr[myplr]._pClass);
4961 	int dexterity = get_max_dexterity(plr[myplr]._pClass);
4962 	int magic = get_max_magic(plr[myplr]._pClass);
4963 	plr_class pc = plr[myplr]._pClass;
4964 
4965 	if (strength < plr[myplr]._pStrength) {
4966 		strength = plr[myplr]._pStrength;
4967 	}
4968 	strength *= 1.2;
4969 
4970 	if (dexterity < plr[myplr]._pDexterity) {
4971 		dexterity = plr[myplr]._pDexterity;
4972 	}
4973 	dexterity *= 1.2;
4974 
4975 	if (magic < plr[myplr]._pMagic) {
4976 		magic = plr[myplr]._pMagic;
4977 	}
4978 	magic *= 1.2;
4979 
4980 	if (boylevel < (lvl >> 1) || boyitem.isEmpty()) {
4981 		do {
4982 			keepgoing = false;
4983 			memset(&item[0], 0, sizeof(*item));
4984 			item[0]._iSeed = AdvanceRndSeed();
4985 			SetRndSeed(item[0]._iSeed);
4986 			itype = RndBoyItem(lvl) - 1;
4987 			GetItemAttrs(0, itype, lvl);
4988 			GetItemBonus(0, itype, lvl, 2 * lvl, TRUE, TRUE);
4989 
4990 			if (!gbIsHellfire) {
4991 				if (item[0]._iIvalue > 90000) {
4992 					keepgoing = true; // prevent breaking the do/while loop too early by failing hellfire's condition in while
4993 					continue;
4994 				}
4995 				break;
4996 			}
4997 
4998 			ivalue = 0;
4999 
5000 			int itemType = item[0]._itype;
5001 
5002 			switch (itemType) {
5003 			case ITYPE_LARMOR:
5004 			case ITYPE_MARMOR:
5005 			case ITYPE_HARMOR:
5006 				ivalue = get_armor_max_value(myplr);
5007 				break;
5008 			case ITYPE_SHIELD:
5009 				ivalue = get_shield_max_value(myplr);
5010 				break;
5011 			case ITYPE_AXE:
5012 				ivalue = get_axe_max_value(myplr);
5013 				break;
5014 			case ITYPE_BOW:
5015 				ivalue = get_bow_max_value(myplr);
5016 				break;
5017 			case ITYPE_MACE:
5018 				ivalue = get_mace_max_value(myplr);
5019 				break;
5020 			case ITYPE_SWORD:
5021 				ivalue = get_sword_max_value(myplr);
5022 				break;
5023 			case ITYPE_HELM:
5024 				ivalue = get_helm_max_value(myplr);
5025 				break;
5026 			case ITYPE_STAFF:
5027 				ivalue = get_staff_max_value(myplr);
5028 				break;
5029 			case ITYPE_RING:
5030 				ivalue = get_ring_max_value(myplr);
5031 				break;
5032 			case ITYPE_AMULET:
5033 				ivalue = get_amulet_max_value(myplr);
5034 				break;
5035 			}
5036 			ivalue *= 0.8;
5037 
5038 			count++;
5039 
5040 			if (count < 200) {
5041 				switch (pc) {
5042 				case PC_WARRIOR:
5043 					if (itemType == ITYPE_BOW || itemType == ITYPE_STAFF)
5044 						ivalue = INT_MAX;
5045 					break;
5046 				case PC_ROGUE:
5047 					if (itemType == ITYPE_SWORD || itemType == ITYPE_STAFF || itemType == ITYPE_AXE || itemType == ITYPE_MACE || itemType == ITYPE_SHIELD)
5048 						ivalue = INT_MAX;
5049 					break;
5050 				case PC_SORCERER:
5051 					if (itemType == ITYPE_STAFF || itemType == ITYPE_AXE || itemType == ITYPE_BOW || itemType == ITYPE_MACE)
5052 						ivalue = INT_MAX;
5053 					break;
5054 				case PC_MONK:
5055 					if (itemType == ITYPE_BOW || itemType == ITYPE_MARMOR || itemType == ITYPE_SHIELD || itemType == ITYPE_MACE)
5056 						ivalue = INT_MAX;
5057 					break;
5058 				case PC_BARD:
5059 					if (itemType == ITYPE_AXE || itemType == ITYPE_MACE || itemType == ITYPE_STAFF)
5060 						ivalue = INT_MAX;
5061 					break;
5062 				case PC_BARBARIAN:
5063 					if (itemType == ITYPE_BOW || itemType == ITYPE_STAFF)
5064 						ivalue = INT_MAX;
5065 					break;
5066 				case NUM_CLASSES:
5067 					break;
5068 				}
5069 			}
5070 		} while (keepgoing || ((item[0]._iIvalue > 200000
5071 		             || item[0]._iMinStr > strength
5072 		             || item[0]._iMinMag > magic
5073 		             || item[0]._iMinDex > dexterity
5074 		             || item[0]._iIvalue < ivalue)
5075 		    && count < 250));
5076 		boyitem = item[0];
5077 		boyitem._iCreateInfo = lvl | CF_BOY;
5078 		boyitem._iIdentified = TRUE;
5079 		boyitem._iStatFlag = StoreStatOk(&boyitem);
5080 		boylevel = lvl >> 1;
5081 	}
5082 }
5083 
HealerItemOk(int i)5084 BOOL HealerItemOk(int i)
5085 {
5086 	if (AllItemsList[i].itype != ITYPE_MISC)
5087 		return FALSE;
5088 
5089 	if (AllItemsList[i].iMiscId == IMISC_SCROLL)
5090 		return AllItemsList[i].iSpell == SPL_HEAL;
5091 	if (AllItemsList[i].iMiscId == IMISC_SCROLLT)
5092 		return AllItemsList[i].iSpell == SPL_HEALOTHER && gbIsMultiplayer;
5093 
5094 	if (!gbIsMultiplayer) {
5095 		if (AllItemsList[i].iMiscId == IMISC_ELIXSTR)
5096 			return !gbIsHellfire || plr[myplr]._pBaseStr < MaxStats[plr[myplr]._pClass][ATTRIB_STR];
5097 		if (AllItemsList[i].iMiscId == IMISC_ELIXMAG)
5098 			return !gbIsHellfire || plr[myplr]._pBaseMag < MaxStats[plr[myplr]._pClass][ATTRIB_MAG];
5099 		if (AllItemsList[i].iMiscId == IMISC_ELIXDEX)
5100 			return !gbIsHellfire || plr[myplr]._pBaseDex < MaxStats[plr[myplr]._pClass][ATTRIB_DEX];
5101 		if (AllItemsList[i].iMiscId == IMISC_ELIXVIT)
5102 			return !gbIsHellfire || plr[myplr]._pBaseVit < MaxStats[plr[myplr]._pClass][ATTRIB_VIT];
5103 	}
5104 
5105 	if (AllItemsList[i].iMiscId == IMISC_REJUV)
5106 		return TRUE;
5107 	if (AllItemsList[i].iMiscId == IMISC_FULLREJUV)
5108 		return TRUE;
5109 
5110 	return FALSE;
5111 }
5112 
RndHealerItem(int lvl)5113 int RndHealerItem(int lvl)
5114 {
5115 	int i, ri;
5116 	int ril[512];
5117 
5118 	ri = 0;
5119 	for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) {
5120 		if (!IsItemAvailable(i))
5121 			continue;
5122 
5123 		if (AllItemsList[i].iRnd != IDROP_NEVER && HealerItemOk(i) && lvl >= AllItemsList[i].iMinMLvl
5124 		    && ri < 512) {
5125 			ril[ri] = i;
5126 			ri++;
5127 		}
5128 	}
5129 
5130 	return ril[random_(50, ri)] + 1;
5131 }
5132 
SortHealer()5133 void SortHealer()
5134 {
5135 	int j, k;
5136 	BOOL sorted;
5137 
5138 	j = 2;
5139 	while (!healitem[j + 1].isEmpty()) {
5140 		j++;
5141 	}
5142 
5143 	sorted = FALSE;
5144 	while (j > 2 && !sorted) {
5145 		sorted = TRUE;
5146 		for (k = 2; k < j; k++) {
5147 			if (healitem[k].IDidx > healitem[k + 1].IDidx) {
5148 				BubbleSwapItem(&healitem[k], &healitem[k + 1]);
5149 				sorted = FALSE;
5150 			}
5151 		}
5152 		j--;
5153 	}
5154 }
5155 
SpawnHealer(int lvl)5156 void SpawnHealer(int lvl)
5157 {
5158 	int i, nsi, srnd, itype;
5159 
5160 	memset(&item[0], 0, sizeof(*item));
5161 	GetItemAttrs(0, IDI_HEAL, 1);
5162 	healitem[0] = item[0];
5163 	healitem[0]._iCreateInfo = lvl;
5164 	healitem[0]._iStatFlag = TRUE;
5165 
5166 	memset(&item[0], 0, sizeof(*item));
5167 	GetItemAttrs(0, IDI_FULLHEAL, 1);
5168 	healitem[1] = item[0];
5169 	healitem[1]._iCreateInfo = lvl;
5170 	healitem[1]._iStatFlag = TRUE;
5171 
5172 	if (gbIsMultiplayer) {
5173 		memset(&item[0], 0, sizeof(*item));
5174 		GetItemAttrs(0, IDI_RESURRECT, 1);
5175 		healitem[2] = item[0];
5176 		healitem[2]._iCreateInfo = lvl;
5177 		healitem[2]._iStatFlag = TRUE;
5178 
5179 		srnd = 3;
5180 	} else {
5181 		srnd = 2;
5182 	}
5183 	nsi = random_(50, gbIsHellfire ? 10 : 8) + 10;
5184 	for (i = srnd; i < nsi; i++) {
5185 		memset(&item[0], 0, sizeof(*item));
5186 		item[0]._iSeed = AdvanceRndSeed();
5187 		SetRndSeed(item[0]._iSeed);
5188 		itype = RndHealerItem(lvl) - 1;
5189 		GetItemAttrs(0, itype, lvl);
5190 		healitem[i] = item[0];
5191 		healitem[i]._iCreateInfo = lvl | CF_HEALER;
5192 		healitem[i]._iIdentified = TRUE;
5193 		healitem[i]._iStatFlag = StoreStatOk(&healitem[i]);
5194 	}
5195 	for (i = nsi; i < 20; i++) {
5196 		healitem[i]._itype = ITYPE_NONE;
5197 	}
5198 	SortHealer();
5199 }
5200 
SpawnStoreGold()5201 void SpawnStoreGold()
5202 {
5203 	memset(&item[0], 0, sizeof(*item));
5204 	GetItemAttrs(0, IDI_GOLD, 1);
5205 	golditem = item[0];
5206 	golditem._iStatFlag = TRUE;
5207 }
5208 
RecreateSmithItem(int ii,int idx,int lvl,int iseed)5209 void RecreateSmithItem(int ii, int idx, int lvl, int iseed)
5210 {
5211 	SetRndSeed(iseed);
5212 	int itype = RndSmithItem(lvl) - 1;
5213 	GetItemAttrs(ii, itype, lvl);
5214 
5215 	item[ii]._iSeed = iseed;
5216 	item[ii]._iCreateInfo = lvl | CF_SMITH;
5217 	item[ii]._iIdentified = TRUE;
5218 }
5219 
RecreatePremiumItem(int ii,int idx,int plvl,int iseed)5220 void RecreatePremiumItem(int ii, int idx, int plvl, int iseed)
5221 {
5222 	SetRndSeed(iseed);
5223 	int itype = RndPremiumItem(plvl >> 2, plvl) - 1;
5224 	GetItemAttrs(ii, itype, plvl);
5225 	GetItemBonus(ii, itype, plvl >> 1, plvl, TRUE, !gbIsHellfire);
5226 
5227 	item[ii]._iSeed = iseed;
5228 	item[ii]._iCreateInfo = plvl | CF_SMITHPREMIUM;
5229 	item[ii]._iIdentified = TRUE;
5230 }
5231 
RecreateBoyItem(int ii,int idx,int lvl,int iseed)5232 void RecreateBoyItem(int ii, int idx, int lvl, int iseed)
5233 {
5234 	SetRndSeed(iseed);
5235 	int itype = RndBoyItem(lvl) - 1;
5236 	GetItemAttrs(ii, itype, lvl);
5237 	GetItemBonus(ii, itype, lvl, 2 * lvl, TRUE, TRUE);
5238 
5239 	item[ii]._iSeed = iseed;
5240 	item[ii]._iCreateInfo = lvl | CF_BOY;
5241 	item[ii]._iIdentified = TRUE;
5242 }
5243 
RecreateWitchItem(int ii,int idx,int lvl,int iseed)5244 void RecreateWitchItem(int ii, int idx, int lvl, int iseed)
5245 {
5246 	if (idx == IDI_MANA || idx == IDI_FULLMANA || idx == IDI_PORTAL) {
5247 		GetItemAttrs(ii, idx, lvl);
5248 	} else if (gbIsHellfire && idx >= 114 && idx <= 117) {
5249 		SetRndSeed(iseed);
5250 		random_(0, 1);
5251 		GetItemAttrs(ii, idx, lvl);
5252 	} else {
5253 		SetRndSeed(iseed);
5254 		int itype = RndWitchItem(lvl) - 1;
5255 		GetItemAttrs(ii, itype, lvl);
5256 		int iblvl = -1;
5257 		if (random_(51, 100) <= 5)
5258 			iblvl = 2 * lvl;
5259 		if (iblvl == -1 && item[ii]._iMiscId == IMISC_STAFF)
5260 			iblvl = 2 * lvl;
5261 		if (iblvl != -1)
5262 			GetItemBonus(ii, itype, iblvl >> 1, iblvl, TRUE, TRUE);
5263 	}
5264 
5265 	item[ii]._iSeed = iseed;
5266 	item[ii]._iCreateInfo = lvl | CF_WITCH;
5267 	item[ii]._iIdentified = TRUE;
5268 }
5269 
RecreateHealerItem(int ii,int idx,int lvl,int iseed)5270 void RecreateHealerItem(int ii, int idx, int lvl, int iseed)
5271 {
5272 	int itype;
5273 
5274 	if (idx == IDI_HEAL || idx == IDI_FULLHEAL || idx == IDI_RESURRECT) {
5275 		GetItemAttrs(ii, idx, lvl);
5276 	} else {
5277 		SetRndSeed(iseed);
5278 		itype = RndHealerItem(lvl) - 1;
5279 		GetItemAttrs(ii, itype, lvl);
5280 	}
5281 
5282 	item[ii]._iSeed = iseed;
5283 	item[ii]._iCreateInfo = lvl | CF_HEALER;
5284 	item[ii]._iIdentified = TRUE;
5285 }
5286 
RecreateTownItem(int ii,int idx,WORD icreateinfo,int iseed,int ivalue)5287 void RecreateTownItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue)
5288 {
5289 	if (icreateinfo & CF_SMITH)
5290 		RecreateSmithItem(ii, idx, icreateinfo & CF_LEVEL, iseed);
5291 	else if (icreateinfo & CF_SMITHPREMIUM)
5292 		RecreatePremiumItem(ii, idx, icreateinfo & CF_LEVEL, iseed);
5293 	else if (icreateinfo & CF_BOY)
5294 		RecreateBoyItem(ii, idx, icreateinfo & CF_LEVEL, iseed);
5295 	else if (icreateinfo & CF_WITCH)
5296 		RecreateWitchItem(ii, idx, icreateinfo & CF_LEVEL, iseed);
5297 	else if (icreateinfo & CF_HEALER)
5298 		RecreateHealerItem(ii, idx, icreateinfo & CF_LEVEL, iseed);
5299 }
5300 
RecalcStoreStats()5301 void RecalcStoreStats()
5302 {
5303 	int i;
5304 
5305 	for (i = 0; i < SMITH_ITEMS; i++) {
5306 		if (!smithitem[i].isEmpty()) {
5307 			smithitem[i]._iStatFlag = StoreStatOk(&smithitem[i]);
5308 		}
5309 	}
5310 	for (i = 0; i < SMITH_PREMIUM_ITEMS; i++) {
5311 		if (!premiumitem[i].isEmpty()) {
5312 			premiumitem[i]._iStatFlag = StoreStatOk(&premiumitem[i]);
5313 		}
5314 	}
5315 	for (i = 0; i < 20; i++) {
5316 		if (!witchitem[i].isEmpty()) {
5317 			witchitem[i]._iStatFlag = StoreStatOk(&witchitem[i]);
5318 		}
5319 	}
5320 	for (i = 0; i < 20; i++) {
5321 		if (!healitem[i].isEmpty()) {
5322 			healitem[i]._iStatFlag = StoreStatOk(&healitem[i]);
5323 		}
5324 	}
5325 	boyitem._iStatFlag = StoreStatOk(&boyitem);
5326 }
5327 
ItemNoFlippy()5328 int ItemNoFlippy()
5329 {
5330 	int r = itemactive[numitems - 1];
5331 	item[r]._iAnimFrame = item[r]._iAnimLen;
5332 	item[r]._iAnimFlag = FALSE;
5333 	item[r]._iSelFlag = 1;
5334 
5335 	return r;
5336 }
5337 
CreateSpellBook(int x,int y,spell_id ispell,BOOL sendmsg,BOOL delta)5338 void CreateSpellBook(int x, int y, spell_id ispell, BOOL sendmsg, BOOL delta)
5339 {
5340 	int lvl = currlevel;
5341 
5342 	if (gbIsHellfire) {
5343 		lvl = GetSpellBookLevel(ispell) + 1;
5344 		if (lvl < 1) {
5345 			return;
5346 		}
5347 	}
5348 
5349 	int idx = RndTypeItems(ITYPE_MISC, IMISC_BOOK, lvl);
5350 	if (numitems >= MAXITEMS)
5351 		return;
5352 
5353 	int ii = AllocateItem();
5354 
5355 	while (true) {
5356 		memset(&item[ii], 0, sizeof(*item));
5357 		SetupAllItems(ii, idx, AdvanceRndSeed(), 2 * lvl, 1, TRUE, FALSE, delta);
5358 		if (item[ii]._iMiscId == IMISC_BOOK && item[ii]._iSpell == ispell)
5359 			break;
5360 	}
5361 	GetSuperItemSpace(x, y, ii);
5362 
5363 	if (sendmsg)
5364 		NetSendCmdDItem(FALSE, ii);
5365 	if (delta)
5366 		DeltaAddItem(ii);
5367 }
5368 
CreateMagicItem(int x,int y,int lvl,int imisc,int imid,int icurs,BOOL sendmsg,BOOL delta)5369 static void CreateMagicItem(int x, int y, int lvl, int imisc, int imid, int icurs, BOOL sendmsg, BOOL delta)
5370 {
5371 	if (numitems >= MAXITEMS)
5372 		return;
5373 
5374 	int ii = AllocateItem();
5375 	int idx = RndTypeItems(imisc, imid, lvl);
5376 
5377 	while (true) {
5378 		memset(&item[ii], 0, sizeof(*item));
5379 		SetupAllItems(ii, idx, AdvanceRndSeed(), 2 * lvl, 1, TRUE, FALSE, delta);
5380 		if (item[ii]._iCurs == icurs)
5381 			break;
5382 
5383 		idx = RndTypeItems(imisc, imid, lvl);
5384 	}
5385 	GetSuperItemSpace(x, y, ii);
5386 
5387 	if (sendmsg)
5388 		NetSendCmdDItem(FALSE, ii);
5389 	if (delta)
5390 		DeltaAddItem(ii);
5391 }
5392 
CreateMagicArmor(int x,int y,int imisc,int icurs,BOOL sendmsg,BOOL delta)5393 void CreateMagicArmor(int x, int y, int imisc, int icurs, BOOL sendmsg, BOOL delta)
5394 {
5395 	int lvl = items_get_currlevel();
5396 	CreateMagicItem(x, y, lvl, imisc, IMISC_NONE, icurs, sendmsg, delta);
5397 }
5398 
CreateAmulet(int x,int y,int lvl,BOOL sendmsg,BOOL delta)5399 void CreateAmulet(int x, int y, int lvl, BOOL sendmsg, BOOL delta)
5400 {
5401 	CreateMagicItem(x, y, lvl, ITYPE_AMULET, IMISC_AMULET, ICURS_AMULET, sendmsg, delta);
5402 }
5403 
CreateMagicWeapon(int x,int y,int imisc,int icurs,BOOL sendmsg,BOOL delta)5404 void CreateMagicWeapon(int x, int y, int imisc, int icurs, BOOL sendmsg, BOOL delta)
5405 {
5406 	int imid = IMISC_NONE;
5407 	if (imisc == ITYPE_STAFF)
5408 		imid = IMISC_STAFF;
5409 
5410 	int curlv = items_get_currlevel();
5411 
5412 	CreateMagicItem(x, y, curlv, imisc, imid, icurs, sendmsg, delta);
5413 }
5414 
NextItemRecord(int i)5415 static void NextItemRecord(int i)
5416 {
5417 	gnNumGetRecords--;
5418 
5419 	if (gnNumGetRecords == 0) {
5420 		return;
5421 	}
5422 
5423 	itemrecord[i].dwTimestamp = itemrecord[gnNumGetRecords].dwTimestamp;
5424 	itemrecord[i].nSeed = itemrecord[gnNumGetRecords].nSeed;
5425 	itemrecord[i].wCI = itemrecord[gnNumGetRecords].wCI;
5426 	itemrecord[i].nIndex = itemrecord[gnNumGetRecords].nIndex;
5427 }
5428 
GetItemRecord(int nSeed,WORD wCI,int nIndex)5429 BOOL GetItemRecord(int nSeed, WORD wCI, int nIndex)
5430 {
5431 	int i;
5432 	DWORD dwTicks;
5433 
5434 	dwTicks = SDL_GetTicks();
5435 
5436 	for (i = 0; i < gnNumGetRecords; i++) {
5437 		if (dwTicks - itemrecord[i].dwTimestamp > 6000) {
5438 			NextItemRecord(i);
5439 			i--;
5440 		} else if (nSeed == itemrecord[i].nSeed && wCI == itemrecord[i].wCI && nIndex == itemrecord[i].nIndex) {
5441 			return FALSE;
5442 		}
5443 	}
5444 
5445 	return TRUE;
5446 }
5447 
SetItemRecord(int nSeed,WORD wCI,int nIndex)5448 void SetItemRecord(int nSeed, WORD wCI, int nIndex)
5449 {
5450 	DWORD dwTicks;
5451 
5452 	dwTicks = SDL_GetTicks();
5453 
5454 	if (gnNumGetRecords == MAXITEMS) {
5455 		return;
5456 	}
5457 
5458 	itemrecord[gnNumGetRecords].dwTimestamp = dwTicks;
5459 	itemrecord[gnNumGetRecords].nSeed = nSeed;
5460 	itemrecord[gnNumGetRecords].wCI = wCI;
5461 	itemrecord[gnNumGetRecords].nIndex = nIndex;
5462 	gnNumGetRecords++;
5463 }
5464 
PutItemRecord(int nSeed,WORD wCI,int nIndex)5465 void PutItemRecord(int nSeed, WORD wCI, int nIndex)
5466 {
5467 	int i;
5468 	DWORD dwTicks;
5469 
5470 	dwTicks = SDL_GetTicks();
5471 
5472 	for (i = 0; i < gnNumGetRecords; i++) {
5473 		if (dwTicks - itemrecord[i].dwTimestamp > 6000) {
5474 			NextItemRecord(i);
5475 			i--;
5476 		} else if (nSeed == itemrecord[i].nSeed && wCI == itemrecord[i].wCI && nIndex == itemrecord[i].nIndex) {
5477 			NextItemRecord(i);
5478 			break;
5479 		}
5480 	}
5481 }
5482 
5483 DEVILUTION_END_NAMESPACE
5484