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