1 /*
2 * items.c
3 * Copyright (C) 2009-2020 Joachim de Groot <jdegroot@web.de>
4 *
5 * NLarn is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * NLarn is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib.h>
20 #include <string.h>
21
22 #include "amulets.h"
23 #include "armour.h"
24 #include "container.h"
25 #include "display.h"
26 #include "game.h"
27 #include "gems.h"
28 #include "items.h"
29 #include "map.h"
30 #include "extdefs.h"
31 #include "player.h"
32 #include "potions.h"
33 #include "random.h"
34 #include "rings.h"
35 #include "scrolls.h"
36 #include "spells.h"
37 #include "utils.h"
38 #include "weapons.h"
39
40 static const char *item_desc_get(item *it, int known);
41
42 const item_type_data item_data[IT_MAX] =
43 {
44 /* item_t name_sg name_pl IMG max_id op bl co eq us st id */
45 { IT_NONE, "", "", ' ', 0, 0, 0, 0, 0, 0, 0, 0, },
46 { IT_AMULET, "amulet", "amulets", '"', AM_LARN, 0, 1, 0, 1, 0, 0, 1, },
47 { IT_AMMO, "ammunition", "ammunition", '\'', AMT_MAX, 1, 1, 1, 1, 0, 1, 1, },
48 { IT_ARMOUR, "armour", "armour", '[', AT_MAX, 1, 1, 1, 1, 0, 0, 1, },
49 { IT_BOOK, "book", "books", '+', SP_MAX, 0, 1, 1, 0, 1, 1, 1, },
50 { IT_CONTAINER, "container", "containers", ']', CT_MAX, 0, 0, 1, 0, 0, 0, 0, },
51 { IT_GEM, "gem", "gems", '*', GT_MAX, 0, 0, 0, 0, 0, 1, 0, },
52 { IT_GOLD, "coin", "coins", '$', 0, 0, 0, 0, 0, 0, 1, 0, },
53 { IT_POTION, "potion", "potions", '!', PO_CURE_DIANTHR, 0, 1, 1, 0, 1, 1, 1, },
54 { IT_RING, "ring", "rings", '=', RT_MAX, 1, 1, 1, 1, 0, 0, 1, },
55 { IT_SCROLL, "scroll", "scrolls", '?', ST_MAX, 0, 1, 1, 0, 1, 1, 1, },
56 { IT_WEAPON, "weapon", "weapons", '(', WT_MAX, 1, 1, 1, 1, 0, 0, 1, },
57 };
58
59 const item_material_data item_materials[IM_MAX] =
60 {
61 /* type name adjective colour frag */
62 { IM_PAPER, "paper", "papier", WHITE, 20, },
63 { IM_CLOTH, "cloth", "cloth", LIGHTGRAY, 15, },
64 { IM_LEATHER, "leather", "leathern", BROWN, 10, },
65 { IM_WOOD, "wood", "wooden", BROWN, 10, },
66 { IM_BONE, "bone", "osseous", DARKGRAY, 15, },
67 { IM_DRAGON_HIDE, "dragon hide", "scabby", GREEN, 2, },
68 { IM_LEAD, "lead", "leady", DARKGRAY, 15, },
69 { IM_IRON, "iron", "irony", LIGHTGRAY, 5, },
70 { IM_STEEL, "steel", "steely", WHITE, 1, },
71 { IM_COPPER, "copper", "cupreous", BROWN, 8, },
72 { IM_SILVER, "silver", "silvery", LIGHTGRAY, 7, },
73 { IM_GOLD, "gold", "golden", YELLOW, 6, },
74 { IM_PLATINUM, "platinum", "platinum", WHITE, 3, },
75 { IM_MITHRIL, "mitril", "mithrial", LIGHTGRAY, 0, },
76 { IM_GLASS, "glass", "vitreous", LIGHTCYAN, 50, },
77 { IM_STONE, "stone", "stony", WHITE, 20, },
78 { IM_GEMSTONE, "gemstone", "gemstone", RED, 0, },
79 };
80
81 /* functions */
82
item_new(item_t item_type,int item_id)83 item *item_new(item_t item_type, int item_id)
84 {
85 item *nitem;
86 effect *eff = NULL;
87 guint loops = 0;
88
89 g_assert(item_type > IT_NONE && item_type < IT_MAX);
90
91 /* has to be zeroed or memcmp will fail */
92 nitem = g_malloc0(sizeof(item));
93
94 nitem->type = item_type;
95 nitem->id = item_id;
96 nitem->count = 1;
97
98 /* set bonus_known on unoptimizable items to avoid stacking problems */
99 if (!item_is_optimizable(item_type))
100 {
101 nitem->bonus_known = TRUE;
102 }
103
104 /* register item with game */
105 nitem->oid = game_item_register(nlarn, nitem);
106
107 /* add special item type specific attributes */
108 switch (item_type)
109 {
110 case IT_AMULET:
111 /* if the amulet has already been created try to create another one */
112 while (nlarn->amulet_created[nitem->id] && (loops < item_max_id(IT_AMULET)))
113 {
114 /* loop through all amulets to find an available one */
115 nitem->id++;
116
117 /* do not create the amulet of larn accidentally */
118 if (nitem->id == item_max_id(item_type))
119 nitem->id = 0;
120
121 loops++;
122 }
123
124 /* every amulet has been created */
125 if (loops == item_max_id(IT_AMULET))
126 {
127 /* remove the failed attempt */
128 item_destroy(nitem);
129
130 /* create something that is not a container */
131 do
132 {
133 item_type = rand_1n(IT_MAX);
134 }
135 while (item_type == IT_CONTAINER);
136
137 /* No need to do a fine touch here, that
138 * will be done in the calling function. */
139 return item_new_random(item_type, FALSE);
140 }
141
142 nlarn->amulet_created[nitem->id] = TRUE;
143
144 if (amulet_effect_t(nitem))
145 {
146 eff = effect_new(amulet_effect_t(nitem));
147 item_effect_add(nitem, eff);
148 }
149
150 break;
151
152 case IT_ARMOUR:
153 /* ensure that unique armour isn't created multiple times */
154 while (nlarn->armour_created[nitem->id])
155 {
156 nitem->id = rand_0n(item_max_id(IT_ARMOUR));
157 }
158
159 if (armour_unique(nitem))
160 {
161 nlarn->armour_created[nitem->id] = TRUE;
162 }
163
164 if (armour_effect(nitem))
165 {
166 eff = effect_new(armour_effect(nitem));
167 item_effect_add(nitem, eff);
168 }
169 break;
170
171 case IT_GEM:
172 /* ensure minimal size */
173 if (nitem->bonus == 0)
174 nitem->bonus = rand_1n(20);
175 break;
176
177 case IT_GOLD:
178 nitem->count = item_id;
179 break;
180
181 case IT_POTION:
182 /* prevent that the unique potion can be created twice */
183 if ((item_id == PO_CURE_DIANTHR) && nlarn->cure_dianthr_created)
184 {
185 nitem->id = rand_0n(item_max_id(IT_POTION));
186 }
187 else if (item_id == PO_CURE_DIANTHR)
188 {
189 nlarn->cure_dianthr_created = TRUE;
190 }
191 break;
192
193 case IT_RING:
194 if (ring_effect_t(nitem))
195 {
196 eff = effect_new(ring_effect_t(nitem));
197
198 /* ring of extra regeneration is better than the average */
199 if (item_id == RT_EXTRA_REGEN)
200 {
201 eff->amount *= 5;
202 }
203
204 item_effect_add(nitem, eff);
205 }
206 break;
207
208 case IT_WEAPON:
209 while (weapon_is_unique(nitem) && nlarn->weapon_created[nitem->id])
210 {
211 /* create another random weapon instead */
212 nitem->id = rand_0n(WT_MAX);
213 }
214
215 if (weapon_is_unique(nitem))
216 {
217 /* mark unique weapon as created */
218 nlarn->weapon_created[nitem->id] = TRUE;
219 }
220
221 /* special effects for Bessman's Hammer */
222 if (nitem->id == WT_BESSMAN)
223 {
224 eff = effect_new(ET_INC_STR);
225 eff->amount = 10;
226 item_effect_add(nitem, eff);
227
228 eff = effect_new(ET_INC_DEX);
229 eff->amount = 10;
230 item_effect_add(nitem, eff);
231
232 eff = effect_new(ET_INC_INT);
233 eff->amount = -10;
234 item_effect_add(nitem, eff);
235 }
236 break;
237
238 default:
239 /* nop */
240 break;
241 }
242
243 return nitem;
244 }
245
item_new_random(item_t item_type,gboolean finetouch)246 item *item_new_random(item_t item_type, gboolean finetouch)
247 {
248 g_assert(item_type > IT_NONE && item_type < IT_MAX);
249
250 int min_id = 0;
251 int max_id = item_max_id(item_type);
252
253 /* special settings for some item types */
254 switch (item_type)
255 {
256 case IT_CONTAINER:
257 max_id = CT_CHEST; /* only bags and caskets */
258 break;
259
260 case IT_GOLD:
261 /* ID is amount in case of gold */
262 min_id = 50;
263 max_id = 250;
264 break;
265
266 default:
267 /* nop */
268 break;
269 }
270
271 int item_id = rand_m_n(min_id, max_id);
272 item *it = item_new(item_type, item_id);
273
274 if (item_type == IT_AMMO)
275 it->count = rand_m_n(10, 50);
276
277 if (finetouch)
278 it = item_new_finetouch(it);
279
280 return it;
281 }
282
item_new_by_level(item_t item_type,int num_level)283 item *item_new_by_level(item_t item_type, int num_level)
284 {
285 item *nitem;
286 float variance, id_base, divisor;
287
288 g_assert (item_type > IT_NONE && item_type < IT_MAX && num_level < MAP_MAX);
289
290 /* no amulets above caverns level 6 */
291 if ((item_type == IT_AMULET) && (num_level < 6))
292 {
293 do
294 {
295 item_type = rand_1n(IT_MAX);
296 } while (item_type == IT_CONTAINER);
297 }
298
299 divisor = 1 / (float)(MAP_MAX - 1);
300
301 switch (item_type)
302 {
303 case IT_BOOK:
304 case IT_WEAPON:
305 variance = 0.2;
306 break;
307
308 case IT_ARMOUR:
309 case IT_RING:
310 variance = 0.5;
311 break;
312
313 default:
314 /* no per-map randomisation */
315 return item_new_random(item_type, TRUE);
316 }
317
318 id_base = item_max_id(item_type) * (num_level * divisor);
319 int id_min = id_base - (item_max_id(item_type) * variance);
320 int id_max = id_base + (item_max_id(item_type) * variance);
321
322 /* clean results */
323 if (id_min < 0) id_min = 0;
324 if (id_max < 1) id_max = 1;
325 if (id_max > (int)item_max_id(item_type)) id_max = item_max_id(item_type);
326
327 /* create the item */
328 nitem = item_new(item_type, rand_m_n(id_min, id_max));
329
330 return item_new_finetouch(nitem);
331 }
332
item_new_finetouch(item * it)333 item *item_new_finetouch(item *it)
334 {
335 g_assert(it != NULL);
336
337 /* maybe the item is blessed or cursed */
338 if (it->type == IT_POTION && it->id == PO_WATER)
339 {
340 // water is always blessed
341 item_bless(it);
342 it->blessed_known = TRUE;
343 }
344 else if (item_is_blessable(it->type) && chance(25))
345 {
346 /* only blessed or cursed items have bonus / minus points */
347 if (item_is_optimizable(it->type))
348 {
349 it->bonus = rand_1n(3);
350 }
351
352 if (chance(50))
353 {
354 /* blessed */
355 item_bless(it);
356 }
357 else
358 {
359 /* cursed */
360 it->bonus = 0 - it->bonus;
361 item_curse(it);
362 }
363 }
364
365 if ((it->bonus != 0) && it->effects)
366 {
367 /* modify effects linked to the item */
368 for (guint eff_no = 0; eff_no < it->effects->len; eff_no++)
369 {
370 gpointer eid = (effect *)g_ptr_array_index(it->effects, eff_no);
371 effect *e = game_effect_get(nlarn, eid);
372 e->amount += it->bonus;
373 }
374 }
375
376 /* maybe the item is corroded
377 Don't erode potions, as they break when exposed to fire */
378 if (it->type != IT_POTION && item_is_corrodible(it->type) && chance(25))
379 {
380 it = item_erode(NULL, it, rand_1n(IET_MAX), FALSE);
381 }
382
383 return it;
384 }
385
item_copy(item * original)386 item *item_copy(item *original)
387 {
388 item *nitem;
389
390 g_assert(original != NULL);
391
392 /* clone item */
393 nitem = g_malloc0(sizeof(item));
394 memcpy(nitem, original, sizeof(item));
395
396 /* copy effects */
397 nitem->effects = NULL;
398
399 if (original->effects != NULL)
400 {
401 for (guint idx = 0; idx < original->effects->len; idx++)
402 {
403 effect *e = g_ptr_array_index(original->effects, idx);
404 effect *ne = effect_copy(e);
405
406 ne->item = nitem;
407 item_effect_add(nitem, ne);
408 }
409 }
410
411 /* reset inventory */
412 nitem->content = NULL;
413
414 /* register copy with game */
415 nitem->oid = game_item_register(nlarn, nitem);
416
417 return nitem;
418 }
419
item_split(item * original,guint32 count)420 item *item_split(item *original, guint32 count)
421 {
422 item *nitem;
423
424 g_assert(original != NULL && count < original->count);
425
426 nitem = item_copy(original);
427
428 nitem->count = count;
429 original->count -= count;
430
431 return nitem;
432 }
433
item_destroy(item * it)434 void item_destroy(item *it)
435 {
436 g_assert(it != NULL && it->type > IT_NONE && it->type < IT_MAX);
437
438 if (it->effects)
439 {
440 while (it->effects->len)
441 {
442 effect *eff;
443 gpointer effect_id = g_ptr_array_remove_index_fast(it->effects,
444 it->effects->len - 1);
445
446 if ((eff = game_effect_get(nlarn, effect_id)))
447 effect_destroy(eff);
448 }
449
450 g_ptr_array_free(it->effects, TRUE);
451 }
452
453 if (it->content != NULL)
454 {
455 inv_destroy(it->content, FALSE);
456 }
457
458 if (it->notes != NULL)
459 {
460 g_free(it->notes);
461 }
462
463 /* unregister item */
464 game_item_unregister(nlarn, it->oid);
465
466 g_free(it);
467 }
468
item_serialize(gpointer oid,gpointer it,gpointer root)469 void item_serialize(gpointer oid, gpointer it, gpointer root)
470 {
471 cJSON *ival;
472
473 item *i = (item *)it;
474
475 cJSON_AddItemToArray((cJSON *)root, ival = cJSON_CreateObject());
476
477 cJSON_AddNumberToObject(ival, "oid", GPOINTER_TO_UINT(oid));
478 cJSON_AddNumberToObject(ival, "type", i->type);
479 cJSON_AddNumberToObject(ival, "id", i->id);
480 cJSON_AddNumberToObject(ival, "bonus", i->bonus);
481 cJSON_AddNumberToObject(ival, "count", i->count);
482
483 if (i->blessed == 1) cJSON_AddTrueToObject(ival, "blessed");
484 if (i->cursed == 1) cJSON_AddTrueToObject(ival, "cursed");
485 if (i->blessed_known == 1) cJSON_AddTrueToObject(ival, "blessed_known");
486 if (i->bonus_known == 1) cJSON_AddTrueToObject(ival, "bonus_known");
487 if (i->fired == 1) cJSON_AddTrueToObject(ival, "fired");
488
489 if (i->corroded > 0) cJSON_AddNumberToObject(ival, "corroded", i->corroded);
490 if (i->burnt > 0) cJSON_AddNumberToObject(ival, "burnt", i->burnt);
491 if (i->rusty > 0) cJSON_AddNumberToObject(ival, "rusty", i->rusty);
492
493 /* container content */
494 if (inv_length(i->content) > 0)
495 {
496 cJSON_AddItemToObject(ival, "content", inv_serialize(i->content));
497 }
498
499 /* player's notes */
500 if (i->notes != NULL)
501 {
502 cJSON_AddStringToObject(ival, "notes", i->notes);
503 }
504
505 /* effects */
506 if (i->effects)
507 {
508 cJSON_AddItemToObject(ival, "effects", effects_serialize(i->effects));
509 }
510 }
511
item_deserialize(cJSON * iser,struct game * g)512 item *item_deserialize(cJSON *iser, struct game *g)
513 {
514 guint oid;
515 item *it;
516 cJSON *obj;
517
518 it = g_malloc0(sizeof(item));
519
520 /* must-have attributes */
521 oid = cJSON_GetObjectItem(iser, "oid")->valueint;
522 it->oid = GUINT_TO_POINTER(oid);
523
524 it->type = cJSON_GetObjectItem(iser, "type")->valueint;
525 it->id = cJSON_GetObjectItem(iser, "id")->valueint;
526 it->bonus = cJSON_GetObjectItem(iser, "bonus")->valueint;
527 it->count = cJSON_GetObjectItem(iser, "count")->valueint;
528
529 /* optional attributes */
530 obj = cJSON_GetObjectItem(iser, "blessed");
531 if (obj != NULL) it->blessed = obj->valueint;
532
533 obj = cJSON_GetObjectItem(iser, "cursed");
534 if (obj != NULL) it->cursed = obj->valueint;
535
536 obj = cJSON_GetObjectItem(iser, "blessed_known");
537 if (obj != NULL) it->blessed_known = obj->valueint;
538
539 obj = cJSON_GetObjectItem(iser, "bonus_known");
540 if (obj != NULL) it->bonus_known = obj->valueint;
541
542 obj = cJSON_GetObjectItem(iser, "fired");
543 if (obj != NULL) it->fired = obj->valueint;
544
545 obj = cJSON_GetObjectItem(iser, "corroded");
546 if (obj != NULL) it->corroded = obj->valueint;
547
548 obj = cJSON_GetObjectItem(iser, "burnt");
549 if (obj != NULL) it->burnt = obj->valueint;
550
551 obj = cJSON_GetObjectItem(iser, "rusty");
552 if (obj != NULL) it->rusty = obj->valueint;
553
554 /* container content */
555 obj = cJSON_GetObjectItem(iser, "content");
556 if (obj != NULL) it->content = inv_deserialize(obj);
557
558 /* player's notes */
559 obj = cJSON_GetObjectItem(iser, "notes");
560 if (obj != NULL) it->notes = g_strdup(obj->valuestring);
561
562 /* effects */
563 obj = cJSON_GetObjectItem(iser, "effects");
564 if (obj != NULL) it->effects = effects_deserialize(obj);
565
566 /* add item to game */
567 g_hash_table_insert(g->items, it->oid, it);
568
569 /* increase max_id to match used ids */
570 if (g->item_max_id < oid)
571 g->item_max_id = oid;
572
573 return it;
574 }
575
item_compare(item * a,item * b)576 int item_compare(item *a, item *b)
577 {
578 int tmp_count, result;
579 gpointer oid_a, oid_b;
580
581 if (a->type != b->type)
582 {
583 return FALSE;
584 }
585 else if (a->type == IT_GOLD)
586 {
587 return TRUE;
588 }
589
590 /* as count can be different for equal items, save count of b */
591 tmp_count = b->count;
592 b->count = a->count;
593
594 /* store oids (never identical!) */
595 oid_a = a->oid;
596 oid_b = b->oid;
597
598 a->oid = NULL;
599 b->oid = NULL;
600
601 result = (memcmp(a, b, sizeof(item)) == 0);
602
603 b->count = tmp_count;
604
605 /* restore oids */
606 a->oid = oid_a;
607 b->oid = oid_b;
608
609 return result;
610 }
611
item_sort(gconstpointer a,gconstpointer b,gpointer data,gboolean force_id)612 int item_sort(gconstpointer a, gconstpointer b, gpointer data, gboolean force_id)
613 {
614 gint order;
615 item *item_a = game_item_get(nlarn, *((gpointer**)a));
616 item *item_b = game_item_get(nlarn, *((gpointer**)b));
617 player *p = (player *)data;
618
619 if (item_a->type == item_b->type)
620 {
621 /* Both items are of identical type. Compare their names. */
622 order = g_ascii_strcasecmp(item_desc_get(item_a, force_id || player_item_known(p, item_a)),
623 item_desc_get(item_b, force_id || player_item_known(p, item_b)));
624 }
625 else if (item_a->type < item_b->type)
626 order = -1;
627 else
628 order = 1;
629
630 return order;
631 }
632
item_describe(item * it,gboolean known,gboolean singular,gboolean definite)633 gchar *item_describe(item *it, gboolean known, gboolean singular, gboolean definite)
634 {
635 GString *desc = g_string_new(NULL);
636
637 g_assert((it != NULL) && (it->type > IT_NONE) && (it->type < IT_MAX));
638
639 /*
640 * Return a simplified description if the player is blinded.
641 * We need to ensure the player object exists as this function
642 * is called very early in the game.
643 */
644 if (nlarn->p && player_effect_get(nlarn->p, ET_BLINDNESS))
645 {
646 struct item_type_data itd = item_data[it->type];
647 g_string_append_printf(desc, "%s %s",
648 it->count == 1 ? a_an(itd.name_sg) : "some",
649 it->count == 1 ? itd.name_sg : itd.name_pl);
650
651 return g_string_free(desc, FALSE);
652 }
653
654 /* collect additional information */
655 char *add_info = NULL;
656 char **add_infos = strv_new();
657 if (it->blessed_known)
658 {
659 if (it->blessed) strv_append(&add_infos, "blessed");
660 else if (it->cursed)
661 {
662 if (it->type == IT_CONTAINER)
663 strv_append(&add_infos, "trapped");
664 else
665 strv_append(&add_infos, "cursed");
666 }
667 else strv_append(&add_infos, "uncursed");
668 }
669
670 if (it->burnt == 1) strv_append(&add_infos, "burnt");
671 if (it->burnt == 2) strv_append(&add_infos, "very burnt");
672 if (it->corroded == 1) strv_append(&add_infos, "corroded");
673 if (it->corroded == 2) strv_append(&add_infos, "very corroded");
674 if (it->rusty == 1) strv_append(&add_infos, "rusty");
675 if (it->rusty == 2) strv_append(&add_infos, "very rusty");
676
677 if (g_strv_length(add_infos))
678 add_info = g_strjoinv(", ", add_infos);
679
680 g_strfreev(add_infos);
681
682 switch (it->type)
683 {
684 case IT_AMULET:
685 if (known)
686 g_string_append_printf(desc, "%s of %s",
687 amulet_type(it) == AMULET ? "amulet" : "talisman",
688 item_desc_get(it, known));
689 else
690 g_string_append_printf(desc, "%s amulet", item_desc_get(it, known));
691 break;
692
693 case IT_AMMO:
694 g_string_append_printf(desc, "%s%s", item_desc_get(it, known),
695 (!singular && it->count > 1) ? "s" : "");
696
697 if (it->bonus_known)
698 {
699 g_string_append_printf(desc, " %+d", it->bonus);
700 }
701 break;
702
703 case IT_ARMOUR:
704 g_string_append(desc, item_desc_get(it, known));
705
706 if (it->bonus_known)
707 {
708 g_string_append_printf(desc, " %+d", it->bonus);
709 }
710
711 break;
712
713 case IT_BOOK:
714 if (known)
715 {
716 g_string_append_printf(desc, "book%s of %s",
717 (!singular && it->count > 1) ? "s" : "",
718 item_desc_get(it, known));
719 }
720 else
721 g_string_append_printf(desc, "%s book%s", item_desc_get(it, known),
722 (!singular && it->count > 1) ? "s" : "");
723 break;
724
725 case IT_CONTAINER:
726 g_string_append(desc, item_desc_get(it, known));
727 break;
728
729 case IT_GEM:
730 g_string_append_printf(desc, "%d carats %s", gem_size(it), item_desc_get(it, known));
731 break;
732
733 case IT_GOLD:
734 g_string_append_printf(desc, "gold piece%s", it->count > 1 ? "s" : "");
735 break;
736
737 case IT_POTION:
738 if (known)
739 {
740 g_string_append_printf(desc, "potion%s of %s",
741 (!singular && it->count > 1) ? "s" : "",
742 item_desc_get(it, known));
743 }
744 else
745 g_string_append_printf(desc, "%s potion%s", item_desc_get(it, known),
746 (!singular && it->count > 1) ? "s" : "");
747 break;
748
749 case IT_RING:
750 if (known)
751 g_string_append_printf(desc, "ring of %s", item_desc_get(it, known));
752 else
753 g_string_append_printf(desc, "%s ring", item_desc_get(it, known));
754
755 /* display bonus if it is known */
756 if (it->bonus_known)
757 g_string_append_printf(desc, " %+d", it->bonus);
758 break;
759
760 case IT_SCROLL:
761 if (known)
762 {
763 g_string_append_printf(desc, "scroll%s of %s",
764 (!singular && it->count > 1) ? "s" : "",
765 item_desc_get(it, known));
766 }
767 else
768 {
769 if (strcmp(item_desc_get(it, known), "") == 0)
770 g_string_append_printf(desc, "unlabelled scroll%s",
771 (!singular && it->count > 1) ? "s" : "");
772 else
773 g_string_append_printf(desc, "scroll%s labelled %s",
774 (!singular && it->count > 1) ? "s" : "",
775 item_desc_get(it, known));
776 }
777 break;
778
779 case IT_WEAPON:
780 if (weapon_is_unique(it))
781 {
782 g_string_append_printf(desc, "%s%s",
783 weapon_needs_article(it) ? "the " : "",
784 item_desc_get(it, known));
785
786 if (it->bonus_known || add_info != NULL)
787 {
788 if (it->bonus_known)
789 {
790 if (add_info != NULL)
791 {
792 /* bonus and additional information */
793 g_string_append_printf(desc, " (%+d, %s)", it->bonus, add_info);
794 }
795 else
796 {
797 /* bonus only */
798 g_string_append_printf(desc, " (%+d)", it->bonus);
799 }
800 }
801 else
802 {
803 /* additional information only */
804 g_string_append_printf(desc, " (%s)", add_info);
805 }
806 }
807 }
808 else
809 {
810 g_string_append(desc, item_desc_get(it, known));
811
812 if (it->bonus_known)
813 g_string_append_printf(desc, " %+d", it->bonus);
814 }
815 break;
816
817 default:
818 break;
819 }
820
821 if (it->notes)
822 {
823 g_string_append_printf(desc, " (%s)",
824 (strlen(it->notes) > 5 ? "noted" : it->notes));
825 }
826
827 /* prepend additional information unless the item is an unique weapon */
828 if (!(it->type == IT_WEAPON && weapon_is_unique(it)))
829 {
830 if (add_info != NULL)
831 {
832 g_string_prepend_c(desc, ' ');
833 g_string_prepend(desc, add_info);
834 }
835
836 /* prepend count or article */
837 gchar first_char = desc->str[0];
838 g_string_prepend_c(desc, ' ');
839
840 if ((it->count == 1) || singular)
841 {
842 g_string_prepend(desc, (const char *)(definite ? "the" : a_an(&first_char)));
843 }
844 else
845 {
846 g_string_prepend(desc, int2str(it->count));
847 }
848 }
849
850 /* free the additional information */
851 g_free(add_info);
852
853 return g_string_free(desc, FALSE);
854 }
855
item_material(item * it)856 item_material_t item_material(item *it)
857 {
858 g_assert (it != NULL);
859
860 switch (it->type)
861 {
862 case IT_AMULET:
863 return amulet_material(it->id);
864 break;
865
866 case IT_AMMO:
867 return ammo_material(it);
868 break;
869
870 case IT_ARMOUR:
871 return armour_material(it);
872 break;
873
874 case IT_BOOK:
875 return IM_PAPER;
876 break;
877
878 case IT_CONTAINER:
879 return container_material(it);
880 break;
881
882 case IT_GEM:
883 return IM_GEMSTONE;
884 break;
885
886 case IT_GOLD:
887 return IM_GOLD;
888 break;
889
890 case IT_POTION:
891 return IM_GLASS;
892 break;
893
894 case IT_RING:
895 return ring_material(it->id);
896 break;
897
898 case IT_SCROLL:
899 return IM_PAPER;
900 break;
901
902 case IT_WEAPON:
903 return weapon_material(it);
904 break;
905
906 default:
907 g_assert(0);
908 /* required to silence a release mode warning */
909 return IM_MAX;
910 }
911 }
912
item_base_price(item * it)913 guint item_base_price(item *it)
914 {
915 g_assert (it != NULL && it->type > IT_NONE && it->type < IT_MAX);
916
917 guint price;
918
919 switch (it->type)
920 {
921 case IT_AMULET:
922 price = amulet_price(it);
923 break;
924
925 case IT_AMMO:
926 price = ammo_price(it);
927 break;
928
929 case IT_ARMOUR:
930 price = armour_price(it);
931 break;
932
933 case IT_BOOK:
934 price = book_price(it);
935 break;
936
937 case IT_CONTAINER:
938 price = container_price(it);
939 break;
940
941 case IT_GEM:
942 price = gem_price(it);
943 break;
944
945 case IT_GOLD:
946 price = 0;
947 break;
948
949 case IT_POTION:
950 price = potion_price(it);
951 break;
952
953 case IT_RING:
954 price = ring_price(it);
955 break;
956
957 case IT_SCROLL:
958 price = scroll_price(it);
959 break;
960
961 case IT_WEAPON:
962 price = weapon_price(it);
963 break;
964
965 default:
966 price = 0;
967 }
968
969 return price;
970 }
971
item_price(item * it)972 guint item_price(item *it)
973 {
974 g_assert (it != NULL && it->type > IT_NONE && it->type < IT_MAX);
975
976 guint price = item_base_price(it);
977
978 /* modify base prices by item's attributes */
979
980 /* 20% price increase / decrease for every +/-1 bonus */
981 if (it->bonus != 0) price = price * (1 + (0.2 * it->bonus));
982
983 /* gem prices are not affected by being blessed or cursed */
984 if (it->type == IT_GEM)
985 return price;
986
987 /* double price if blessed */
988 if (it->blessed) price <<=1;
989
990 /* half price if cursed */
991 if (it->cursed) price >>=1;
992
993 /* half price if corroded */
994 if (it->corroded == 1) price >>=1;
995
996 /* quarter price if very corroded */
997 if (it->corroded == 2) price /= 4;
998
999 /* half price if burnt */
1000 if (it->burnt == 1) price >>=1;
1001
1002 /* quarter price if very burnt */
1003 if (it->burnt == 2) price /= 4;
1004
1005 /* half price if rusty */
1006 if (it->rusty == 1) price >>=1;
1007
1008 /* quarter price if very rusty */
1009 if (it->rusty == 2) price /= 4;
1010
1011 return price;
1012 }
1013
item_weight(item * it)1014 int item_weight(item *it)
1015 {
1016 int weight = 0;
1017
1018 g_assert(it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1019
1020 switch (it->type)
1021 {
1022 case IT_AMULET:
1023 weight = 150;
1024 break;
1025
1026 case IT_AMMO:
1027 weight = ammo_weight(it);
1028 break;
1029
1030 case IT_ARMOUR:
1031 weight = armour_weight(it);
1032 break;
1033
1034 case IT_BOOK:
1035 weight = book_weight(it);
1036 break;
1037
1038 case IT_POTION:
1039 weight = 250;
1040 break;
1041
1042 case IT_RING:
1043 weight = 10;
1044 break;
1045
1046 case IT_SCROLL:
1047 weight = 100;
1048 break;
1049
1050 case IT_CONTAINER:
1051 weight = container_weight(it) + inv_weight(it->content);
1052 break;
1053
1054 case IT_GOLD:
1055 /* Is this too heavy? Is this too light?
1056 It should give the player a reason to use the bank. */
1057 weight = 4;
1058 break;
1059
1060 case IT_GEM:
1061 weight = gem_weight(it);
1062 break;
1063
1064 case IT_WEAPON:
1065 weight = weapon_weight(it);
1066 break;
1067
1068 default:
1069 weight = 0;
1070 break;
1071 }
1072
1073 if (item_is_stackable(it->type))
1074 weight = weight * it->count;
1075
1076 return weight;
1077 }
1078
item_colour(item * it)1079 int item_colour(item *it)
1080 {
1081 g_assert(it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1082
1083 switch (it->type)
1084 {
1085 case IT_AMULET:
1086 case IT_AMMO:
1087 case IT_ARMOUR:
1088 case IT_RING:
1089 case IT_WEAPON:
1090 return item_materials[item_material(it)].colour;
1091 break;
1092
1093 case IT_BOOK:
1094 return book_colour(it);
1095 break;
1096
1097 case IT_POTION:
1098 return potion_colour(it->id);
1099 break;
1100
1101 case IT_SCROLL:
1102 return WHITE;
1103 break;
1104
1105 case IT_CONTAINER:
1106 return BROWN;
1107 break;
1108
1109 case IT_GOLD:
1110 return YELLOW;
1111 break;
1112
1113 case IT_GEM:
1114 return gem_colour(it);
1115 break;
1116
1117 default:
1118 return BLACK;
1119 break;
1120 }
1121 }
1122
item_fragility(item * it)1123 guint item_fragility(item *it)
1124 {
1125 int probability = item_materials[item_material(it)].fragility;
1126
1127 /* Ensure that unique weapons do not break */
1128 if (it->type == IT_WEAPON && weapon_is_unique(it))
1129 return 0;
1130
1131 probability += 15 * it->burnt;
1132 probability += 15 * it->corroded;
1133 probability += 15 * it->rusty;
1134
1135 if (it->blessed)
1136 probability /= 2;
1137
1138 if (it->cursed)
1139 probability *= 2;
1140
1141 return (guint)max(0, min(probability, 100));
1142 }
1143
item_effect_add(item * it,effect * e)1144 void item_effect_add(item *it, effect *e)
1145 {
1146 g_assert (it != NULL && it->oid != NULL && e != NULL);
1147
1148 /* create list if not existent */
1149 if (!it->effects)
1150 {
1151 it->effects = g_ptr_array_new();
1152 }
1153
1154 /* this effect is permanent */
1155 e->turns = 0;
1156
1157 /* link item to effect */
1158 e->item = it->oid;
1159
1160 /* add effect to list */
1161 effect_add(it->effects, e);
1162 }
1163
item_bless(item * it)1164 int item_bless(item *it)
1165 {
1166 g_assert (it != NULL && !it->cursed);
1167
1168 if (it->blessed)
1169 return FALSE;
1170
1171 it->blessed = TRUE;
1172
1173 return TRUE;
1174 }
1175
item_curse(item * it)1176 int item_curse(item *it)
1177 {
1178 g_assert (it != NULL && !it->blessed);
1179
1180 if (it->cursed)
1181 return FALSE;
1182
1183 it->cursed = TRUE;
1184
1185 return TRUE;
1186 }
1187
item_remove_curse(item * it)1188 int item_remove_curse(item *it)
1189 {
1190 g_assert (it != NULL && it->cursed);
1191
1192 if (!it->cursed || it->blessed)
1193 return FALSE;
1194
1195 it->cursed = FALSE;
1196 it->blessed_known = TRUE;
1197
1198 return TRUE;
1199 }
1200
item_enchant(item * it)1201 item *item_enchant(item *it)
1202 {
1203 g_assert(it != NULL);
1204
1205 it->bonus++;
1206
1207 /* warn against over-enchantment */
1208 if (it->bonus == 3)
1209 {
1210 /* hide bonus from description */
1211 gboolean bonus_known = it->bonus_known;
1212 it->bonus_known = FALSE;
1213
1214 gchar *desc = item_describe(it, player_item_known(nlarn->p, it),
1215 (it->count == 1), TRUE);
1216
1217 desc[0] = g_ascii_toupper(desc[0]);
1218 log_add_entry(nlarn->log, "%s vibrate%s strangely.",
1219 desc, (it->count == 1) ? "s" : "");
1220
1221 g_free(desc);
1222 it->bonus_known = bonus_known;
1223 }
1224
1225 /* item has been over-enchanted */
1226 if (it->bonus > 3)
1227 {
1228 player_item_destroy(nlarn->p, it);
1229 return NULL;
1230 }
1231
1232 if ((it->type == IT_RING) && it->effects)
1233 {
1234 for (guint pos = 0; pos < it->effects->len; pos++)
1235 {
1236 gpointer oid = g_ptr_array_index(it->effects, pos);
1237 effect *e = game_effect_get(nlarn, oid);
1238
1239 e->amount++;
1240 }
1241 }
1242
1243 return it;
1244 }
1245
item_disenchant(item * it)1246 item *item_disenchant(item *it)
1247 {
1248 g_assert(it != NULL);
1249
1250 if (it->bonus <= -3)
1251 {
1252 player_item_destroy(nlarn->p, it);
1253 return NULL;
1254 }
1255
1256 it->bonus--;
1257
1258 if (it->bonus == -3)
1259 {
1260 gchar *desc = item_describe(it, player_item_known(nlarn->p, it), FALSE, TRUE);
1261
1262 desc[0] = g_ascii_toupper(desc[0]);
1263 log_add_entry(nlarn->log, "%s vibrate%s warningly.",
1264 desc, (it->count == 1) ? "s" : "");
1265
1266 g_free(desc);
1267 it->bonus_known = TRUE;
1268 }
1269
1270 if ((it->type == IT_RING) && it->effects)
1271 {
1272 for (guint pos = 0; pos < it->effects->len; pos++)
1273 {
1274 gpointer oid = g_ptr_array_index(it->effects, pos);
1275 effect *e = game_effect_get(nlarn, oid);
1276
1277 e->amount--;
1278 }
1279 }
1280
1281 return it;
1282 }
1283
material_affected(item_material_t mat,item_erosion_type iet)1284 static int material_affected(item_material_t mat, item_erosion_type iet)
1285 {
1286 switch (iet)
1287 {
1288 case IET_BURN:
1289 /* potions will shatter when exposed to fire */
1290 return (mat <= IM_BONE || mat == IM_GLASS);
1291 case IET_CORRODE:
1292 return (mat == IM_IRON);
1293 case IET_RUST:
1294 return (mat == IM_IRON);
1295 default:
1296 return (FALSE);
1297 }
1298 }
1299
item_erode(inventory ** inv,item * it,item_erosion_type iet,gboolean visible)1300 item *item_erode(inventory **inv, item *it, item_erosion_type iet, gboolean visible)
1301 {
1302 gboolean destroyed = FALSE;
1303 const char *erosion_desc = NULL;
1304 gchar *item_desc = NULL;
1305
1306 g_assert(it != NULL);
1307
1308 /* Don't ever destroy the potion of cure dianthroritis.
1309 This is not currently possible, but add a check in case that changes. */
1310 if (it->type == IT_POTION && it->id == PO_CURE_DIANTHR)
1311 return (it);
1312
1313 if (!material_affected(item_material(it), iet))
1314 return (it);
1315
1316 if (visible)
1317 {
1318 /* prepare item description before it has been affected */
1319 item_desc = item_describe(it, player_item_known(nlarn->p, it),
1320 (it->count == 1), TRUE);
1321
1322 item_desc[0] = g_ascii_toupper(item_desc[0]);
1323 }
1324
1325 // Blessed items have a 50% chance of resisting the erosion.
1326 if (it->blessed && chance(50))
1327 {
1328 if (visible)
1329 {
1330 log_add_entry(nlarn->log, "%s resist%s.", item_desc,
1331 (it->count == 1) ? "s" : "");
1332
1333 g_free(item_desc);
1334 it->blessed_known = TRUE;
1335 }
1336 return (it);
1337 }
1338
1339 switch (iet)
1340 {
1341 case IET_BURN:
1342 if (it->type == IT_POTION)
1343 {
1344 erosion_desc = "shatter";
1345 destroyed = TRUE;
1346 }
1347 else if (item_material(it) == IM_GLASS)
1348 {
1349 /* other things made of glass are not affected */
1350 }
1351 else
1352 {
1353 it->burnt++;
1354 erosion_desc = "burn";
1355
1356 if (it->burnt == 3) destroyed = TRUE;
1357 }
1358 break;
1359
1360 case IET_CORRODE:
1361 it->corroded++;
1362 erosion_desc = "corrode";
1363
1364 if (it->corroded == 3) destroyed = TRUE;
1365 break;
1366
1367 case IET_RUST:
1368 it->rusty++;
1369 erosion_desc = "rust";
1370
1371 /* it's been very rusty already -> destroy */
1372 if (it->rusty == 3) destroyed = TRUE;
1373 break;
1374
1375 default:
1376 /* well, just do nothing. */
1377 break;
1378 }
1379
1380 if (erosion_desc != NULL && visible)
1381 {
1382 /* items has been eroded, describe the event if it is visible */
1383 log_add_entry(nlarn->log, "%s %s%s.", item_desc,
1384 erosion_desc, (it->count == 1) ? "s" : "");
1385 }
1386
1387 if (destroyed)
1388 {
1389 /* item has been destroyed */
1390 if (visible)
1391 {
1392 /* describe the event if the item was visible */
1393 log_add_entry(nlarn->log, "%s %s destroyed.", item_desc,
1394 is_are(it->count));
1395 }
1396
1397 if (inv != NULL)
1398 {
1399 if (*inv == nlarn->p->inventory)
1400 {
1401 /* if the inventory we are working on is the player's
1402 * inventory, try to unequip the item first as we do
1403 * not know if it is equipped (this would lead to nasty
1404 * segmentation faults otherwise) */
1405 player_item_unequip(nlarn->p, &nlarn->p->inventory, it, TRUE);
1406 }
1407
1408 inv_del_element(inv, it);
1409 }
1410
1411 if (it->content != NULL)
1412 {
1413 /* if the item is a container an still has undestroyed content,
1414 * this content has to be put into the items inventory, e.g. a
1415 * casket burns -> all undestroyed items inside the casket
1416 * continue to exist on the floor tile the casket was standing on
1417 */
1418 while (inv_length(it->content) > 0)
1419 {
1420 item *cit = inv_get(it->content, inv_length(it->content) - 1);
1421 inv_del_element(&it->content, cit);
1422 inv_add(inv, cit);
1423 }
1424 }
1425
1426 /* remove the item from the game */
1427 item_destroy(it);
1428 it = NULL;
1429 }
1430
1431 g_free(item_desc);
1432
1433 return it;
1434 }
1435
item_obtainable(item_t type,int id)1436 int item_obtainable(item_t type, int id)
1437 {
1438 int obtainable;
1439
1440 switch (type)
1441 {
1442 case IT_AMMO:
1443 obtainable = ammo_t_obtainable(id);
1444 break;
1445
1446 case IT_ARMOUR:
1447 obtainable = armours[id].obtainable;
1448 break;
1449
1450 case IT_BOOK:
1451 obtainable = book_type_obtainable(id);
1452 break;
1453
1454 case IT_CONTAINER:
1455 obtainable = (id == CT_BAG);
1456 break;
1457
1458 case IT_POTION:
1459 obtainable = (potion_type_store_stock(id) > 0);
1460 break;
1461
1462 case IT_RING:
1463 obtainable = ring_type_obtainable(id);
1464 break;
1465
1466 case IT_SCROLL:
1467 obtainable = (scroll_type_store_stock(id) > 0);
1468 break;
1469
1470 case IT_WEAPON:
1471 obtainable = weapon_type_obtainable(id);
1472 break;
1473
1474 default:
1475 obtainable = FALSE;
1476 }
1477
1478 return obtainable;
1479 }
1480
item_detailed_description(item * it,gboolean known,gboolean shop)1481 char *item_detailed_description(item *it, gboolean known, gboolean shop)
1482 {
1483 g_assert (it != NULL);
1484
1485 GString *desc = g_string_new("");
1486
1487 switch (it->type)
1488 {
1489 case IT_AMMO:
1490 g_string_append_printf(desc, "Ammunition for %ss\n",
1491 ammo_class_name[ammo_class(it)]);
1492
1493 if (it->bonus_known)
1494 {
1495 g_string_append_printf(desc, "Damage: +%d\n"
1496 "Accuracy: +%d\n",
1497 ammo_damage(it), ammo_accuracy(it));
1498 }
1499 else
1500 {
1501 g_string_append_printf(desc, "Base damage: +%d\n"
1502 "Base accuracy: +%d\n",
1503 ammo_base_damage(it), ammo_base_accuracy(it));
1504 }
1505
1506 break;
1507
1508 case IT_BOOK:
1509 if (known)
1510 {
1511 gchar *stdesc = spell_desc_by_id(it->id);
1512 g_string_append_printf(desc, "%s\nSpell level: %d\n",
1513 stdesc, spell_level_by_id(it->id));
1514 g_free(stdesc);
1515 }
1516 break;
1517
1518 case IT_WEAPON:
1519 {
1520 /* assemble a string of weapon attributes */
1521 char **attributes = strv_new();
1522
1523 if (weapon_is_twohanded(it))
1524 strv_append(&attributes, "two-handed");
1525
1526 if (weapon_is_ranged(it))
1527 strv_append(&attributes, "ranged");
1528
1529 if (weapon_is_unique(it))
1530 strv_append(&attributes, "unique");
1531
1532 if (g_strv_length(attributes) > 0)
1533 {
1534 gchar *tmp = g_strjoinv(", ", attributes);
1535 tmp[0] = g_ascii_toupper(tmp[0]);
1536 g_string_append_printf(desc, "%s weapon\n", tmp);
1537 g_free(tmp);
1538 }
1539
1540 g_strfreev(attributes);
1541
1542 if (it->bonus_known)
1543 {
1544 g_string_append_printf(desc,
1545 "Damage: +%d\n"
1546 "Accuracy: +%d\n",
1547 weapon_damage(it), weapon_acc(it));
1548 }
1549 else
1550 {
1551 g_string_append_printf(desc,
1552 "Base damage: +%d\n"
1553 "Base accuracy: +%d\n",
1554 weapon_base_damage(it), weapon_base_acc(it));
1555 }
1556 }
1557 break;
1558
1559 case IT_ARMOUR:
1560 if (it->bonus_known)
1561 {
1562 g_string_append_printf(desc, "Armour class: %d\n", armour_ac(it));
1563 }
1564 else
1565 {
1566 g_string_append_printf(desc, "Base AC: %d\n", armour_base_ac(it));
1567 }
1568 break;
1569
1570 default:
1571 break;
1572 }
1573
1574 if (it->notes)
1575 g_string_append_printf(desc, "Notes: %s\n", it->notes);
1576
1577 g_string_append_printf(desc, "Weight: %.2f kg\n",
1578 (float)item_weight(it) / 1000);
1579
1580 g_string_append_printf(desc, "Material: %s",
1581 item_material_name(item_material(it)));
1582
1583 if (shop)
1584 {
1585 /* inside shop - show the item's price */
1586 g_string_append_printf(desc, "\nPrice: %d gold%s", item_price(it),
1587 it->count > 1 ? " each" : "");
1588 }
1589
1590 return g_string_free(desc, FALSE);
1591 }
1592
item_filter_container(item * it)1593 int item_filter_container(item *it)
1594 {
1595 g_assert (it != NULL);
1596 return (it->type == IT_CONTAINER);
1597 }
1598
item_filter_gems(item * it)1599 int item_filter_gems(item *it)
1600 {
1601 g_assert (it != NULL);
1602 return (it->type == IT_GEM);
1603 }
1604
item_filter_gold(item * it)1605 int item_filter_gold(item *it)
1606 {
1607 g_assert (it != NULL);
1608 return (it->type == IT_GOLD);
1609 }
1610
item_filter_not_gold(item * it)1611 int item_filter_not_gold(item *it)
1612 {
1613 g_assert (it != NULL);
1614 return (it->type != IT_GOLD);
1615 }
1616
item_filter_potions(item * it)1617 int item_filter_potions(item *it)
1618 {
1619 g_assert (it != NULL);
1620 return (it->type == IT_POTION);
1621 }
1622
item_filter_legible(item * it)1623 int item_filter_legible(item *it)
1624 {
1625 g_assert (it != NULL);
1626 return (it->type == IT_SCROLL) || (it->type == IT_BOOK);
1627 }
1628
item_filter_unid(item * it)1629 int item_filter_unid(item *it)
1630 {
1631 g_assert (it != NULL);
1632 return (!player_item_identified(nlarn->p, it));
1633 }
1634
item_filter_cursed(item * it)1635 int item_filter_cursed(item *it)
1636 {
1637 g_assert (it != NULL);
1638 return (it->cursed == TRUE);
1639 }
1640
item_filter_cursed_or_unknown(item * it)1641 int item_filter_cursed_or_unknown(item *it)
1642 {
1643 g_assert (it != NULL);
1644 return (item_is_blessable(it->type) && (it->cursed || !it->blessed_known));
1645 }
1646
item_filter_nonblessed(item * it)1647 int item_filter_nonblessed(item *it)
1648 {
1649 g_assert (it != NULL);
1650 return (it->blessed == FALSE
1651 || (it->blessed_known == FALSE && item_is_blessable(it->type)));
1652 }
1653
item_filter_pcd(item * it)1654 int item_filter_pcd(item *it)
1655 {
1656 g_assert (it != NULL);
1657 return (it->type == IT_POTION && it->id == PO_CURE_DIANTHR);
1658 }
1659
item_filter_blank_scroll(item * it)1660 int item_filter_blank_scroll(item *it)
1661 {
1662 g_assert (it != NULL);
1663 return (it->type == IT_SCROLL && it->id == ST_BLANK);
1664 }
1665
item_is_unique(item * it)1666 gboolean item_is_unique(item *it)
1667 {
1668 switch (it->type)
1669 {
1670 case IT_ARMOUR:
1671 return armour_unique(it);
1672 break;
1673 case IT_POTION:
1674 return (it->id == PO_CURE_DIANTHR);
1675 break;
1676 case IT_AMULET:
1677 return TRUE;
1678 break;
1679 case IT_WEAPON:
1680 return weapon_is_unique(it);
1681 break;
1682 default:
1683 return FALSE;
1684 break;
1685 }
1686 }
1687
item_desc_get(item * it,int known)1688 static const char *item_desc_get(item *it, int known)
1689 {
1690 g_assert(it != NULL && it->type > IT_NONE && it->type < IT_MAX);
1691
1692 switch (it->type)
1693 {
1694 case IT_AMULET:
1695 if (known)
1696 return amulet_name(it);
1697 else
1698 return item_material_adjective(item_material(it));
1699 break;
1700
1701 case IT_AMMO:
1702 return ammo_name(it);
1703 break;
1704
1705 case IT_ARMOUR:
1706 if (!known && armour_disguise(it) != AT_MAX)
1707 return armours[armour_disguise(it)].name;
1708 else
1709 return armour_name(it);
1710 break;
1711
1712 case IT_BOOK:
1713 if (known)
1714 return book_name(it);
1715 else
1716 return book_desc(it->id);
1717 break;
1718
1719 case IT_CONTAINER:
1720 return container_name(it);
1721 break;
1722
1723 case IT_POTION:
1724 if (known)
1725 return potion_name(it);
1726 else
1727 return potion_desc(it->id);
1728 break;
1729
1730 case IT_RING:
1731 if (known)
1732 return ring_name(it);
1733 else
1734 return item_material_adjective(item_material(it));
1735 break;
1736
1737 case IT_SCROLL:
1738 if (known)
1739 return scroll_name(it);
1740 else
1741 return scroll_desc(it->id);
1742 break;
1743
1744 case IT_GEM:
1745 return (char *)gem_name(it);
1746 break;
1747
1748 case IT_WEAPON:
1749 return weapon_name(it);
1750 break;
1751
1752 default:
1753 return "";
1754 }
1755 }
1756