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