1 /**
2  * @file
3  * @brief Random and unrandom artefact functions.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "artefact.h"
9 #include "art-enum.h"
10 
11 #include <algorithm>
12 #include <climits>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <functional>
17 
18 #include "branch.h"
19 #include "colour.h"
20 #include "database.h"
21 #include "god-item.h"
22 #include "item-name.h"
23 #include "item-prop.h"
24 #include "item-status-flag-type.h"
25 #include "items.h"
26 #include "libutil.h"
27 #include "makeitem.h"
28 #include "random.h"
29 #include "religion.h"
30 #include "shout.h"
31 #include "spl-book.h"
32 #include "state.h"
33 #include "stringutil.h"
34 #include "tag-version.h"
35 #include "unicode.h"
36 
37 // Putting this here since art-enum.h is generated.
38 
39 // Make sure there's enough room in you.unique_items to hold all
40 // the unrandarts.
41 COMPILE_CHECK((NUM_UNRANDARTS < MAX_UNRANDARTS));
42 // Non-artefact brands and unrandart indexes both go into
43 // item.special, so make sure they don't overlap.
44 COMPILE_CHECK(((int) NUM_SPECIAL_WEAPONS < (int) UNRAND_START));
45 
_god_fits_artefact(const god_type which_god,const item_def & item,bool name_check_only=false)46 static bool _god_fits_artefact(const god_type which_god, const item_def &item,
47                                bool name_check_only = false)
48 {
49     if (which_god == GOD_NO_GOD)
50         return false;
51 
52     // Jellies can't eat artefacts, so their god won't make any.
53     if (which_god == GOD_JIYVA)
54         return false;
55 
56     // First check the item's base_type and sub_type, then check the
57     // item's brand and other randart properties.
58 
59     const bool type_bad = !god_likes_item_type(item, which_god);
60 
61     if (type_bad && !name_check_only)
62     {
63         die("%s attempting to gift invalid type of item.",
64             god_name(which_god).c_str());
65     }
66 
67     if (type_bad)
68         return false;
69 
70     const int brand = get_weapon_brand(item);
71     const int ego   = get_armour_ego_type(item);
72 
73     if (is_evil_god(which_god) && brand == SPWPN_HOLY_WRATH)
74         return false;
75     else if (is_good_god(which_god)
76              && (brand == SPWPN_DRAINING
77                  || brand == SPWPN_PAIN
78                  || brand == SPWPN_VAMPIRISM
79                  || brand == SPWPN_REAPING
80                  || brand == SPWPN_CHAOS
81                  || is_demonic(item)))
82     {
83         return false;
84     }
85 
86     switch (which_god)
87     {
88     case GOD_ZIN:
89         // Lawful god: no mutagenics.
90         if (artefact_property(item, ARTP_CONTAM))
91             return false;
92         break;
93 
94     case GOD_SHINING_ONE:
95         // Crusader god: holiness, honourable combat.
96         if (item.base_type == OBJ_WEAPONS && brand != SPWPN_HOLY_WRATH)
97             return false;
98 
99         if (artefact_property(item, ARTP_INVISIBLE)
100             || artefact_property(item, ARTP_STEALTH) > 0)
101         {
102             return false;
103         }
104         break;
105 
106     case GOD_LUGONU:
107         // Abyss god: corruption.
108         if (item.base_type == OBJ_WEAPONS && brand != SPWPN_DISTORTION)
109             return false;
110         break;
111 
112     case GOD_KIKUBAAQUDGHA:
113         // Necromancy god.
114         if (item.base_type == OBJ_WEAPONS && brand != SPWPN_PAIN)
115             return false;
116     case GOD_SIF_MUNA:
117     case GOD_VEHUMET:
118         // The magic gods: no preventing spellcasting.
119         if (artefact_property(item, ARTP_PREVENT_SPELLCASTING))
120             return false;
121         break;
122 
123     case GOD_TROG:
124         // Limited selection of brands.
125         if (brand != SPWPN_VORPAL
126             && brand != SPWPN_FLAMING
127             && brand != SPWPN_ANTIMAGIC)
128         {
129             return false;
130         }
131 
132         if (artefact_property(item, ARTP_MAGICAL_POWER) > 0
133             || artefact_property(item, ARTP_INTELLIGENCE) > 0)
134         {
135             return false;
136         }
137         break;
138 
139     case GOD_CHEIBRIADOS:
140         // Slow god: no speed, no berserking.
141         if (brand == SPWPN_SPEED || ego == SPARM_RAMPAGING)
142             return false;
143 
144         if (artefact_property(item, ARTP_ANGRY)
145             || artefact_property(item, ARTP_BERSERK)
146             || artefact_property(item, ARTP_RAMPAGING))
147         {
148             return false;
149         }
150         break;
151 
152     case GOD_DITHMENOS:
153         // No reducing stealth.
154         if (artefact_property(item, ARTP_STEALTH) < 0)
155             return false;
156         break;
157 
158     default:
159         break;
160     }
161 
162     return true;
163 }
164 
replace_name_parts(const string & name_in,const item_def & item)165 string replace_name_parts(const string &name_in, const item_def& item)
166 {
167     string name = name_in;
168 
169     const god_type god_gift = origin_as_god_gift(item);
170 
171     // Don't allow "player's Death" type names for god gifts (except
172     // for those from Xom).
173     if (name.find("@player_death@", 0) != string::npos
174         || name.find("@player_doom@", 0) != string::npos)
175     {
176         if (god_gift == GOD_NO_GOD || god_gift == GOD_XOM)
177         {
178             name = replace_all(name, "@player_death@",
179                                "@player_name@"
180                                + getRandNameString("killer_name"));
181             name = replace_all(name, "@player_doom@",
182                                "@player_name@'s "
183                                + getRandNameString("death_or_doom"));
184         }
185         else
186         {
187             // Simply overwrite the name with one of type "God's Favour".
188             name = "of ";
189             name += god_name(god_gift, false);
190             name += "'s ";
191             name += getRandNameString("divine_esteem");
192         }
193     }
194     name = replace_all(name, "@player_name@", you.your_name);
195 
196     name = replace_all(name, "@player_species@",
197                  species::name(you.species, species::SPNAME_GENUS));
198 
199     if (name.find("@branch_name@", 0) != string::npos)
200     {
201         string place = branches[random2(NUM_BRANCHES)].longname;
202         if (!place.empty())
203             name = replace_all(name, "@branch_name@", place);
204     }
205 
206     // Occasionally use long name for Xom (see religion.cc).
207     name = replace_all(name, "@xom_name@", god_name(GOD_XOM, coinflip()));
208 
209     if (name.find("@god_name@", 0) != string::npos)
210     {
211         god_type which_god;
212 
213         // God gifts will always get the gifting god's name.
214         if (god_gift != GOD_NO_GOD)
215             which_god = god_gift;
216         else
217         {
218             do
219             {
220                 which_god = random_god();
221             }
222             while (!_god_fits_artefact(which_god, item, true));
223         }
224 
225         name = replace_all(name, "@god_name@", god_name(which_god, false));
226     }
227 
228     return name;
229 }
230 
231 // Remember: disallow unrandart creation in Abyss/Pan.
232 
233 // Functions defined in art-func.h are referenced in art-data.h
234 #include "art-func.h"
235 
236 static const unrandart_entry unranddata[] =
237 {
238 #include "art-data.h"
239 };
240 
241 static const unrandart_entry *_seekunrandart(const item_def &item);
242 
is_known_artefact(const item_def & item)243 bool is_known_artefact(const item_def &item)
244 {
245     if (!item_type_known(item))
246         return false;
247 
248     return is_artefact(item);
249 }
250 
is_artefact(const item_def & item)251 bool is_artefact(const item_def &item)
252 {
253     return item.flags & ISFLAG_ARTEFACT_MASK;
254 }
255 
256 // returns true is item is a pure randart
is_random_artefact(const item_def & item)257 bool is_random_artefact(const item_def &item)
258 {
259     return item.flags & ISFLAG_RANDART;
260 }
261 
262 /** Is this an unrandart, and if so which one?
263  *
264  *  @param item The item to be checked.
265  *  @param which The unrand enum to be checked against (default 0).
266  *  @returns true if item is an unrand, and if which is not 0, if it is the unrand
267  *           specfied by enum in which.
268  */
is_unrandom_artefact(const item_def & item,int which)269 bool is_unrandom_artefact(const item_def &item, int which)
270 {
271     return item.flags & ISFLAG_UNRANDART
272            && (!which || which == item.unrand_idx);
273 }
274 
is_special_unrandom_artefact(const item_def & item)275 bool is_special_unrandom_artefact(const item_def &item)
276 {
277     return item.flags & ISFLAG_UNRANDART
278            && (_seekunrandart(item)->flags & UNRAND_FLAG_SPECIAL);
279 }
280 
autoid_unrand(item_def & item)281 void autoid_unrand(item_def &item)
282 {
283     if (!(item.flags & ISFLAG_UNRANDART) || item.flags & ISFLAG_KNOW_TYPE)
284         return;
285     const uint16_t uflags = _seekunrandart(item)->flags;
286     if (uflags & UNRAND_FLAG_UNIDED)
287         return;
288 
289     set_ident_flags(item, ISFLAG_IDENT_MASK | ISFLAG_NOTED_ID);
290 }
291 
get_unique_item_status(int art)292 unique_item_status_type get_unique_item_status(int art)
293 {
294     ASSERT_RANGE(art, UNRAND_START + 1, UNRAND_LAST);
295     return you.unique_items[art - UNRAND_START];
296 }
297 
_set_unique_item_status(int art,bool exists)298 static void _set_unique_item_status(int art, bool exists)
299 {
300     ASSERT_RANGE(art, UNRAND_START + 1, UNRAND_LAST);
301 
302     const unique_item_status_type status = !exists
303         ? UNIQ_NOT_EXISTS
304         : !crawl_state.generating_level
305                 // treat unrands that generate in these branches as if they
306                 // were acquired. TODO: there's a potential bug here if every
307                 // octopus king ring generates and the last is acquired. Also,
308                 // I suspect that these getting lost in the abyss isn't handled
309                 // right
310                 || level_id::current().branch == BRANCH_TROVE
311                 || level_id::current().branch == BRANCH_ABYSS
312             ? UNIQ_EXISTS_NONLEVELGEN
313             : UNIQ_EXISTS;
314     you.unique_items[art - UNRAND_START] = status;
315 }
316 
set_unique_item_status(const item_def & item,unique_item_status_type status)317 void set_unique_item_status(const item_def& item,
318                             unique_item_status_type status)
319 {
320     if (item.flags & ISFLAG_UNRANDART)
321         _set_unique_item_status(item.unrand_idx, status);
322 }
323 
324 /**
325  * Fill out the inherent ARTPs corresponding to a given type of armour.
326  *
327  * @param arm           The armour_type of the armour in question.
328  * @param proprt[out]   The properties list to be populated.
329  */
_populate_armour_intrinsic_artps(const armour_type arm,artefact_properties_t & proprt)330 static void _populate_armour_intrinsic_artps(const armour_type arm,
331                                              artefact_properties_t &proprt)
332 {
333     proprt[ARTP_FIRE] += armour_type_prop(arm, ARMF_RES_FIRE);
334     proprt[ARTP_COLD] += armour_type_prop(arm, ARMF_RES_COLD);
335     proprt[ARTP_NEGATIVE_ENERGY] += armour_type_prop(arm, ARMF_RES_NEG);
336     proprt[ARTP_POISON] += armour_type_prop(arm, ARMF_RES_POISON);
337     proprt[ARTP_ELECTRICITY] += armour_type_prop(arm, ARMF_RES_ELEC);
338     proprt[ARTP_RCORR] += armour_type_prop(arm, ARMF_RES_CORR);
339     proprt[ARTP_WILLPOWER] += armour_type_prop(arm, ARMF_WILLPOWER);
340     proprt[ARTP_STEALTH] += armour_type_prop(arm, ARMF_STEALTH);
341     proprt[ARTP_REGENERATION] += armour_type_prop(arm, ARMF_REGENERATION);
342 }
343 
344 /// The artefact properties corresponding to a given piece of jewellery.
345 struct jewellery_fake_artp
346 {
347     /// The artp matching the jewellery (e.g. ARTP_AC for RING_PROTECTION)
348     artefact_prop_type  artp;
349     /// The value of the artp. (E.g. '9' for RING_MAGICAL_POWER.) If set to 0, uses item.plus instead.
350     int                 plus;
351 };
352 
353 static map<jewellery_type, vector<jewellery_fake_artp>> jewellery_artps = {
354     { AMU_REGENERATION, { { ARTP_REGENERATION, 1 } } },
355     { AMU_REFLECTION, { { ARTP_SHIELDING, AMU_REFLECT_SH / 2} } },
356 
357     { RING_MAGICAL_POWER, { { ARTP_MAGICAL_POWER, 9 } } },
358     { RING_FLIGHT, { { ARTP_FLY, 1 } } },
359     { RING_SEE_INVISIBLE, { { ARTP_SEE_INVISIBLE, 1 } } },
360     { RING_STEALTH, { { ARTP_STEALTH, 1 } } },
361 
362     { RING_PROTECTION_FROM_FIRE, { { ARTP_FIRE, 1 } } },
363     { RING_PROTECTION_FROM_COLD, { { ARTP_COLD, 1 } } },
364     { RING_POISON_RESISTANCE, { { ARTP_POISON, 1 } } },
365     { RING_LIFE_PROTECTION, { { ARTP_NEGATIVE_ENERGY, 1 } } },
366     { RING_WILLPOWER, { { ARTP_WILLPOWER, 1 } } },
367     { RING_RESIST_CORROSION, { { ARTP_RCORR, 1 } } },
368 
369     { RING_FIRE, { { ARTP_FIRE, 1 }, { ARTP_COLD, -1 } } },
370     { RING_ICE, { { ARTP_COLD, 1 }, { ARTP_FIRE, -1 } } },
371 
372     { RING_STRENGTH, { { ARTP_STRENGTH, 0 } } },
373     { RING_INTELLIGENCE, { { ARTP_INTELLIGENCE, 0 } } },
374     { RING_DEXTERITY, { { ARTP_DEXTERITY, 0 } } },
375     { RING_PROTECTION, { { ARTP_AC, 0 } } },
376     { RING_EVASION, { { ARTP_EVASION, 0 } } },
377     { RING_SLAYING, { { ARTP_SLAYING, 0 } } },
378 };
379 
380 /**
381  * Fill out the inherent ARTPs corresponding to a given type of jewellery.
382  *
383  * @param arm           The jewellery in question.
384  * @param proprt[out]   The properties list to be populated.
385  * @param known[out]    The props which are known.
386  */
_populate_jewel_intrinsic_artps(const item_def & item,artefact_properties_t & proprt,artefact_known_props_t & known)387 static void _populate_jewel_intrinsic_artps(const item_def &item,
388                                               artefact_properties_t &proprt,
389                                               artefact_known_props_t &known)
390 {
391     const jewellery_type jewel = (jewellery_type)item.sub_type;
392     vector<jewellery_fake_artp> *props = map_find(jewellery_artps, jewel);
393     if (!props)
394         return;
395 
396     const bool id_props = item_ident(item, ISFLAG_KNOW_PROPERTIES)
397                           || item_ident(item, ISFLAG_KNOW_TYPE);
398 
399     for (const auto &fake_artp : *props)
400     {
401         proprt[fake_artp.artp] += fake_artp.plus ? fake_artp.plus : item.plus;
402         if (id_props)
403             known[fake_artp.artp] = true;
404     }
405 }
406 
407 
408 /**
409  * Fill out the inherent ARTPs corresponding to a given item.
410  *
411  * @param arm           The item in question.
412  * @param proprt[out]   The properties list to be populated.
413  * @param known[out]    The props which are known.
414  */
_populate_item_intrinsic_artps(const item_def & item,artefact_properties_t & proprt,artefact_known_props_t & known)415 static void _populate_item_intrinsic_artps(const item_def &item,
416                                              artefact_properties_t &proprt,
417                                              artefact_known_props_t &known)
418 {
419     switch (item.base_type)
420     {
421         case OBJ_ARMOUR:
422             _populate_armour_intrinsic_artps((armour_type)item.sub_type,
423                                              proprt);
424             break;
425         case OBJ_JEWELLERY:
426             _populate_jewel_intrinsic_artps(item, proprt, known);
427             break;
428         default:
429             break;
430     }
431 }
432 
artefact_desc_properties(const item_def & item,artefact_properties_t & proprt,artefact_known_props_t & known)433 void artefact_desc_properties(const item_def &item,
434                               artefact_properties_t &proprt,
435                               artefact_known_props_t &known)
436 {
437     // Randart books have no randart properties.
438     if (item.base_type == OBJ_BOOKS)
439         return;
440 
441     // actual artefact properties
442     artefact_properties(item, proprt);
443     artefact_known_properties(item, known);
444 
445     // fake artefact properties (intrinsics)
446     _populate_item_intrinsic_artps(item, proprt, known);
447 }
448 
_add_randart_weapon_brand(const item_def & item,artefact_properties_t & item_props)449 static void _add_randart_weapon_brand(const item_def &item,
450                                     artefact_properties_t &item_props)
451 {
452     const int item_type = item.sub_type;
453 
454     if (!is_weapon_brand_ok(item_type, item_props[ARTP_BRAND], true))
455         item_props[ARTP_BRAND] = SPWPN_NORMAL;
456 
457     if (item_props[ARTP_BRAND] != SPWPN_NORMAL)
458         return;
459 
460     if (is_range_weapon(item))
461     {
462         item_props[ARTP_BRAND] = random_choose_weighted(
463             2, SPWPN_SPEED,
464             4, SPWPN_VENOM,
465             4, SPWPN_VORPAL,
466             4, SPWPN_FLAMING,
467             4, SPWPN_FREEZING);
468 
469         if (item_attack_skill(item) == SK_CROSSBOWS)
470         {
471             // Penetration and electrocution are only allowed on
472             // crossbows. This may change in future.
473             if (one_chance_in(5))
474                 item_props[ARTP_BRAND] = SPWPN_ELECTROCUTION;
475             else if (one_chance_in(5))
476                 item_props[ARTP_BRAND] = SPWPN_PENETRATION;
477         }
478     }
479     else if (is_demonic(item) && x_chance_in_y(7, 9))
480     {
481         item_props[ARTP_BRAND] = random_choose(
482             SPWPN_DRAINING,
483             SPWPN_FLAMING,
484             SPWPN_FREEZING,
485             SPWPN_ELECTROCUTION,
486             SPWPN_VAMPIRISM,
487             SPWPN_PAIN,
488             SPWPN_VENOM);
489         // fall back to regular melee brands 2/9 of the time
490     }
491     else
492     {
493         item_props[ARTP_BRAND] = random_choose_weighted(
494             73, SPWPN_VORPAL,
495             34, SPWPN_FLAMING,
496             34, SPWPN_FREEZING,
497             26, SPWPN_VENOM,
498             26, SPWPN_DRAINING,
499             13, SPWPN_HOLY_WRATH,
500             13, SPWPN_ELECTROCUTION,
501             13, SPWPN_SPEED,
502             13, SPWPN_VAMPIRISM,
503             13, SPWPN_PAIN,
504             13, SPWPN_ANTIMAGIC,
505             13, SPWPN_PROTECTION,
506             13, SPWPN_SPECTRAL,
507              3, SPWPN_DISTORTION,
508              3, SPWPN_CHAOS);
509     }
510 
511     // no brand = magic flag to reject and retry
512     if (!is_weapon_brand_ok(item_type, item_props[ARTP_BRAND], true))
513         item_props[ARTP_BRAND] = SPWPN_NORMAL;
514 }
515 
516 /**
517  * Can the given artefact property be placed on the given item?
518  *
519  * @param prop          The artefact property in question (e.g. ARTP_BLINK).
520  * @param item          The item in question.
521  * @param extant_props  The properties already chosen for the artefact.
522  * @return              True if the property doesn't conflict with any chosen
523  *                      or intrinsic properties, and doesn't violate any other
524  *                      special constraints (e.g. no slaying on weapons);
525  *                      false otherwise.
526  */
_artp_can_go_on_item(artefact_prop_type prop,const item_def & item,const artefact_properties_t & extant_props)527 static bool _artp_can_go_on_item(artefact_prop_type prop, const item_def &item,
528                                  const artefact_properties_t &extant_props)
529 {
530     // see also _randart_is_conflicting
531 
532     artefact_properties_t intrinsic_proprt;
533     intrinsic_proprt.init(0);
534     artefact_known_props_t _;
535     _populate_item_intrinsic_artps(item, intrinsic_proprt, _);
536     if (intrinsic_proprt[prop])
537         return false; // don't duplicate intrinsic props
538 
539     const object_class_type item_class = item.base_type;
540     // Categorise items by whether they're quick to swap or not. Some artefact
541     // properties aren't appropriate on easily swappable items.
542     const bool non_swappable = item_class == OBJ_ARMOUR
543                                || item_class == OBJ_JEWELLERY
544                                   && jewellery_is_amulet(item);
545 
546     // warning: using some item calls may not work here, for example,
547     // get_weapon_brand; the `item` object is not fully set up.
548     switch (prop)
549     {
550         // weapons already have slaying
551         case ARTP_SLAYING:
552             return item_class != OBJ_WEAPONS;
553         // prevent properties that conflict with naga innates
554         case ARTP_POISON:
555         case ARTP_SEE_INVISIBLE:
556             return !item.is_type(OBJ_ARMOUR, ARM_BARDING);
557         case ARTP_RAMPAGING:
558             return non_swappable && !item.is_type(OBJ_ARMOUR, ARM_BARDING);
559         // prevent properties that conflict with each other
560         case ARTP_CORRODE:
561             return !extant_props[ARTP_RCORR] && !intrinsic_proprt[ARTP_RCORR];
562         case ARTP_RCORR:
563             return !extant_props[ARTP_CORRODE];
564         case ARTP_MAGICAL_POWER:
565             return item_class != OBJ_WEAPONS
566                    || extant_props[ARTP_BRAND] != SPWPN_ANTIMAGIC;
567         case ARTP_BLINK:
568             return !extant_props[ARTP_PREVENT_TELEPORTATION];
569         case ARTP_PREVENT_TELEPORTATION:
570             return !extant_props[ARTP_BLINK] && non_swappable;
571         // only on melee weapons
572         case ARTP_BERSERK:
573         case ARTP_ANGRY:
574         case ARTP_NOISE:
575             return item_class == OBJ_WEAPONS && !is_range_weapon(item);
576         case ARTP_PREVENT_SPELLCASTING:
577             if (item.is_type(OBJ_JEWELLERY, AMU_MANA_REGENERATION))
578                 return false;
579             // fallthrough
580         case ARTP_REGENERATION:
581         case ARTP_HARM:
582         case ARTP_INVISIBLE:
583             // only on items that can't be quickly swapped
584             return non_swappable;
585         // prevent on armour (since it's swapped infrequently) and rings (since
586         // 2 slots reduces the pressure to swap)
587         case ARTP_FRAGILE:
588             return item_class != OBJ_ARMOUR
589                    && (item_class != OBJ_JEWELLERY
590                        || jewellery_is_amulet(item));
591         case ARTP_ARCHMAGI:
592             return item.is_type(OBJ_ARMOUR, ARM_ROBE);
593         default:
594             return true;
595     }
596 }
597 
598 /// Generation info for a type of artefact property.
599 struct artefact_prop_data
600 {
601     /// The name of the prop, as displayed on item annotations, etc.
602     const char *name;
603     /// The types of values this prop can have (e.g. bool, positive int, int)
604     artp_value_type value_types;
605     /// Weight in randart selection (higher = more common)
606     int weight;
607     /// Randomly generate a 'good' value; null if this prop is never good
608     function<int ()> gen_good_value;
609     /// Randomly generate a 'bad' value; null if this prop is never bad
610     function<int ()> gen_bad_value;
611     /// The value beyond which the artp should not be repeatedly applied.
612     int max_dup;
613     /// The amount to increment the odds of a property being reapplied
614     int odds_inc;
615 };
616 
617 /// Generate 'good' values for stat artps (e.g. ARTP_STRENGTH)
_gen_good_stat_artp()618 static int _gen_good_stat_artp() { return 1 + random2(3); }
619 
620 /// Generate 'bad' values for stat artps (e.g. ARTP_STRENGTH)
_gen_bad_stat_artp()621 static int _gen_bad_stat_artp() { return -2 - random2(4); }
622 
623 /// Generate 'good' values for resist-ish artps (e.g. ARTP_FIRE)
_gen_good_res_artp()624 static int _gen_good_res_artp() { return 1; }
625 
626 /// Generate 'bad' values for resist-ish artps (e.g. ARTP_FIRE)
_gen_bad_res_artp()627 static int _gen_bad_res_artp() { return -1; }
628 
629 /// Generate 'good' values for ARTP_HP/ARTP_MAGICAL_POWER
_gen_good_hpmp_artp()630 static int _gen_good_hpmp_artp() { return 9; }
631 
632 /// Generate 'bad' values for ARTP_HP/ARTP_MAGICAL_POWER
_gen_bad_hpmp_artp()633 static int _gen_bad_hpmp_artp() { return -_gen_good_hpmp_artp(); }
634 
635 /// Generation info for artefact properties.
636 static const artefact_prop_data artp_data[] =
637 {
638     { "Brand", ARTP_VAL_BRAND, 0, nullptr, nullptr, 0, 0 }, // ARTP_BRAND,
639     { "AC", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0}, // ARTP_AC,
640     { "EV", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0 }, // ARTP_EVASION,
641     { "Str", ARTP_VAL_ANY, 100,     // ARTP_STRENGTH,
642         _gen_good_stat_artp, _gen_bad_stat_artp, 7, 1 },
643     { "Int", ARTP_VAL_ANY, 100,     // ARTP_INTELLIGENCE,
644         _gen_good_stat_artp, _gen_bad_stat_artp, 7, 1 },
645     { "Dex", ARTP_VAL_ANY, 100,     // ARTP_DEXTERITY,
646         _gen_good_stat_artp, _gen_bad_stat_artp, 7, 1 },
647     { "rF", ARTP_VAL_ANY, 60,       // ARTP_FIRE,
648         _gen_good_res_artp, _gen_bad_res_artp, 2, 4},
649     { "rC", ARTP_VAL_ANY, 60,       // ARTP_COLD,
650         _gen_good_res_artp, _gen_bad_res_artp, 2, 4 },
651     { "rElec", ARTP_VAL_BOOL, 55,   // ARTP_ELECTRICITY,
__anon432693100102() 652         []() { return 1; }, nullptr, 0, 0  },
653     { "rPois", ARTP_VAL_BOOL, 55,   // ARTP_POISON,
__anon432693100202() 654         []() { return 1; }, nullptr, 0, 0 },
655     { "rN", ARTP_VAL_ANY, 55,       // ARTP_NEGATIVE_ENERGY,
656         _gen_good_res_artp, nullptr, 2, 4 },
657     { "Will", ARTP_VAL_ANY, 50,       // ARTP_WILLPOWER,
658         _gen_good_res_artp, _gen_bad_res_artp, 2, 4 },
659     { "SInv", ARTP_VAL_BOOL, 30,    // ARTP_SEE_INVISIBLE,
__anon432693100302() 660         []() { return 1; }, nullptr, 0, 0 },
661     { "+Inv", ARTP_VAL_BOOL, 15,    // ARTP_INVISIBLE,
__anon432693100402() 662         []() { return 1; }, nullptr, 0, 0 },
663     { "Fly", ARTP_VAL_BOOL, 15,    // ARTP_FLY,
__anon432693100502() 664         []() { return 1; }, nullptr, 0, 0 },
665     { "+Blink", ARTP_VAL_BOOL, 15,  // ARTP_BLINK,
__anon432693100602() 666         []() { return 1; }, nullptr, 0, 0 },
667     { "+Rage", ARTP_VAL_BOOL, 15,   // ARTP_BERSERK,
__anon432693100702() 668         []() { return 1; }, nullptr, 0, 0 },
669     { "*Noise", ARTP_VAL_POS, 25,    // ARTP_NOISE,
__anon432693100802() 670         nullptr, []() { return 2; }, 0, 0 },
671     { "-Cast", ARTP_VAL_BOOL, 25,   // ARTP_PREVENT_SPELLCASTING,
__anon432693100902() 672         nullptr, []() { return 1; }, 0, 0 },
673     { "*Tele", ARTP_VAL_BOOL,  0,   // ARTP_CAUSE_TELEPORTATION,
__anon432693100a02() 674         nullptr, []() { return 1; }, 0, 0 },
675     { "-Tele", ARTP_VAL_BOOL, 25,   // ARTP_PREVENT_TELEPORTATION,
__anon432693100b02() 676         nullptr, []() { return 1; }, 0, 0 },
677     { "*Rage", ARTP_VAL_POS, 25,    // ARTP_ANGRY,
__anon432693100c02() 678         nullptr, []() { return 5; }, 0, 0 },
679 #if TAG_MAJOR_VERSION == 34
680     { "Hungry", ARTP_VAL_POS, 0, nullptr, nullptr, 0, 0 },// ARTP_METABOLISM,
681 #endif
682     { "Contam", ARTP_VAL_POS, 20,   // ARTP_CONTAM
__anon432693100d02() 683         nullptr, []() { return 1; }, 0, 0 },
684 #if TAG_MAJOR_VERSION == 34
685     { "Acc", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0 }, // ARTP_ACCURACY,
686 #endif
687     { "Slay", ARTP_VAL_ANY, 30,     // ARTP_SLAYING,
__anon432693100e02() 688       []() { return 2 + random2(2); },
__anon432693100f02() 689       []() { return -(2 + random2(5)); }, 3, 2 },
690 #if TAG_MAJOR_VERSION == 34
691     { "*Curse", ARTP_VAL_POS, 0, nullptr, nullptr, 0 }, // ARTP_CURSE,
692 #endif
693     { "Stlth", ARTP_VAL_ANY, 40,    // ARTP_STEALTH,
694         _gen_good_res_artp, _gen_bad_res_artp, 0, 0 },
695     { "MP", ARTP_VAL_ANY, 15,       // ARTP_MAGICAL_POWER,
696         _gen_good_hpmp_artp, _gen_bad_hpmp_artp, 0, 0 },
697     { "Delay", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0 }, // ARTP_BASE_DELAY,
698     { "HP", ARTP_VAL_ANY, 0,       // ARTP_HP,
699         _gen_good_hpmp_artp, _gen_bad_hpmp_artp, 0, 0 },
700     { "Clar", ARTP_VAL_BOOL, 0, nullptr, nullptr, 0, 0 }, // ARTP_CLARITY,
701     { "BAcc", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0 },  // ARTP_BASE_ACC,
702     { "BDam", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0 },  // ARTP_BASE_DAM,
703     { "RMsl", ARTP_VAL_BOOL, 0, nullptr, nullptr, 0, 0 }, // ARTP_RMSL,
704 #if TAG_MAJOR_VERSION == 34
705     { "+Fog", ARTP_VAL_BOOL, 0, nullptr, nullptr, 0, 0 }, // ARTP_FOG,
706 #endif
707     { "Regen", ARTP_VAL_BOOL, 35,   // ARTP_REGENERATION,
__anon432693101002() 708         []() { return 1; }, nullptr, 0, 0 },
709 #if TAG_MAJOR_VERSION == 34
710     { "SustAt", ARTP_VAL_BOOL, 0, nullptr, nullptr, 0, 0 }, // ARTP_SUSTAT,
711 #endif
712     { "nupgr", ARTP_VAL_BOOL, 0, nullptr, nullptr, 0, 0 },// ARTP_NO_UPGRADE,
713     { "rCorr", ARTP_VAL_BOOL, 40,   // ARTP_RCORR,
__anon432693101102() 714         []() { return 1; }, nullptr, 0, 0 },
715     { "rMut", ARTP_VAL_BOOL, 0, nullptr, nullptr, 0, 0 }, // ARTP_RMUT,
716 #if TAG_MAJOR_VERSION == 34
717     { "+Twstr", ARTP_VAL_BOOL, 0,   // ARTP_TWISTER,
__anon432693101202() 718         []() { return 1; }, nullptr, 0, 0 },
719 #endif
720     { "*Corrode", ARTP_VAL_BOOL, 25, // ARTP_CORRODE,
__anon432693101302() 721         nullptr, []() { return 1; }, 0, 0 },
722     { "Drain", ARTP_VAL_BOOL, 25, // ARTP_DRAIN,
__anon432693101402() 723         nullptr, []() { return 1; }, 0, 0 },
724     { "*Slow", ARTP_VAL_BOOL, 25, // ARTP_SLOW,
__anon432693101502() 725         nullptr, []() { return 1; }, 0, 0 },
726     { "Fragile", ARTP_VAL_BOOL, 25, // ARTP_FRAGILE,
__anon432693101602() 727         nullptr, []() { return 1; }, 0, 0 },
728     { "SH", ARTP_VAL_ANY, 0, nullptr, nullptr, 0, 0 }, // ARTP_SHIELDING,
729     { "Harm", ARTP_VAL_BOOL, 25, // ARTP_HARM,
__anon432693101702() 730         []() {return 1;}, nullptr, 0, 0},
731     { "Rampage", ARTP_VAL_BOOL, 25, // ARTP_RAMPAGING,
__anon432693101802() 732         []() {return 1;}, nullptr, 0, 0},
733     { "Archmagi", ARTP_VAL_BOOL, 25, // ARTP_ARCHMAGI,
__anon432693101902() 734         []() {return 1;}, nullptr, 0, 0},
735 };
736 COMPILE_CHECK(ARRAYSZ(artp_data) == ARTP_NUM_PROPERTIES);
737 // weights sum to 1000
738 
739 
740 /**
741  * Is it possible for the given artp to be generated with 'good' values
742  * (generally helpful to the player?
743  * E.g. ARTP_SLAYING, ARTP_FIRE, ARTP_REGENERATION, etc.
744  *
745  * @param prop      The artefact property in question.
746  * @return      true if the ARTP is ever generated as a 'good' prop; false
747  *              otherwise.
748  */
artp_potentially_good(artefact_prop_type prop)749 bool artp_potentially_good(artefact_prop_type prop)
750 {
751     return artp_data[prop].gen_good_value != nullptr;
752 }
753 
754 /**
755  * Is it possible for the given artp to be generated with 'bad' values
756  * (generally harmful to the player?
757  * E.g. ARTP_SLAYING, ARTP_AC, ARTP_CONTAM, etc.
758  *
759  * @param prop      The artefact property in question.
760  * @return      true if the ARTP is ever generated as a 'bad' prop; false
761  *              otherwise.
762  */
artp_potentially_bad(artefact_prop_type prop)763 bool artp_potentially_bad(artefact_prop_type prop)
764 {
765     return artp_data[prop].gen_bad_value != nullptr;
766 }
767 
768 /**
769  * What type of values can this prop have?
770  *
771  * Positive, boolean (0 or 1), or any (integer).
772  *
773  * There should be a better way of expressing this...
774  *
775  * @param prop      The prop type in question.
776  * @return          Possible value types for the prop.
777  */
artp_potential_value_types(artefact_prop_type prop)778 artp_value_type artp_potential_value_types(artefact_prop_type prop)
779 {
780     ASSERT_RANGE(prop, 0, ARRAYSZ(artp_data));
781     return artp_data[prop].value_types;
782 }
783 
784 /**
785  * Return the name for a given artefact property.
786  *
787  * @param prop      The artp in question.
788  * @return          A name; e.g. rCorr, Slay, HP, etc.
789  */
artp_name(artefact_prop_type prop)790 const char *artp_name(artefact_prop_type prop)
791 {
792     ASSERT_RANGE(prop, 0, ARRAYSZ(artp_data));
793     return artp_data[prop].name;
794 }
795 
796 /**
797  * Add a 'good' version of a given prop to the given set of item props.
798  *
799  * The property may already exist in the set; if so, increase its value.
800  *
801  * @param prop[in]              The prop to be added.
802  * @param item_props[out]       The list of item props to be added to.
803  */
_add_good_randart_prop(artefact_prop_type prop,artefact_properties_t & item_props)804 static void _add_good_randart_prop(artefact_prop_type prop,
805                                    artefact_properties_t &item_props)
806 {
807     // Add one to the starting value for stat bonuses.
808     if ((prop == ARTP_STRENGTH
809          || prop == ARTP_INTELLIGENCE
810          || prop == ARTP_DEXTERITY)
811         && item_props[prop] == 0)
812     {
813         item_props[prop]++;
814     }
815 
816     item_props[prop] += artp_data[prop].gen_good_value();
817 }
818 
819 /**
820  * Generate the properties for a randart. We start with a quality level, then
821  * determine number of good and bad properties from that level. The more good
822  * properties we have, the more likely we are to add some bad properties in
823  * to make it a bit more interesting.
824  *
825  * For each "good" and "bad" property we want, we randomly pick from the total
826  * list of good or bad properties; if the picked property already exists and
827  * can have variable levels, we increment the levels by a certain amount. There
828  * is a maximum number of distinct properties that can be applied to a randart
829  * (for legibility, mostly), so overflow gets picked as increments to
830  * variable-level properties when possible.
831  *
832  * @param item          The item to apply properties to.
833  * @param item_props    The properties of that item.
834  * @param quality       How high quality the randart will be, measured in number
835                         of rolls for good property boosts.
836  * @param max_bad_props The maximum number of bad properties this artefact can
837                         be given.
838  */
_get_randart_properties(const item_def & item,artefact_properties_t & item_props,int quality=0,const int max_bad_props=2)839 static void _get_randart_properties(const item_def &item,
840                                     artefact_properties_t &item_props,
841                                     int quality = 0,
842                                     const int max_bad_props = 2)
843 {
844     const object_class_type item_class = item.base_type;
845 
846     // If we didn't receive a quality level, figure out how good we want the
847     // artefact to be. The default calculation range is 1 to 7.
848     if (quality < 1)
849         quality = max(1, binomial(7, 30));
850 
851     // then consider adding bad properties. the better the artefact, the more
852     // likely we add a bad property, up to a max of 2.
853     int bad = min(binomial(1 + div_rand_round(quality, 5), 30), max_bad_props);
854     // we start by assuming we'll allow one good property per quality level
855     // and an additional one for each bad property.
856     int good = quality + bad;
857     // but we want avoid generating more then 4-ish properties properties or
858     // things get spammy. Extra "good" properties will be used to enhance
859     // properties only, not to add more distinct properties. There is still a
860     // small chance of >4 properties.
861     int max_properties = 4 + one_chance_in(20);
862     max_properties += one_chance_in(40);
863     int enhance = 0;
864     if (good + bad > max_properties)
865     {
866         enhance = good + bad - max_properties;
867         good = max_properties - bad;
868     }
869 
870     // initialize a vector of weighted artefact properties to pick from
871     vector<pair<artefact_prop_type, int>> art_prop_weights;
872     for (int i = 0; i < ARTP_NUM_PROPERTIES; ++i)
873     {
874         art_prop_weights.emplace_back(static_cast<artefact_prop_type>(i),
875                                       artp_data[i].weight);
876     }
877     item_props.init(0);
878 
879     // make sure all weapons have a brand
880     if (item_class == OBJ_WEAPONS)
881         _add_randart_weapon_brand(item, item_props);
882 
883     // randomly pick properties from the list, choose an appropriate value,
884     // then subtract them from the good/bad/enhance count as needed
885     // the 'enhance' count is not guaranteed to be used.
886     while (good > 0 || bad > 0)
887     {
888         const artefact_prop_type *prop_ptr
889             = random_choose_weighted(art_prop_weights);
890         ASSERTM(prop_ptr, "all %d randart properties have weight 0?",
891                 (int) art_prop_weights.size());
892         const artefact_prop_type prop = *prop_ptr;
893 
894         if (!_artp_can_go_on_item(prop, item, item_props))
895             continue;
896 
897         // should we try to generate a good or bad version of the prop?
898         const bool can_gen_good = good > 0 && artp_potentially_good(prop);
899         const bool can_gen_bad = bad > 0 && artp_potentially_bad(prop);
900         const bool gen_good = can_gen_good && (!can_gen_bad || coinflip());
901 
902         if (gen_good)
903         {
904             // potentially increment the value of the property more than once,
905             // using up a good property each time.
906             // always do so if there's any 'enhance' left, if possible.
907             for (int chance_denom = 1;
908                  item_props[prop] <= artp_data[prop].max_dup
909                     && (enhance > 0
910                         || good > 0 && one_chance_in(chance_denom));
911                  chance_denom += artp_data[prop].odds_inc)
912             {
913                 _add_good_randart_prop(prop, item_props);
914                 if (enhance > 0 && chance_denom > 1)
915                     --enhance;
916                 else
917                     --good;
918             }
919         }
920         else if (can_gen_bad)
921         {
922             item_props[prop] = artp_data[prop].gen_bad_value();
923             --bad;
924         }
925         else
926             continue;
927 
928         // don't choose the same prop twice
929         const auto weight_tuple = make_pair(prop, artp_data[prop].weight);
930         const auto old_end = art_prop_weights.end();
931         const auto new_end = std::remove(art_prop_weights.begin(), old_end,
932                                          weight_tuple);
933         // That should have removed exactly one entry.
934         ASSERT(new_end == old_end - 1);
935         art_prop_weights.erase(new_end, old_end);
936     }
937 }
938 
setup_unrandart(item_def & item,bool creating)939 void setup_unrandart(item_def &item, bool creating)
940 {
941     ASSERT(is_unrandom_artefact(item));
942     CrawlVector &rap = item.props[ARTEFACT_PROPS_KEY].get_vector();
943     const unrandart_entry *unrand = _seekunrandart(item);
944 
945     if (unrand->prpty[ARTP_NO_UPGRADE] && !creating)
946         return; // don't mangle mutable items
947 
948     for (int i = 0; i < ART_PROPERTIES; i++)
949         rap[i] = static_cast<short>(unrand->prpty[i]);
950 
951     item.base_type = unrand->base_type;
952     item.sub_type  = unrand->sub_type;
953     item.plus      = unrand->plus;
954 }
955 
_init_artefact_properties(item_def & item)956 static bool _init_artefact_properties(item_def &item)
957 {
958     ASSERT(is_artefact(item));
959 
960     if (is_unrandom_artefact(item))
961     {
962         setup_unrandart(item);
963         return true;
964     }
965 
966     CrawlVector &rap = item.props[ARTEFACT_PROPS_KEY].get_vector();
967     for (vec_size i = 0; i < ART_PROPERTIES; i++)
968         rap[i] = static_cast<short>(0);
969 
970     ASSERT(item.base_type != OBJ_BOOKS);
971 
972     artefact_properties_t prop;
973     prop.init(0);
974     _get_randart_properties(item, prop);
975 
976     for (int i = 0; i < ART_PROPERTIES; i++)
977         rap[i] = static_cast<short>(prop[i]);
978 
979     return true;
980 }
981 
artefact_known_properties(const item_def & item,artefact_known_props_t & known)982 void artefact_known_properties(const item_def &item,
983                                artefact_known_props_t &known)
984 {
985     ASSERT(is_artefact(item));
986     if (!item.props.exists(KNOWN_PROPS_KEY)) // randbooks
987         return;
988 
989     const CrawlStoreValue &_val = item.props[KNOWN_PROPS_KEY];
990     ASSERT(_val.get_type() == SV_VEC);
991     const CrawlVector &known_vec = _val.get_vector();
992     ASSERT(known_vec.get_type()     == SV_BOOL);
993     ASSERT(known_vec.size()         == ART_PROPERTIES);
994     ASSERT(known_vec.get_max_size() == ART_PROPERTIES);
995 
996     if (item_ident(item, ISFLAG_KNOW_PROPERTIES))
997     {
998         for (vec_size i = 0; i < ART_PROPERTIES; i++)
999             known[i] = static_cast<bool>(true);
1000     }
1001     else
1002     {
1003         for (vec_size i = 0; i < ART_PROPERTIES; i++)
1004             known[i] = known_vec[i];
1005     }
1006 }
1007 
artefact_properties(const item_def & item,artefact_properties_t & proprt)1008 void artefact_properties(const item_def &item,
1009                          artefact_properties_t  &proprt)
1010 {
1011     ASSERT(is_artefact(item));
1012     ASSERT(item.base_type != OBJ_BOOKS);
1013     ASSERT(item.props.exists(ARTEFACT_PROPS_KEY) || is_unrandom_artefact(item));
1014 
1015     if (item.props.exists(ARTEFACT_PROPS_KEY))
1016     {
1017         const CrawlVector &rap_vec =
1018             item.props[ARTEFACT_PROPS_KEY].get_vector();
1019         ASSERT(rap_vec.get_type()     == SV_SHORT);
1020         ASSERT(rap_vec.size()         == ART_PROPERTIES);
1021         ASSERT(rap_vec.get_max_size() == ART_PROPERTIES);
1022 
1023         for (vec_size i = 0; i < ART_PROPERTIES; i++)
1024             proprt[i] = rap_vec[i].get_short();
1025     }
1026     else // if (is_unrandom_artefact(item))
1027     {
1028         const unrandart_entry *unrand = _seekunrandart(item);
1029 
1030         for (int i = 0; i < ART_PROPERTIES; i++)
1031             proprt[i] = static_cast<short>(unrand->prpty[i]);
1032     }
1033 }
1034 
artefact_property(const item_def & item,artefact_prop_type prop)1035 int artefact_property(const item_def &item, artefact_prop_type prop)
1036 {
1037     ASSERT(is_artefact(item));
1038     ASSERT(item.base_type != OBJ_BOOKS);
1039     ASSERT(item.props.exists(ARTEFACT_PROPS_KEY) || is_unrandom_artefact(item));
1040     if (item.props.exists(ARTEFACT_PROPS_KEY))
1041     {
1042         const CrawlVector &rap_vec =
1043             item.props[ARTEFACT_PROPS_KEY].get_vector();
1044         return rap_vec[prop].get_short();
1045     }
1046     else // if (is_unrandom_artefact(item))
1047     {
1048         const unrandart_entry *unrand = _seekunrandart(item);
1049         return static_cast<short>(unrand->prpty[prop]);
1050 
1051     }
1052 }
1053 
1054 /**
1055  * Check whether a particular property's value is known to the player.
1056  */
artefact_property_known(const item_def & item,artefact_prop_type prop)1057 bool artefact_property_known(const item_def &item, artefact_prop_type prop)
1058 {
1059     ASSERT(is_artefact(item));
1060     if (item_ident(item, ISFLAG_KNOW_PROPERTIES))
1061         return true;
1062 
1063     if (!item.props.exists(KNOWN_PROPS_KEY)) // randbooks
1064         return false;
1065 
1066     const CrawlVector &known_vec = item.props[KNOWN_PROPS_KEY].get_vector();
1067     ASSERT(known_vec.get_type()     == SV_BOOL);
1068     ASSERT(known_vec.size()         == ART_PROPERTIES);
1069 
1070     return known_vec[prop].get_bool();
1071 }
1072 
1073 /**
1074  * check what the player knows about an a particular property.
1075  */
artefact_known_property(const item_def & item,artefact_prop_type prop)1076 int artefact_known_property(const item_def &item, artefact_prop_type prop)
1077 {
1078     return artefact_property_known(item, prop) ? artefact_property(item, prop)
1079                                                : 0;
1080 }
1081 
_artefact_num_props(const artefact_properties_t & proprt)1082 static int _artefact_num_props(const artefact_properties_t &proprt)
1083 {
1084     int num = 0;
1085 
1086     // Count all properties.
1087     for (int i = 0; i < ARTP_NUM_PROPERTIES; ++i)
1088         if (proprt[i] != 0)
1089             num++;
1090 
1091     return num;
1092 }
1093 
artefact_learn_prop(item_def & item,artefact_prop_type prop)1094 void artefact_learn_prop(item_def &item, artefact_prop_type prop)
1095 {
1096     ASSERT(is_artefact(item));
1097     ASSERT(item.props.exists(KNOWN_PROPS_KEY));
1098     CrawlStoreValue &_val = item.props[KNOWN_PROPS_KEY];
1099     ASSERT(_val.get_type() == SV_VEC);
1100     CrawlVector &known_vec = _val.get_vector();
1101     ASSERT(known_vec.get_type()     == SV_BOOL);
1102     ASSERT(known_vec.size()         == ART_PROPERTIES);
1103     ASSERT(known_vec.get_max_size() == ART_PROPERTIES);
1104 
1105     if (item_ident(item, ISFLAG_KNOW_PROPERTIES))
1106         return;
1107 
1108     known_vec[prop] = static_cast<bool>(true);
1109 }
1110 
_get_artefact_type(const item_def & item,bool appear=false)1111 static string _get_artefact_type(const item_def &item, bool appear = false)
1112 {
1113     switch (item.base_type)
1114     {
1115     case OBJ_BOOKS:
1116         return "book";
1117     case OBJ_WEAPONS:
1118         return "weapon";
1119     case OBJ_ARMOUR:
1120         if (item.sub_type == ARM_ROBE)
1121             return "robe";
1122         if (get_item_slot(item) == EQ_BODY_ARMOUR)
1123             return "body armour";
1124         return "armour";
1125     case OBJ_JEWELLERY:
1126         // Distinguish between amulets and rings only in appearance.
1127         if (!appear)
1128             return "jewellery";
1129 
1130         if (jewellery_is_amulet(item))
1131             return "amulet";
1132         else
1133             return "ring";
1134     default:
1135         return "artefact";
1136     }
1137 }
1138 
_pick_db_name(const item_def & item)1139 static bool _pick_db_name(const item_def &item)
1140 {
1141     switch (item.base_type)
1142     {
1143     case OBJ_WEAPONS:
1144     case OBJ_ARMOUR:
1145         return coinflip();
1146     case OBJ_JEWELLERY:
1147         return one_chance_in(5);
1148     default:
1149         return false;
1150     }
1151 }
1152 
_artefact_name_lookup(const item_def & item,const string & lookup)1153 static string _artefact_name_lookup(const item_def &item, const string &lookup)
1154 {
1155     const string name = getRandNameString(lookup);
1156     return name.empty() ? name : replace_name_parts(name, item);
1157 }
1158 
_artefact_name_lookup(string & result,const item_def & item,const string & lookup)1159 static bool _artefact_name_lookup(string &result, const item_def &item,
1160                                   const string &lookup)
1161 {
1162     result = _artefact_name_lookup(item, lookup);
1163     return !result.empty();
1164 }
1165 
make_artefact_name(const item_def & item,bool appearance)1166 string make_artefact_name(const item_def &item, bool appearance)
1167 {
1168     ASSERT(is_artefact(item));
1169 
1170     ASSERT(item.base_type == OBJ_WEAPONS
1171            || item.base_type == OBJ_ARMOUR
1172            || item.base_type == OBJ_JEWELLERY
1173            || item.base_type == OBJ_BOOKS);
1174 
1175     if (is_unrandom_artefact(item))
1176     {
1177         const unrandart_entry *unrand = _seekunrandart(item);
1178         if (!appearance)
1179             return unrand->name;
1180         return unrand->unid_name;
1181     }
1182 
1183     string lookup;
1184     string result;
1185 
1186     // Use prefix of gifting god, if applicable.
1187     bool god_gift = false;
1188     int item_orig = 0;
1189     if (!appearance)
1190     {
1191         // Divine gifts don't look special, so this is only necessary
1192         // for actually naming an item.
1193         item_orig = item.orig_monnum;
1194         if (item_orig < 0)
1195             item_orig = -item_orig;
1196         else
1197             item_orig = 0;
1198 
1199         if (item_orig > GOD_NO_GOD && item_orig < NUM_GODS)
1200         {
1201             lookup += god_name(static_cast<god_type>(item_orig), false) + " ";
1202             god_gift = true;
1203         }
1204     }
1205 
1206     // get base type
1207     lookup += _get_artefact_type(item, appearance);
1208 
1209     if (appearance)
1210     {
1211         string appear = getRandNameString(lookup, " appearance");
1212         if (appear.empty())
1213         {
1214             appear = getRandNameString("general appearance");
1215             if (appear.empty()) // still nothing found?
1216                 appear = "non-descript";
1217         }
1218 
1219         result += appear;
1220         result += " ";
1221         result += item_base_name(item);
1222         return result;
1223     }
1224 
1225     if (_pick_db_name(item))
1226     {
1227         result += item_base_name(item) + " ";
1228 
1229         int tries = 100;
1230         string name;
1231         do
1232         {
1233             (_artefact_name_lookup(name, item, lookup)
1234 
1235              // If nothing found, try god name alone.
1236              || (god_gift
1237                  && _artefact_name_lookup(name, item,
1238                                           god_name(
1239                                              static_cast<god_type>(item_orig),
1240                                              false)))
1241 
1242              // If still nothing found, try base type alone.
1243              || _artefact_name_lookup(name, item, _get_artefact_type(item)));
1244         }
1245         while (--tries > 0 && strwidth(name) > 25);
1246 
1247         if (name.empty()) // still nothing found?
1248             result += "of Bugginess";
1249         else
1250             result += name;
1251     }
1252     else
1253     {
1254         // construct a unique name
1255         const string st_p = make_name();
1256         result += item_base_name(item);
1257 
1258         if (one_chance_in(3))
1259         {
1260             result += " of ";
1261             result += st_p;
1262         }
1263         else
1264         {
1265             result += " \"";
1266             result += st_p;
1267             result += "\"";
1268         }
1269     }
1270 
1271     return result;
1272 }
1273 
_seekunrandart(const item_def & item)1274 static const unrandart_entry *_seekunrandart(const item_def &item)
1275 {
1276     return get_unrand_entry(item.unrand_idx);
1277 }
1278 
get_artefact_base_name(const item_def & item,bool terse)1279 string get_artefact_base_name(const item_def &item, bool terse)
1280 {
1281     string base_name = item_base_name(item);
1282     const char* custom_type = _seekunrandart(item)->type_name;
1283     if (custom_type)
1284         base_name = custom_type;
1285     if (terse)
1286     {
1287         base_name = replace_all(base_name, "executioner's axe", "exec axe");
1288         base_name = replace_all(base_name, "giant spiked club", "g.spiked club");
1289         base_name = replace_all(base_name, "triple crossbow", "triple xbow");
1290     }
1291     return base_name;
1292 }
1293 
get_artefact_name(const item_def & item,bool force_known)1294 string get_artefact_name(const item_def &item, bool force_known)
1295 {
1296     ASSERT(is_artefact(item));
1297 
1298     if (item_type_known(item) || force_known)
1299     {
1300         // print artefact's real name, if that's set
1301         if (item.props.exists(ARTEFACT_NAME_KEY))
1302             return item.props[ARTEFACT_NAME_KEY].get_string();
1303         // other unrands don't use cached names
1304         if (is_unrandom_artefact(item))
1305             return _seekunrandart(item)->name;
1306         return make_artefact_name(item, false);
1307     }
1308     // print artefact appearance
1309     if (item.props.exists(ARTEFACT_APPEAR_KEY))
1310         return item.props[ARTEFACT_APPEAR_KEY].get_string();
1311     return make_artefact_name(item, true);
1312 }
1313 
set_artefact_name(item_def & item,const string & name)1314 void set_artefact_name(item_def &item, const string &name)
1315 {
1316     ASSERT(is_artefact(item));
1317     ASSERT(!name.empty());
1318     item.props[ARTEFACT_NAME_KEY].get_string() = name;
1319 }
1320 
find_unrandart_index(const item_def & artefact)1321 int find_unrandart_index(const item_def& artefact)
1322 {
1323     return artefact.unrand_idx;
1324 }
1325 
get_unrand_entry(int unrand_index)1326 const unrandart_entry* get_unrand_entry(int unrand_index)
1327 {
1328     unrand_index -= UNRAND_START;
1329 
1330     if (unrand_index <= -1 || unrand_index >= NUM_UNRANDARTS)
1331         return &unranddata[0];  // dummy unrandart
1332     else
1333         return &unranddata[unrand_index];
1334 }
1335 
find_okay_unrandart(uint8_t aclass,uint8_t atype,bool in_abyss)1336 int find_okay_unrandart(uint8_t aclass, uint8_t atype, bool in_abyss)
1337 {
1338     int ret = -1;
1339 
1340     // Pick randomly among unrandarts with the proper
1341     // base_type and sub_type. This will rule out unrands that have already
1342     // placed as part of levelgen, but may find unrands that have been acquired.
1343     // because of this, the caller needs to properly set up a fallback randart
1344     // in some cases: see makeitem.cc:_setup_fallback_randart.
1345     for (int i = 0, count = 0; i < NUM_UNRANDARTS; i++)
1346     {
1347         const int              index = i + UNRAND_START;
1348         const unrandart_entry* entry = &unranddata[i];
1349 
1350         // Skip dummy entries.
1351         if (entry->base_type == OBJ_UNASSIGNED)
1352             continue;
1353 
1354         const unique_item_status_type status =
1355             get_unique_item_status(index);
1356 
1357         if (in_abyss && status != UNIQ_LOST_IN_ABYSS
1358             || !in_abyss && status != UNIQ_NOT_EXISTS
1359                // for acquired items, ignore them in the random calculations
1360                // here and let fallback artefacts replace them.
1361                // TODO: abyss? double check trove
1362                && status != UNIQ_EXISTS_NONLEVELGEN)
1363         {
1364             continue;
1365         }
1366 
1367         // Never randomly generated until lost in the abyss.
1368         if ((!in_abyss || status != UNIQ_LOST_IN_ABYSS)
1369             && entry->flags & UNRAND_FLAG_NOGEN)
1370         {
1371             continue;
1372         }
1373 
1374         if (entry->base_type != aclass
1375             || atype != OBJ_RANDOM && entry->sub_type != atype
1376                // Acquirement.
1377                && (aclass != OBJ_WEAPONS
1378                    || item_attack_skill(entry->base_type, atype) !=
1379                       item_attack_skill(entry->base_type, entry->sub_type)
1380                    || hands_reqd(&you, entry->base_type,
1381                                  atype) !=
1382                       hands_reqd(&you, entry->base_type,
1383                                  entry->sub_type)))
1384         {
1385             continue;
1386         }
1387 
1388         count++;
1389 
1390         if (one_chance_in(count))
1391             ret = index;
1392     }
1393 
1394     return ret;
1395 }
1396 
get_unrandart_num(const char * name)1397 int get_unrandart_num(const char *name)
1398 {
1399     string uname = name;
1400     uname = replace_all(uname, " ", "_");
1401     uname = replace_all(uname, "'", "");
1402     lowercase(uname);
1403     string quoted = "\"";
1404     quoted += uname;
1405     quoted += "\"";
1406 
1407     for (unsigned int i = 0; i < ARRAYSZ(unranddata); ++i)
1408     {
1409         string art = unranddata[i].name;
1410         art = replace_all(art, " ", "_");
1411         art = replace_all(art, "'", "");
1412         lowercase(art);
1413         if (art == uname || art.find(quoted) != string::npos)
1414             return UNRAND_START + i;
1415     }
1416     return SPWPN_NORMAL;
1417 }
1418 
_randart_is_redundant(const item_def & item,artefact_properties_t & proprt)1419 static bool _randart_is_redundant(const item_def &item,
1420                                    artefact_properties_t &proprt)
1421 {
1422     if (item.base_type != OBJ_JEWELLERY)
1423         return false;
1424 
1425     artefact_prop_type provides  = ARTP_NUM_PROPERTIES;
1426 
1427     switch (item.sub_type)
1428     {
1429     case RING_PROTECTION:
1430         provides = ARTP_AC;
1431         break;
1432 
1433     case RING_FIRE:
1434     case RING_PROTECTION_FROM_FIRE:
1435         provides = ARTP_FIRE;
1436         break;
1437 
1438     case RING_POISON_RESISTANCE:
1439         provides = ARTP_POISON;
1440         break;
1441 
1442     case RING_ICE:
1443     case RING_PROTECTION_FROM_COLD:
1444         provides = ARTP_COLD;
1445         break;
1446 
1447     case RING_STRENGTH:
1448         provides = ARTP_STRENGTH;
1449         break;
1450 
1451     case RING_SLAYING:
1452         provides = ARTP_SLAYING;
1453         break;
1454 
1455     case RING_SEE_INVISIBLE:
1456         provides = ARTP_SEE_INVISIBLE;
1457         break;
1458 
1459     case RING_STEALTH:
1460         provides = ARTP_STEALTH;
1461         break;
1462 
1463     case RING_EVASION:
1464         provides = ARTP_EVASION;
1465         break;
1466 
1467     case RING_DEXTERITY:
1468         provides = ARTP_DEXTERITY;
1469         break;
1470 
1471     case RING_INTELLIGENCE:
1472         provides = ARTP_INTELLIGENCE;
1473         break;
1474 
1475     case RING_MAGICAL_POWER:
1476         provides = ARTP_MAGICAL_POWER;
1477         break;
1478 
1479     case RING_FLIGHT:
1480         provides = ARTP_FLY;
1481         break;
1482 
1483     case RING_LIFE_PROTECTION:
1484         provides = ARTP_NEGATIVE_ENERGY;
1485         break;
1486 
1487     case RING_WILLPOWER:
1488         provides = ARTP_WILLPOWER;
1489         break;
1490 
1491     case RING_RESIST_CORROSION:
1492         provides = ARTP_RCORR;
1493         break;
1494 
1495     case AMU_REGENERATION:
1496         provides = ARTP_REGENERATION;
1497         break;
1498 
1499     case AMU_REFLECTION:
1500         provides = ARTP_SHIELDING;
1501         break;
1502     }
1503 
1504     if (provides == ARTP_NUM_PROPERTIES)
1505         return false;
1506 
1507     if (proprt[provides] != 0)
1508         return true;
1509 
1510     return false;
1511 }
1512 
_randart_is_conflicting(const item_def & item,artefact_properties_t & proprt)1513 static bool _randart_is_conflicting(const item_def &item,
1514                                      artefact_properties_t &proprt)
1515 {
1516     // see also _artp_can_go_on_item
1517 
1518     if (proprt[ARTP_PREVENT_SPELLCASTING]
1519         && (proprt[ARTP_INTELLIGENCE] > 0
1520             || proprt[ARTP_MAGICAL_POWER] > 0
1521             || proprt[ARTP_ARCHMAGI]))
1522     {
1523         return true;
1524     }
1525 
1526     if (item.base_type == OBJ_WEAPONS
1527         && get_weapon_brand(item) == SPWPN_HOLY_WRATH
1528         && is_demonic(item))
1529     {
1530         return true;
1531     }
1532 
1533     if (item.is_type(OBJ_JEWELLERY, RING_WIZARDRY)
1534         && proprt[ARTP_INTELLIGENCE] < 0)
1535     {
1536         return true;
1537     }
1538 
1539     return false;
1540 }
1541 
randart_is_bad(const item_def & item,artefact_properties_t & proprt)1542 bool randart_is_bad(const item_def &item, artefact_properties_t &proprt)
1543 {
1544     if (item.base_type == OBJ_BOOKS)
1545         return false;
1546 
1547     if (_artefact_num_props(proprt) == 0)
1548         return true;
1549 
1550     // Weapons must have a brand and at least one other property.
1551     if (item.base_type == OBJ_WEAPONS
1552         && (proprt[ARTP_BRAND] == SPWPN_NORMAL
1553             || _artefact_num_props(proprt) < 2))
1554     {
1555         return true;
1556     }
1557 
1558     return _randart_is_redundant(item, proprt)
1559            || _randart_is_conflicting(item, proprt);
1560 }
1561 
randart_is_bad(const item_def & item)1562 bool randart_is_bad(const item_def &item)
1563 {
1564     artefact_properties_t proprt;
1565     proprt.init(0);
1566     artefact_properties(item, proprt);
1567 
1568     return randart_is_bad(item, proprt);
1569 }
1570 
_artefact_setup_prop_vectors(item_def & item)1571 static void _artefact_setup_prop_vectors(item_def &item)
1572 {
1573     CrawlHashTable &props = item.props;
1574     if (!props.exists(ARTEFACT_PROPS_KEY))
1575         props[ARTEFACT_PROPS_KEY].new_vector(SV_SHORT).resize(ART_PROPERTIES);
1576 
1577     CrawlVector &rap = props[ARTEFACT_PROPS_KEY].get_vector();
1578     rap.set_max_size(ART_PROPERTIES);
1579 
1580     for (vec_size i = 0; i < ART_PROPERTIES; i++)
1581         rap[i].get_short() = 0;
1582 
1583     if (!item.props.exists(KNOWN_PROPS_KEY))
1584     {
1585         props[KNOWN_PROPS_KEY].new_vector(SV_BOOL).resize(ART_PROPERTIES);
1586         CrawlVector &known = item.props[KNOWN_PROPS_KEY].get_vector();
1587         known.set_max_size(ART_PROPERTIES);
1588         for (vec_size i = 0; i < ART_PROPERTIES; i++)
1589             known[i] = static_cast<bool>(false);
1590     }
1591 }
1592 
1593 // If force_mundane is true, normally mundane items are forced to
1594 // nevertheless become artefacts.
make_item_randart(item_def & item,bool force_mundane)1595 bool make_item_randart(item_def &item, bool force_mundane)
1596 {
1597     if (item.base_type != OBJ_WEAPONS
1598         && item.base_type != OBJ_ARMOUR
1599         && item.base_type != OBJ_JEWELLERY)
1600     {
1601         return false;
1602     }
1603 
1604     // This item already is a randart.
1605     if (item.flags & ISFLAG_RANDART)
1606         return true;
1607 
1608     // Not a truly random artefact.
1609     if (item.flags & ISFLAG_UNRANDART)
1610         return false;
1611 
1612     // Mundane items are much less likely to be artefacts.
1613     if (!force_mundane && item.is_mundane() && !one_chance_in(5))
1614         return false;
1615 
1616     _artefact_setup_prop_vectors(item);
1617     item.flags |= ISFLAG_RANDART;
1618 
1619     const god_type god_gift = origin_as_god_gift(item);
1620 
1621     int randart_tries = 500;
1622     do
1623     {
1624         // Now that we found something, initialise the props array.
1625         if (--randart_tries <= 0 || !_init_artefact_properties(item))
1626         {
1627             // Something went wrong that no amount of rerolling will fix.
1628             item.unrand_idx = 0;
1629             item.props.erase(ARTEFACT_PROPS_KEY);
1630             item.props.erase(KNOWN_PROPS_KEY);
1631             item.flags &= ~ISFLAG_RANDART;
1632             return false;
1633         }
1634     }
1635     while (randart_is_bad(item)
1636            || god_gift != GOD_NO_GOD && !_god_fits_artefact(god_gift, item));
1637 
1638     // get true artefact name
1639     if (item.props.exists(ARTEFACT_NAME_KEY))
1640         ASSERT(item.props[ARTEFACT_NAME_KEY].get_type() == SV_STR);
1641     else
1642         set_artefact_name(item, make_artefact_name(item, false));
1643 
1644     // get artefact appearance
1645     if (item.props.exists(ARTEFACT_APPEAR_KEY))
1646         ASSERT(item.props[ARTEFACT_APPEAR_KEY].get_type() == SV_STR);
1647     else
1648         item.props[ARTEFACT_APPEAR_KEY].get_string() =
1649             make_artefact_name(item, true);
1650 
1651     return true;
1652 }
1653 
_ashenzari_artefact_name(item_def & item)1654 static string _ashenzari_artefact_name(item_def &item)
1655 {
1656     const int old_orig = item.orig_monnum;
1657 
1658     item.orig_monnum = -GOD_ASHENZARI;
1659 
1660     int tries = 100;
1661     string name;
1662     do
1663     {
1664         name = _artefact_name_lookup(item, "Ashenzari");
1665     }
1666     while (--tries > 0 && strwidth(name) > 25);
1667 
1668     item.orig_monnum = old_orig;
1669 
1670     return item_base_name(item) + " " + (name.empty() ? "of Ashenzari" : name);
1671 }
1672 
make_ashenzari_randart(item_def & item)1673 void make_ashenzari_randart(item_def &item)
1674 {
1675     if (item.base_type != OBJ_WEAPONS
1676         && item.base_type != OBJ_ARMOUR
1677         && item.base_type != OBJ_JEWELLERY)
1678     {
1679         return;
1680     }
1681 
1682     // This item already is a randart, just rename
1683     if (item.flags & ISFLAG_RANDART)
1684     {
1685         set_artefact_name(item, _ashenzari_artefact_name(item));
1686         return;
1687     }
1688 
1689     // Too special, will just be cursed
1690     if (item.flags & ISFLAG_UNRANDART)
1691         return;
1692 
1693     const int brand = item.brand;
1694 
1695     // Ash randarts get no props
1696     _artefact_setup_prop_vectors(item);
1697     item.flags |= ISFLAG_RANDART;
1698     item.flags |= ISFLAG_KNOW_PROPERTIES;
1699 
1700     if (item.brand != SPWPN_NORMAL)
1701         set_artefact_brand(item, brand);
1702 
1703     set_artefact_name(item, _ashenzari_artefact_name(item));
1704     item.props[ARTEFACT_APPEAR_KEY].get_string() =
1705         make_artefact_name(item, true);
1706 
1707 }
1708 
_make_faerie_armour(item_def & item)1709 static void _make_faerie_armour(item_def &item)
1710 {
1711     item_def doodad;
1712     for (int i=0; i<100; i++)
1713     {
1714         doodad.clear();
1715         doodad.base_type = item.base_type;
1716         doodad.sub_type = item.sub_type;
1717         if (!make_item_randart(doodad))
1718         {
1719             i--; // Forbidden props are not absolute, artefactness is.
1720             continue;
1721         }
1722 
1723         // -Cast makes no sense on someone called "the Enchantress".
1724         if (artefact_property(doodad, ARTP_PREVENT_SPELLCASTING))
1725             continue;
1726 
1727         if (one_chance_in(20))
1728             artefact_set_property(doodad, ARTP_CLARITY, 1);
1729         if (one_chance_in(20))
1730             artefact_set_property(doodad, ARTP_MAGICAL_POWER, 1 + random2(10));
1731         if (one_chance_in(20))
1732             artefact_set_property(doodad, ARTP_HP, random2(16) - 5);
1733 
1734         break;
1735     }
1736     ASSERT(is_artefact(doodad));
1737     ASSERT(doodad.sub_type == item.sub_type);
1738 
1739     doodad.props[ARTEFACT_APPEAR_KEY].get_string()
1740         = item.props[ARTEFACT_APPEAR_KEY].get_string();
1741     doodad.props.erase(ARTEFACT_NAME_KEY);
1742     item.props = doodad.props;
1743 
1744     // On body armour, an enchantment of less than 0 is never viable.
1745     int high_plus = random2(6) - 2;
1746     high_plus += random2(6);
1747     item.plus = max(high_plus, random2(2));
1748 }
1749 
1750 static jewellery_type octoring_types[8] =
1751 {
1752     RING_SEE_INVISIBLE, RING_PROTECTION_FROM_FIRE, RING_PROTECTION_FROM_COLD,
1753     RING_RESIST_CORROSION, RING_FLIGHT, RING_WIZARDRY, RING_MAGICAL_POWER,
1754     RING_LIFE_PROTECTION
1755 };
1756 
_make_octoring(item_def & item)1757 static void _make_octoring(item_def &item)
1758 {
1759     if (you.octopus_king_rings == 0xff)
1760     {
1761         ASSERT(you.wizard || you.suppress_wizard || crawl_state.test);
1762         item.sub_type = octoring_types[random2(8)];
1763         return;
1764     }
1765 
1766     int which = 0;
1767     do which = random2(8); while (you.octopus_king_rings & (1 << which));
1768 
1769     item.sub_type = octoring_types[which];
1770 
1771     // Save that we've found that particular type
1772     you.octopus_king_rings |= 1 << which;
1773 
1774     // If there are any types left, unset the 'already found' flag
1775     if (you.octopus_king_rings != 0xff)
1776         _set_unique_item_status(UNRAND_OCTOPUS_KING_RING, false);
1777 }
1778 
make_item_unrandart(item_def & item,int unrand_index)1779 bool make_item_unrandart(item_def &item, int unrand_index)
1780 {
1781     ASSERT_RANGE(unrand_index, UNRAND_START + 1, (UNRAND_START + NUM_UNRANDARTS));
1782     rng::subgenerator item_rng; // for safety's sake, this is sometimes called
1783                                 // directly
1784 
1785     item.unrand_idx = unrand_index;
1786 
1787     const unrandart_entry *unrand = &unranddata[unrand_index - UNRAND_START];
1788 
1789     item.flags |= ISFLAG_UNRANDART;
1790     _artefact_setup_prop_vectors(item);
1791     _init_artefact_properties(item);
1792 
1793     // get artefact appearance
1794     ASSERT(!item.props.exists(ARTEFACT_APPEAR_KEY));
1795     item.props[ARTEFACT_APPEAR_KEY].get_string() = unrand->unid_name;
1796 
1797     _set_unique_item_status(unrand_index, true);
1798 
1799     if (unrand_index == UNRAND_FAERIE)
1800         _make_faerie_armour(item);
1801     else if (unrand_index == UNRAND_OCTOPUS_KING_RING)
1802         _make_octoring(item);
1803     else if (unrand_index == UNRAND_WOE && !you.has_mutation(MUT_NO_GRASPING)
1804              && !you.could_wield(item, true, true))
1805     {
1806         // always wieldable, always 2-handed
1807         item.sub_type = WPN_BROAD_AXE;
1808     }
1809 
1810     if (!(unrand->flags & UNRAND_FLAG_UNIDED)
1811         && !strcmp(unrand->name, unrand->unid_name))
1812     {
1813         set_ident_flags(item, ISFLAG_IDENT_MASK | ISFLAG_NOTED_ID);
1814     }
1815 
1816     return true;
1817 }
1818 
unrand_reacts()1819 void unrand_reacts()
1820 {
1821     item_def*  weapon     = you.weapon();
1822     const int  old_plus   = weapon ? weapon->plus : 0;
1823 
1824     for (int i = 0; i < NUM_EQUIP; i++)
1825     {
1826         if (you.unrand_reacts[i])
1827         {
1828             item_def&        item  = you.inv[you.equip[i]];
1829             const unrandart_entry* entry = get_unrand_entry(item.unrand_idx);
1830 
1831             entry->world_reacts_func(&item);
1832         }
1833     }
1834 
1835     if (weapon && (old_plus != weapon->plus))
1836         you.wield_change = true;
1837 }
1838 
artefact_set_property(item_def & item,artefact_prop_type prop,int val)1839 void artefact_set_property(item_def          &item,
1840                             artefact_prop_type prop,
1841                             int                val)
1842 {
1843     ASSERT(is_artefact(item));
1844     ASSERT(item.props.exists(ARTEFACT_PROPS_KEY));
1845 
1846     CrawlVector &rap_vec = item.props[ARTEFACT_PROPS_KEY].get_vector();
1847     ASSERT(rap_vec.get_type()     == SV_SHORT);
1848     ASSERT(rap_vec.size()         == ART_PROPERTIES);
1849     ASSERT(rap_vec.get_max_size() == ART_PROPERTIES);
1850 
1851     rap_vec[prop].get_short() = val;
1852 }
1853 
1854 template<typename Z>
artefact_pad_store_vector(CrawlVector & vec,Z value)1855 static inline void artefact_pad_store_vector(CrawlVector &vec, Z value)
1856 {
1857     if (vec.get_max_size() < ART_PROPERTIES)
1858     {
1859         // Authentic tribal dance to propitiate the asserts in store.cc:
1860         const int old_size = vec.get_max_size();
1861         vec.set_max_size(VEC_MAX_SIZE);
1862         vec.resize(ART_PROPERTIES);
1863         vec.set_max_size(ART_PROPERTIES);
1864         for (int i = old_size; i < ART_PROPERTIES; ++i)
1865             vec[i] = value;
1866     }
1867 }
1868 
artefact_fixup_props(item_def & item)1869 void artefact_fixup_props(item_def &item)
1870 {
1871     CrawlHashTable &props = item.props;
1872     if (props.exists(ARTEFACT_PROPS_KEY))
1873         artefact_pad_store_vector(props[ARTEFACT_PROPS_KEY], short(0));
1874 
1875     if (props.exists(KNOWN_PROPS_KEY))
1876         artefact_pad_store_vector(props[KNOWN_PROPS_KEY], false);
1877 }
1878