00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00022
00023 #include "crogue.h"
00024 #include "export.h"
00025
00026
00027 int filter_none (const item *i);
00028 int filter_cursed (const item *i);
00029 int filter_worn (const item *i);
00030 int filter_wearable (const item *i);
00031 int filter_unidentified(const item *i);
00032 int filter_usable (const item *i);
00033 int filter_enchantable (const item *i);
00034 int filter_plural (const item *i);
00035 int filter_rusty (const item *i);
00036
00037
00038 int pick_item(inventoryfilter f);
00039 void pick_multiple(inventoryfilter f, void (*function)(int which));
00040 static int items_menu(inventoryfilter f);
00041 static void itemstring(int n, char *buf);
00042 static int item_by_sequence(int n);
00043 static void sort_inventory(void);
00044 int filter_matches(inventoryfilter f);
00045
00046
00047 void init_inventory(void);
00048 item split_item(int slot);
00049 void merge_item(int slot, item it);
00050 int give_item(const item* itm, sint silent);
00051
00052
00053 item *player_equipment(int slot);
00054 static int num_player_equipment(int slot);
00055
00056
00057 const item_property_desc *get_item_property(const item *itm, uint type);
00058 void apply_item_property(const item *itm, uint stat, sint *n);
00059 void apply_all_item_properties(const item *itm, int trigger);
00060 static void append_armor_description(const item *itm, char *output);
00061 static const item_property *extract_item_property(const item_properties *pr, uint type);
00062 void award_item_points(void);
00063 void rust_inventory(uint which);
00064
00065
00066 void give_gold(ulong amt);
00067 ulong player_gold(void);
00068 void debit_gold(ulong amt);
00069
00070 static ulong item_value(const item *i);
00071 ulong item_cost(const item *i);
00072 ulong sale_price(const item *i);
00073
00074
00075 void list_discoveries(void);
00076 void identify_type(ushort t);
00077 ulong type_is_identified(ulong t);
00078
00079
00080 void compress_items(void);
00081 void place_item(const item *i, ushort x, ushort y);
00082 uchar draw_item(uchar x, uchar y);
00083 void init_items();
00084 void cleanup_items(void);
00085 static uint add_item(item itm);
00086 static void delete_item(uint which);
00087
00088 item randitem(filelink type);
00089 static schar random_plus(void);
00090
00091 static uint can_stack(const item *one, const item *two);
00092
00093
00094 void drop_item(void);
00095 void drop_multiple(void);
00096 static void do_drop_item(int which);
00097 void wear_item(void);
00098 static void do_wear_item(int which);
00099 void takeoff_item(void);
00100 void takeoff_multiple(void);
00101 static void do_takeoff_item(int which);
00102 void pickup(uint pickup_prompt);
00103
00104
00105
00106
00107
00108 #define NUM_ITEM_CATEGORIES 13
00109 const char *categoryNames[NUM_ITEM_CATEGORIES] =
00110 {
00111 gettext("Weapons"),
00112 gettext("Ammunition"),
00113 gettext("Armor"),
00114 gettext("Amulets"),
00115 gettext("Rings"),
00116 gettext("Wands"),
00117 gettext("Potions"),
00118 gettext("Scrolls"),
00119 gettext("Spellbooks"),
00120 gettext("Food"),
00121 gettext("Gems"),
00122 gettext("Money"),
00123 gettext("Tools")
00124 };
00125
00126
00127
00128
00129
00130
00131 int filter_none(const item *i)
00132 {
00133 return 1;
00134 }
00135
00136
00137 int filter_cursed(const item *i)
00138 {
00139 return (i->flags & ITEMFLAG_CURSED) && (i->flags & ITEMFLAG_IDENTIFIED);
00140 }
00141
00142
00143 int filter_worn(const item *i)
00144 {
00145 return i->flags & ITEMFLAG_EQUIPPED;
00146 }
00147
00148
00149 int filter_wearable(const item *i)
00150 {
00151 if(i->flags & ITEMFLAG_EQUIPPED)
00152 return 0;
00153 return ITEMDESC(*i).spot >= 0;
00154 }
00155
00156
00157 int filter_unidentified(const item *i)
00158 {
00159 itemdesc *desc = &ITEMDESC(*i);
00160
00161 if(!type_is_identified(i->type))
00162 return 1;
00163 else if(!(i->flags & ITEMFLAG_IDENTIFIED))
00164 {
00165
00166
00167 if(desc->flags & ITEMFLAG_PLUS)
00168 return 1;
00169 if((desc->flags & ITEMFLAG_CHARGED) && !(desc->flags & ITEMFLAG_HIDECHARGE))
00170 return 1;
00171 }
00172 return 0;
00173 }
00174
00175
00176 int filter_usable(const item *i)
00177 {
00178 return (ITEMDESC(*i).flags & ITEMFLAG_USABLE);
00179 }
00180
00181
00182 int filter_enchantable(const item *i)
00183 {
00184 return ITEMDESC(*i).flags & (ITEMFLAG_PLUS | ITEMFLAG_CHARGED);
00185 }
00186
00187
00188 int filter_plural(const item *i)
00189 {
00190 return (i->stacksize != 1 && ITEMDESC(*i).flags & ITEMFLAG_STACK
00191 && !(ITEMDESC(*i).flags & ITEMFLAG_HIDECHARGE));
00192 }
00193
00194
00195 int filter_rusty(const item *i)
00196 {
00197 return (i->rustiness > 0);
00198 }
00199
00200
00201
00202
00203
00204 static inventoryfilter invfilter;
00205
00206
00207 int pick_item(inventoryfilter f)
00208 {
00209 int ret;
00210 ret = items_menu(f);
00211 full_redraw();
00212 return ret;
00213 }
00214
00215
00216 void pick_multiple(inventoryfilter f, void (*function)(int which))
00217 {
00218 int which;
00219
00220 UI_Menu_Set_Persist(1);
00221
00222 do
00223 {
00224 which = items_menu(f);
00225 function(which);
00226 }
00227 while(which >= 0);
00228
00229 UI_Menu_Set_Persist(0);
00230 full_redraw();
00231 }
00232
00233
00234 static int items_menu(inventoryfilter f)
00235 {
00236 int ret;
00237 int num_choices;
00238
00239 if(count_inventory()==0)
00240 return -2;
00241
00242 sort_inventory();
00243
00244 invfilter = f;
00245 num_choices = filter_matches(f);
00246
00247 if(!num_choices)
00248 return -2;
00249
00250 setTabStops(item_tab_stops);
00251 ret = UI_Menu_Pick( inventory_rect, num_choices, &itemstring, 0 );
00252
00253 if(ret<0)
00254 return -1;
00255
00256 ret = item_by_sequence(ret);
00257
00258 if( ret<0 )
00259 return -1;
00260 else
00261 return ret;
00262 }
00263
00264
00265 static void itemstring(int n, char *buf)
00266 {
00267 static int prev_n = -2;
00268 static int i = 0;
00269 static int prev_category;
00270 char hotkey_display;
00271
00272 if(n == prev_n+1)
00273 {
00274 prev_n = n;
00275 n=1;
00276 } else {
00277 prev_n = n;
00278 prev_category = -1;
00279 i=0;
00280 }
00281
00282 for(; i<PLRINVSIZE; i++)
00283 {
00284 if( !invfilter(&INVENTORY(i)) )
00285 continue;
00286
00287 if(ITEMDESC(INVENTORY(i)).category != prev_category)
00288 {
00289 if(!n)
00290 {
00291 sprintf(buf, "\n%s",
00292 categoryNames[ITEMDESC(INVENTORY(i)).category]);
00293 break;
00294 }
00295 n--;
00296
00297 prev_category = ITEMDESC(INVENTORY(i)).category;
00298 }
00299
00300 if(!n)
00301 {
00302 hotkey_display = INVENTORY(i).hotkey;
00303 #ifdef REALCOMPUTER
00304 if(hotkey_display)
00305 sprintf(buf,"%c - %s",hotkey_display,itemname(&INVENTORY(i)));
00306 else
00307 strcpy(buf, itemname(&INVENTORY(i)));
00308 #else
00309 if(!hotkey_display) hotkey_display = ' ';
00310 sprintf(buf, "%c\t%s", hotkey_display, itemname(&INVENTORY(i)));
00311 #endif
00312 break;
00313 }
00314 n--;
00315 }
00316 }
00317
00318
00319
00320 static int item_by_sequence(int n)
00321 {
00322 int i;
00323 int prev_category = -1;
00324
00325 for(i=0; i<PLRINVSIZE; i++)
00326 {
00327 if( !invfilter(&INVENTORY(i)) )
00328 continue;
00329
00330 if(ITEMDESC(INVENTORY(i)).category != prev_category)
00331 {
00332 if(!n)
00333 return -ITEMDESC(INVENTORY(i)).category - 1;
00334 n--;
00335
00336 prev_category = ITEMDESC(INVENTORY(i)).category;
00337 }
00338
00339 if(!n)
00340 return i;
00341 n--;
00342 }
00343
00344 return 0;
00345 }
00346
00347
00348 static void sort_inventory(void)
00349 {
00350 int i, j;
00351 int num=0;
00352 int cont;
00353 item tmp;
00354
00355 for(i=0; i<NUM_ITEM_CATEGORIES; i++)
00356 for(j=0; j<PLRINVSIZE; j++)
00357 if(INVENTORY(j).type && ITEMDESC(INVENTORY(j)).category == i)
00358 INVENTORY(j).next = num++;
00359
00360 do
00361 {
00362 cont = 0;
00363
00364 for(i=0; i<PLRINVSIZE; i++)
00365 if(INVENTORY(i).type && INVENTORY(i).next != i)
00366 {
00367 cont = 1;
00368
00369 tmp = INVENTORY(INVENTORY(i).next);
00370 INVENTORY(INVENTORY(i).next) = INVENTORY(i);
00371 INVENTORY(i) = tmp;
00372 }
00373 }
00374 while(cont);
00375 }
00376
00377
00378
00379 int filter_matches(inventoryfilter f)
00380 {
00381 int i, j;
00382 int n=0;
00383
00384 for(i=0; i<PLRINVSIZE; i++)
00385 if(INVENTORY(i).type && f(&INVENTORY(i)))
00386 n++;
00387
00388 for(i=0; i<NUM_ITEM_CATEGORIES; i++)
00389 for(j=0; j<PLRINVSIZE; j++)
00390 if(INVENTORY(j).type && ITEMDESC(INVENTORY(j)).category == i && f(&INVENTORY(j)))
00391 {
00392 n++;
00393 break;
00394 }
00395
00396 return n;
00397 }
00398
00399
00400
00401
00402
00403
00404 void init_inventory(void)
00405 {
00406 const uint *ptr;
00407 const item *contents;
00408 uint i;
00409 uint num_items;
00410
00411
00412 memset(&w->plr.inventory, 0, sizeof(item) * PLRINVSIZE);
00413
00414 ptr = (const uint*)deref_file_ptr(w->desc.startinginventory);
00415 num_items = *ptr;
00416 contents = (item*)(ptr+1);
00417
00418 for(i=0; i<num_items; i++)
00419 {
00420 INVENTORY(i) = contents[i];
00421
00422 identify_type(INVENTORY(i).type);
00423 }
00424 }
00425
00426
00427 item split_item(int slot)
00428 {
00429 item ret = INVENTORY(slot);
00430
00431 if(ITEMDESC(INVENTORY(slot)).flags & ITEMFLAG_CHARGED)
00432 {
00433 INVENTORY(slot).type = 0;
00434 return ret;
00435 }
00436
00437 ret.stacksize = 1;
00438
00439 INVENTORY(slot).stacksize--;
00440 if(INVENTORY(slot).stacksize<=0)
00441 {
00442 INVENTORY(slot).type = 0;
00443 }
00444
00445 return ret;
00446 }
00447
00448
00449 void merge_item(int slot, item it)
00450 {
00451 if(INVENTORY(slot).type == 0) {
00452 INVENTORY(slot) = it;
00453 } else {
00454 INVENTORY(slot).stacksize += it.stacksize;
00455 }
00456 }
00457
00458
00459 int give_item(const item *itm, sint silent)
00460 {
00461 int i;
00462
00463
00464 if(ITEMDESC(*itm).flags & ITEMFLAG_STACK) {
00465 for(i=0; i<PLRINVSIZE; i++) {
00466 if( can_stack(&INVENTORY(i), itm) )
00467 {
00468 INVENTORY(i).stacksize += itm->stacksize;
00469 if( !silent ) {
00470 message(gettext("You have %s."), itemname(&INVENTORY(i)));
00471 }
00472 return 1;
00473 }
00474 }
00475 }
00476
00477 for(i=0; i<PLRINVSIZE; i++) {
00478 if(!INVENTORY(i).type) {
00479 if( !silent ) {
00480 message(gettext("You got the %s."), itemname(itm) );
00481 }
00482 INVENTORY(i) = *itm;
00483 return 1;
00484 }
00485 }
00486
00487 message(gettext("You don't have room in your pack for the %s."),
00488 itemname(itm));
00489 return 0;
00490 }
00491
00492
00493 void give_item_slip(const item *itm)
00494 {
00495 int i;
00496
00497
00498 if(ITEMDESC(*itm).flags & ITEMFLAG_STACK) {
00499 for(i=0; i<PLRINVSIZE; i++) {
00500 if( can_stack(&INVENTORY(i), itm) )
00501 {
00502 INVENTORY(i).stacksize += itm->stacksize;
00503 message(gettext("You have %s."), itemname(&INVENTORY(i)));
00504 return;
00505 }
00506 }
00507 }
00508
00509 for(i=0; i<PLRINVSIZE; i++) {
00510 if(!INVENTORY(i).type) {
00511 message(gettext("You got the %s."), itemname(itm) );
00512 INVENTORY(i) = *itm;
00513 return;
00514 }
00515 }
00516
00517 place_item(itm, w->plr.x, w->plr.y);
00518 if(filter_plural(itm))
00519 message(gettext("The %s slips from your grasp."), itemname(itm));
00520 else
00521 message(gettext("The %s slip from your grasp."), itemname(itm));
00522 }
00523
00524
00525
00526
00527
00528
00529 item *player_equipment(int slot)
00530 {
00531 int i;
00532
00533 for(i=0; i<PLRINVSIZE; i++)
00534 {
00535 if(INVENTORY(i).type && ITEMDESC(INVENTORY(i)).spot == slot && INVENTORY(i).flags & ITEMFLAG_EQUIPPED)
00536 return &INVENTORY(i);
00537 }
00538
00539 return NULL;
00540 }
00541
00542
00543 static int num_player_equipment(int slot)
00544 {
00545 int i;
00546 int n=0;
00547
00548 for(i=0; i<PLRINVSIZE; i++)
00549 {
00550 if(INVENTORY(i).type && ITEMDESC(INVENTORY(i)).spot == slot && INVENTORY(i).flags & ITEMFLAG_EQUIPPED)
00551 n++;
00552 }
00553 return n;
00554 }
00555
00556
00557
00558
00559
00560
00561
00562 uint count_inventory(void)
00563 {
00564 int i, num=0;
00565 for(i=0; i<PLRINVSIZE; i++)
00566 if(INVENTORY(i).type)
00567 num++;
00568 return num;
00569 }
00570
00571
00572 int iterate_item_property(const item_properties *pr, uint type, short (*func)(const item_property *pr))
00573 {
00574 int i;
00575 const item_property *pos = &pr->properties[0];
00576
00577 for(i=0; i<pr->num_properties; i++)
00578 {
00579 if(pos->type == PROPERTY_INHERIT)
00580 {
00581 if( iterate_item_property( (const item_properties*)deref_file_ptr
00582 (pos->contents.parent), type, func ) )
00583 return 1;
00584 }
00585 else if(type == pos->type)
00586 {
00587 if( func(pos) )
00588 return 1;
00589 }
00590 pos++;
00591 }
00592 return 0;
00593 }
00594
00595
00596 const item_property_desc *get_item_property(const item *itm, uint type)
00597 {
00598 const item_property *ret = extract_item_property( &ITEMDESC(*itm).properties, type );
00599 if(!ret)
00600 return NULL;
00601 return &ret->contents;
00602 }
00603
00604
00605 static const item_property *extract_item_property(const item_properties *pr, uint type)
00606 {
00607 int i;
00608 const item_property *pos = &pr->properties[0], *ret;
00609
00610 for(i=0; i<pr->num_properties; i++)
00611 {
00612 if(pos->type == PROPERTY_INHERIT)
00613 {
00614 if( (ret = extract_item_property( (const item_properties*)
00615 deref_file_ptr(pos->contents.parent), type )) )
00616 return ret;
00617 }
00618 else if(type == pos->type)
00619 return pos;
00620 pos++;
00621 }
00622 return NULL;
00623 }
00624
00625 static const item *current_item;
00626 static sint *current_target;
00627 static uint current_stat;
00628
00629
00630 static short cb_apply_item_stat(const item_property *pr)
00631 {
00632 const stat_mod_desc *s = &pr->contents.worn_stat_effect;
00633 if(s->stat != current_stat)
00634 return 0;
00635
00636 apply_stat_mod_desc(current_target, s, current_item->plus);
00637 return 0;
00638 }
00639
00640
00641 static short cb_apply_item_stat_all(const item_property *pr)
00642 {
00643 const stat_mod_desc *s = &pr->contents.worn_stat_effect;
00644
00645 apply_stat_mod_desc(NULL, s, current_item->plus);
00646 return 0;
00647 }
00648
00649
00650 void apply_item_property(const item *itm, uint stat, sint *n)
00651 {
00652 current_item = itm;
00653 current_target = n;
00654 current_stat = stat;
00655 iterate_item_property(&ITEMDESC(*itm).properties, PROPERTY_WORN_STAT_EFFECT, &cb_apply_item_stat);
00656 }
00657
00658
00659 void apply_all_item_properties(const item *itm, int trigger)
00660 {
00661 current_item = itm;
00662 iterate_item_property(&ITEMDESC(*itm).properties, trigger, &cb_apply_item_stat_all);
00663 }
00664
00665
00666
00667 const char *shortitemname(const item *i)
00668 {
00669 itemdesc *desc;
00670 desc = &ITEMDESC(*i);
00671 if( type_is_identified(i->type) || isNull(desc->unidname)
00672 || i->flags & ITEMFLAG_UNPAID )
00673 return (uchar*)deref_file_ptr(desc->name);
00674 else
00675 return ((const itemextension*)deref_file_ptr(desc->unidname))->str;
00676 }
00677
00678
00679
00680 const char *rusty_descriptions[] =
00681 {
00682 gettext("pristine "),
00683 gettext("slightly rusty "),
00684 gettext("rusty "),
00685 gettext("very rusty ")
00686 };
00687
00688
00689 uchar *n_itemname(ulong which)
00690 {
00691 return itemname(&INVENTORY(which));
00692 }
00693
00694
00695 uchar *itemname(const item *i)
00696 {
00697 const char *n;
00698 long count;
00699 static char name[128];
00700 uint identified;
00701 itemdesc *desc;
00702
00703 identified = i->flags & ITEMFLAG_IDENTIFIED;
00704 desc = &ITEMDESC(*i);
00705
00706 n = shortitemname(i);
00707
00708 strcpy(name, "");
00709
00710 if(filter_plural(i))
00711 catprintf(name, "%li ", i->stacksize);
00712 if(i->flags & ITEMFLAG_CURSED && identified)
00713 catprintf(name, gettext("cursed "));
00714
00715 if(i->rustiness > 0)
00716 catprintf(name, rusty_descriptions[i->rustiness]);
00717
00718 if(!(ITEMDESC(*i).flags & ITEMFLAG_STACK))
00719 count=1;
00720 else if(ITEMDESC(*i).flags & ITEMFLAG_HIDECHARGE)
00721 count=1;
00722 else
00723 count = i->stacksize;
00724
00725 catprintf(name, pluralize(count, n));
00726
00727 if(desc->flags & ITEMFLAG_CHARGED && !(desc->flags & ITEMFLAG_HIDECHARGE) && identified)
00728 catprintf(name, gettext(" (%li charges)"), i->stacksize);
00729
00730 if(identified)
00731 {
00732 append_armor_description(i, name);
00733 }
00734 if(i->flags & ITEMFLAG_EQUIPPED)
00735 {
00736 if(desc->spot == 0)
00737 catprintf(name, gettext(" (wielded)"));
00738 else if(desc->spot == 1)
00739 catprintf(name, gettext(" (readied)"));
00740 else catprintf(name, gettext(" (worn)"));
00741 }
00742
00743 if(i->flags & ITEMFLAG_UNPAID)
00744 catprintf(name, gettext(" (%li gold)"), item_cost(i));
00745
00746 return name;
00747 }
00748
00749
00750
00751 typedef struct statdisplay
00752 {
00753 uint stat;
00754 const char *str;
00755 } statdisplay;
00756
00757 #define NUM_DISPLAYED_STATS 7
00758 const statdisplay displayed_stats[NUM_DISPLAYED_STATS] = {
00759 {STAT_STRENGTH, gettext("St")},
00760 {STAT_DEXTERITY, gettext("Dx")},
00761 {STAT_TOUGHNESS, gettext("To")},
00762 {STAT_MANA, gettext("Ma")},
00763 {STAT_WILLPOWER, gettext("Wi")},
00764 {STAT_LIGHTRADIUS, gettext("Light")},
00765 {STAT_SPEED, gettext("Spd")}
00766 };
00767
00768 static sint tmp_stats[NUM_STATS];
00769
00770 static short cb_apply_item_stat_to_temp(const item_property *pr)
00771 {
00772 apply_stat_mod_desc(&tmp_stats[pr->contents.worn_stat_effect.stat], &pr->contents.worn_stat_effect, current_item->plus);
00773 return 0;
00774 }
00775
00776
00777 static void append_armor_description(const item *itm, char *output)
00778 {
00779 int i;
00780 current_item = itm;
00781
00782 for(i=0; i<NUM_STATS; i++)
00783 tmp_stats[i] = 0;
00784
00785 iterate_item_property(&ITEMDESC(*itm).properties, PROPERTY_WORN_STAT_EFFECT, &cb_apply_item_stat_to_temp);
00786 iterate_item_property(&ITEMDESC(*itm).properties, PROPERTY_THROWN_EFFECT, &cb_apply_item_stat_to_temp);
00787 if(tmp_stats[STAT_MISSILEDAM_MAX] > tmp_stats[STAT_DAMAGE_MAX])
00788 tmp_stats[STAT_DAMAGE_MAX] = tmp_stats[STAT_MISSILEDAM_MAX];
00789 if(tmp_stats[STAT_MISSILEDAM_MIN] > tmp_stats[STAT_DAMAGE_MIN])
00790 tmp_stats[STAT_DAMAGE_MIN] = tmp_stats[STAT_MISSILEDAM_MIN];
00791 if(tmp_stats[STAT_MISSILE_TOHIT] > tmp_stats[STAT_TOHIT])
00792 tmp_stats[STAT_TOHIT] += tmp_stats[STAT_MISSILE_TOHIT];
00793
00794 if(tmp_stats[STAT_DV] || tmp_stats[STAT_PV])
00795 catprintf(output, " [%i, %i]", tmp_stats[STAT_DV], tmp_stats[STAT_PV]);
00796 if((tmp_stats[STAT_DAMAGE_MIN] || tmp_stats[STAT_DAMAGE_MAX]) && tmp_stats[STAT_TOHIT])
00797 catprintf(output, " (%i-%i, %i)", tmp_stats[STAT_DAMAGE_MIN], tmp_stats[STAT_DAMAGE_MAX], tmp_stats[STAT_TOHIT]);
00798 else
00799 {
00800 if(tmp_stats[STAT_DAMAGE_MIN] || tmp_stats[STAT_DAMAGE_MAX])
00801 catprintf(output, " (%i-%i)", tmp_stats[STAT_DAMAGE_MIN], tmp_stats[STAT_DAMAGE_MAX]);
00802 else if(tmp_stats[STAT_TOHIT])
00803 catprintf(output, " {%i}", tmp_stats[STAT_TOHIT]);
00804 }
00805
00806 for(i=0; i<NUM_DISPLAYED_STATS; i++)
00807 {
00808 if(tmp_stats[displayed_stats[i].stat])
00809 catprintf(output, " (%i %s)", tmp_stats[displayed_stats[i].stat], displayed_stats[i].str);
00810 }
00811
00812 }
00813
00814
00815
00816 void award_item_points(void)
00817 {
00818 int i;
00819 const item_property_desc *factor;
00820 for(i=0; i<PLRINVSIZE; i++)
00821 {
00822 if(INVENTORY(i).type==0)
00823 continue;
00824 factor = get_item_property(&INVENTORY(i), PROPERTY_SCORE);
00825 if(factor)
00826 w->plr.score += factor->score_factor * ITEMDESC(INVENTORY(i)).value * INVENTORY(i).stacksize;
00827 }
00828 }
00829
00830
00831 void rust_inventory(uint which)
00832 {
00833 if(ITEMDESC(INVENTORY(which)).flags & ITEMFLAG_RUSTPROOF)
00834 return;
00835
00836 switch(INVENTORY(which).rustiness)
00837 {
00838 case 0:
00839 message(gettext("Your %s rusts!"), shortitemname(&INVENTORY(which)));
00840 INVENTORY(which).rustiness = 1;
00841 INVENTORY(which).plus --;
00842 break;
00843 case 1:
00844 case 2:
00845 message(gettext("Your %s rusts further!"), shortitemname(&INVENTORY(which)));
00846 INVENTORY(which).rustiness++;
00847 INVENTORY(which).plus--;
00848 break;
00849 case 3:
00850 message(gettext("Your %s collapses into a pile of rust."), shortitemname(&INVENTORY(which)));
00851 INVENTORY(which).type = 0;
00852 break;
00853 }
00854 }
00855
00856
00857
00858
00859
00860
00861
00862 ushort fix_rust(void)
00863 {
00864 long cost;
00865 int which;
00866
00867 if(filter_matches(filter_rusty) == 0)
00868 return 1;
00869
00870 which = pick_item(filter_rusty);
00871
00872 if(which == -1)
00873 return 2;
00874
00875 cost = INVENTORY(which).rustiness * 100;
00876
00877 if(prompt(retprintf(gettext("Fix your %s for %li gold?"), itemname(&INVENTORY(which)), cost)))
00878 {
00879 if(player_gold() < cost) {
00880 message(gettext("You don't have enough money."));
00881 return 3;
00882 }
00883 debit_gold(cost);
00884 message(gettext("Your %s is fixed."), itemname(&INVENTORY(which)));
00885 INVENTORY(which).plus += INVENTORY(which).rustiness;
00886 INVENTORY(which).rustiness = 0;
00887 }
00888 return 3;
00889 }
00890
00891
00892
00893
00894
00895
00896 void give_gold(ulong amt)
00897 {
00898 item gold;
00899
00900 gold = randitem(w->desc.generate_currency);
00901 gold.stacksize = amt;
00902
00903 give_item(&gold, 1);
00904 }
00905
00906
00907 ulong player_gold(void)
00908 {
00909 int i;
00910 for(i=0; i<PLRINVSIZE; i++)
00911 {
00912 if(ITEMDESC(INVENTORY(i)).flags & ITEMFLAG_CURRENCY)
00913 return INVENTORY(i).stacksize;
00914 }
00915 return 0;
00916 }
00917
00918
00919 void debit_gold(ulong amt)
00920 {
00921 int i;
00922 for(i=0; i<PLRINVSIZE; i++)
00923 {
00924 if(ITEMDESC(INVENTORY(i)).flags & ITEMFLAG_CURRENCY)
00925 {
00926 if(INVENTORY(i).stacksize > amt)
00927 INVENTORY(i).stacksize -= amt;
00928 else
00929 INVENTORY(i).type = 0;
00930 return;
00931 }
00932 }
00933 }
00934
00935
00936
00937 static ulong item_value(const item *i)
00938 {
00939 int v;
00940 int factor = 5;
00941 int mod = 0;
00942
00943 v = ITEMDESC(*i).value;
00944 if(i->plus <= -2) factor = 2;
00945 else switch(i->plus) {
00946 case -1: factor = 4; break;
00947 case 0: factor = 5; break;
00948 case 1: factor = 6; mod = 20; break;
00949 case 2: factor = 7; mod = 50; break;
00950 case 3: factor = 8; mod = 100; break;
00951 case 4: factor = 9; mod = 200; break;
00952 case 5: factor = 10; mod = 300; break;
00953 }
00954 v = (v*factor)/5;
00955 if(ITEMDESC(*i).flags & ITEMFLAG_STACK)
00956 v *= i->stacksize;
00957 else if(ITEMDESC(*i).flags & ITEMFLAG_CHARGED && i->stacksize>1)
00958 v += (ITEMDESC(*i).value) * (i->stacksize-1) / 15;
00959 else
00960 v += mod;
00961 return v;
00962 }
00963
00964
00965 ulong item_cost(const item *i)
00966 {
00967 if(!(i->flags & ITEMFLAG_UNPAID))
00968 return 0;
00969
00970 return item_value(i);
00971 }
00972
00973
00974 ulong sale_price(const item *i)
00975 {
00976 return (item_value(i) * 1l) / 3l;
00977 }
00978
00979
00980
00981
00982
00983
00984 void list_discoveries(void)
00985 {
00986 int i;
00987 int num_discoveries = 0;
00988 char buf[256];
00989 draw_string_info state = {0, 0};
00990
00991 setTabStops(discovery_screen_tab_stops);
00992
00993 for(i=0; i<w->desc.itementries; i++)
00994 {
00995 if(type_is_identified(i) && !isNull(ITEMDESCN(i).unidname))
00996 {
00997 if(!num_discoveries)
00998 Graph_ClrScr();
00999
01000 sprintf(buf, "%s\t%s\n", ((const itemextension*)deref_file_ptr(ITEMDESCN(i).unidname))->str,
01001 (const char*)deref_file_ptr(ITEMDESCN(i).name));
01002 #ifdef IS_CALCULATOR
01003 SetFont(OPTION_FONT_SMALL);
01004 #endif
01005 draw_string(buf, &state, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 1);
01006 num_discoveries++;
01007 }
01008 }
01009 if(!num_discoveries)
01010 {
01011 message(gettext("You haven't identified anything interesting."));
01012 return;
01013 }
01014 read_char();
01015 full_redraw();
01016 }
01017
01018
01019 void init_discoveries(void)
01020 {
01021 int i;
01022 w->itemids = (ulong*)debug_malloc( (w->desc.itementries/32 + 1) * 4 );
01023 for(i=0; i < w->desc.itementries/32 + 1; i++)
01024 w->itemids[i] = 0;
01025 for(i=0; i<w->desc.itementries; i++) {
01026 if( ITEMDESCN(i).flags & ITEMFLAG_AUTOID )
01027 identify_type(i);
01028 }
01029 }
01030
01031
01032 void identify_type(ushort t)
01033 {
01034 w->itemids[t/32] |= 1l << (t%32l);
01035 }
01036
01037
01038 ulong type_is_identified(ulong t)
01039 {
01040 return w->itemids[t/32] & (1l << (t % 32));
01041 }
01042
01043
01044
01045
01046
01047 static const int items_startsize = 64;
01048 static const int items_allocinterval = 8;
01049
01050
01051 void compress_items(void)
01052 {
01053 int i;
01054
01055
01056 for(i=0; i<w->items.num; i++)
01057 {
01058 if(!w->items.items[i].type)
01059 memset(&w->items.items[i], 0, sizeof(item));
01060 }
01061 }
01062
01063
01064 void place_item(const item *i, ushort x, ushort y)
01065 {
01066 int top = top_item(x, y);
01067 int pos;
01068
01069
01070 item itm = *i;
01071
01072 pos = top;
01073
01074 while(pos)
01075 {
01076 if(can_stack(&itm, &w->items.items[pos]))
01077 {
01078 w->items.items[pos].stacksize += itm.stacksize;
01079 return;
01080 }
01081 pos = w->items.items[pos].next;
01082 }
01083
01084
01085 w->t[y][x].flags |= TFLAG_ITEM;
01086 if(top) w->items.items[top].flags &= ~ITEMFLAG_TOP;
01087 itm.next = top;
01088 itm.flags |= ITEMFLAG_TOP;
01089 itm.x = x;
01090 itm.y = y;
01091 add_item(itm);
01092 }
01093
01094
01095 uchar draw_item(uchar x, uchar y)
01096 {
01097 return ITEMDESC( w->items.items[ top_item(x,y) ] ).drawchar;
01098 }
01099
01100
01101 short count_items(ushort x, ushort y)
01102 {
01103 int pos, ret;
01104
01105 if(!(w->t[y][x].flags & TFLAG_ITEM))
01106 return 0;
01107
01108 pos = top_item(x, y);
01109 ret = 0;
01110
01111 while(pos != 0) {
01112 pos = w->items.items[pos].next;
01113 ret++;
01114 }
01115 return ret;
01116 }
01117
01118
01119 short top_item(ushort x, ushort y)
01120 {
01121 int ii, best_try = 0;
01122 item *itm;
01123
01124 if(!(w->t[y][x].flags & TFLAG_ITEM))
01125 return 0;
01126
01127 for(ii=0; ii<w->items.alloced; ii++)
01128 {
01129 itm = &w->items.items[ii];
01130 if(itm->x == x && itm->y == y)
01131 {
01132 if(itm->flags & ITEMFLAG_TOP)
01133 return ii;
01134 else
01135 best_try = ii;
01136 }
01137 }
01138 #ifdef DEBUG_HEAVILY
01139 message(gettext("WARNING: Damaged item chain at (%i,%i)"), (int)x, (int)y);
01140 #endif
01141 return best_try;
01142 }
01143
01144 #ifdef SUPPORT_COLOR
01145
01146 colorinfo color_item(uchar x, uchar y)
01147 {
01148 item *i = &w->items.items[top_item(x,y)];
01149 colorinfo ret = {7|COLOR_BOLD, 7|COLOR_BOLD};
01150
01151 if(ITEMDESC(*i).color.lit)
01152 ret = ITEMDESC(*i).color;
01153 else if(!isNull(ITEMDESC(*i).unidname))
01154 ret = ((const itemextension*)deref_file_ptr(ITEMDESC(*i).unidname))->color;
01155 return ret;
01156 }
01157
01158 #endif
01159
01160 void init_items()
01161 {
01162 int i;
01163
01164 w->items.num = 0;
01165 w->items.alloced = items_startsize;
01166
01167 w->items.items = debug_malloc(sizeof(item) * items_startsize);
01168
01169 for(i=0; i<w->items.alloced; i++)
01170 w->items.items[i].type = 0;
01171 }
01172
01173
01174 void cleanup_items(void)
01175 {
01176 if(w->items.items)
01177 debug_free(w->items.items);
01178 }
01179
01180
01181 static uint add_item(item itm)
01182 {
01183 int i;
01184
01185
01186 w->items.num++;
01187 if(w->items.num >= w->items.alloced) {
01188 w->items.alloced += items_allocinterval;
01189 w->items.items = (item*)debug_realloc(w->items.items,
01190 sizeof(item) * w->items.alloced);
01191
01192
01193 for(i=w->items.alloced - items_allocinterval; i<w->items.alloced; i++)
01194 w->items.items[i].type = 0;
01195 }
01196
01197
01198
01199
01200 for(i=1; ; i++) {
01201 if(!w->items.items[i].type)
01202 break;
01203 }
01204 w->items.items[i] = itm;
01205
01206 return i;
01207 }
01208
01209
01210 static void delete_item(uint which)
01211 {
01212 int i;
01213
01214
01215 for(i=0; i<w->items.alloced; i++)
01216 {
01217 if(w->items.items[i].next == which)
01218 w->items.items[i].next = w->items.items[which].next;
01219 }
01220 w->items.items[which].type = 0;
01221 w->items.num--;
01222 }
01223
01224
01225
01226 item randitem(filelink type)
01227 {
01228 item i;
01229 memset(&i, 0, sizeof(i));
01230
01231 i.type = ((itemdesc*)deref_file_ptr(type)) -> num;
01232
01233 i.stacksize = 1;
01234 i.rustiness = 0;
01235
01236 if(ITEMDESC(i).flags & (ITEMFLAG_STACK|ITEMFLAG_CHARGED|ITEMFLAG_FUELED|ITEMFLAG_HIDECHARGE))
01237 i.stacksize = nrandom(ITEMDESC(i).stacksize, ITEMDESC(i).stacksize/2);
01238 if(i.stacksize<1) i.stacksize = 1;
01239
01240 if(ITEMDESC(i).flags & ITEMFLAG_PLUS)
01241 i.plus = random_plus();
01242 if(!(ITEMDESC(i).flags & ITEMFLAG_RUSTPROOF))
01243 {
01244 if(RANGE(7,0) == 0)
01245 {
01246 i.rustiness = RANGE(2,1);
01247 i.plus -= i.rustiness;
01248 }
01249 }
01250
01251 if(i.plus < 0 || ITEMDESC(i).flags & ITEMFLAG_GEN_CURSED)
01252 i.flags |= ITEMFLAG_CURSED;
01253
01254 return i;
01255 }
01256
01257
01258 static schar random_plus(void)
01259 {
01260
01261 const schar weighted_plus[16] = {
01262 1, 1,
01263 1, 1,
01264 1, 1,
01265 1, 1,
01266 2, 2,
01267 2, 2,
01268 3, 3,
01269 4, 5
01270 };
01271
01272 int t;
01273
01274
01275 t = RANGE(10,1);
01276 if (t==10) return weighted_plus[lrand()%16];
01277 else if(t==1) return -weighted_plus[lrand()%16];
01278 else return 0;
01279 }
01280
01281
01282
01283 static uint can_stack(const item *one, const item *two)
01284 {
01285 if( !(ITEMDESC(*one).flags & ITEMFLAG_STACK) )
01286 return 0;
01287
01288 if(one->type != two->type)
01289 return 0;
01290 if(one->plus != two->plus)
01291 return 0;
01292 if(one->rustiness != two->rustiness)
01293 return 0;
01294
01295 if( one->plus )
01296 {
01297 if( (one->flags ^ two->flags) & ITEMFLAG_IDENTIFIED )
01298 return 0;
01299 }
01300
01301 if( (one->flags ^ two->flags)
01302 & ~(ITEMFLAG_EQUIPPED|ITEMFLAG_IDENTIFIED|ITEMFLAG_TOP) )
01303 return 0;
01304
01305 return 1;
01306 }
01307
01308
01309
01310
01311
01312
01313 void drop_item(void)
01314 {
01315 int which;
01316
01317 if( filter_matches(filter_none) == 0 )
01318 {
01319 message(gettext("You don't have anything to drop."));
01320 return;
01321 }
01322
01323 message(gettext("Drop what?"));
01324 which = pick_item(filter_none);
01325
01326 if(which<0) {
01327 message(gettext("Never mind."));
01328 return;
01329 }
01330
01331 do_drop_item(which);
01332 }
01333
01334
01335 void drop_multiple(void)
01336 {
01337 if( filter_matches(filter_none) == 0 )
01338 {
01339 message(gettext("You don't have anything to drop."));
01340 return;
01341 }
01342 message(gettext("Drop what?"));
01343
01344 pick_multiple(filter_none, &do_drop_item);
01345 }
01346
01347
01348 static void do_drop_item(int which)
01349 {
01350 ulong value = 0;
01351
01352 if(which<0)
01353 return;
01354
01355 if(INVENTORY(which).flags & ITEMFLAG_EQUIPPED)
01356 {
01357 if( ITEMDESC(INVENTORY(which)).spot == 0 ) {
01358 message(gettext("You need to unwield it first."));
01359 return;
01360 } else {
01361 message(gettext("You can't drop something you're wearing."));
01362 return;
01363 }
01364 }
01365
01366 if(w->t[w->plr.y][w->plr.x].type == TILE_SHOPFLOOR && !(INVENTORY(which).flags & ITEMFLAG_UNPAID))
01367 {
01368 if(ITEMDESC(INVENTORY(which)).flags & ITEMFLAG_CURRENCY) {
01369 message(gettext("You can't sell gold."));
01370 return;
01371 }
01372 value = sale_price(&INVENTORY(which));
01373 if(!prompt(retprintf(gettext("Sell your %s for %li gold?"), itemname(&INVENTORY(which)), value)))
01374 return;
01375 message(gettext("You sell the %s."), itemname(&INVENTORY(which)));
01376 INVENTORY(which).flags |= ITEMFLAG_UNPAID;
01377 } else {
01378 message(gettext("You drop the %s."), itemname(&INVENTORY(which)));
01379 }
01380 place_item(&INVENTORY(which), w->plr.x, w->plr.y);
01381 INVENTORY(which).type=0;
01382 if(value)
01383 give_gold(value);
01384
01385 update_player();
01386 timepass(1);
01387 }
01388
01389
01390 static const int max_worn_items[10] =
01391 {
01392 1,
01393 1,
01394 1,
01395 1,
01396 1,
01397 1,
01398 1,
01399 1,
01400 2,
01401 1
01402 };
01403
01404
01405 void wear_item(void)
01406 {
01407 int which;
01408
01409 if( filter_matches(filter_wearable) == 0 )
01410 {
01411 message(gettext("You don't have anything to equip."));
01412 return;
01413 }
01414 message(gettext("Equip what?"));
01415
01416 which=pick_item(filter_wearable);
01417
01418 if(which<0) {
01419 message(gettext("Never mind."));
01420 return;
01421 }
01422
01423 do_wear_item(which);
01424 }
01425
01426
01427 void wear_multiple(void)
01428 {
01429 if( filter_matches(filter_wearable) == 0 )
01430 {
01431 message(gettext("You don't have anything to equip."));
01432 return;
01433 }
01434 message(gettext("Equip what?"));
01435
01436 pick_multiple(filter_wearable, &do_wear_item);
01437 }
01438
01439
01440 static void do_wear_item(int which)
01441 {
01442 int slot;
01443 item *unequip;
01444
01445 slot = ITEMDESC(INVENTORY(which)).spot;
01446
01447 if(num_player_equipment(slot) >= max_worn_items[slot])
01448 {
01449 unequip = player_equipment(slot);
01450
01451 if( unequip )
01452 {
01453 if(unequip->flags & ITEMFLAG_CURSED)
01454 {
01455 if(slot) message(gettext("You can't remove your old armor."));
01456 else message(gettext("Your weapon is welded to your hand!"));
01457 return;
01458 }
01459 unequip->flags &= ~ITEMFLAG_EQUIPPED;
01460 message(
01461 (slot>1) ? (gettext("You take off the %s")) :
01462 (slot==1) ? (gettext("You unready the %s")) :
01463 (gettext("You unwield the %s")),
01464 itemname(unequip));
01465 }
01466 }
01467
01468 identify_type(INVENTORY(which).type);
01469 INVENTORY(which).flags |= ITEMFLAG_IDENTIFIED;
01470
01471 call_usefunc( get_item_property(&INVENTORY(which),
01472 PROPERTY_WEAR_FUNC)->function, which );
01473
01474 update_player();
01475
01476 timepass(1);
01477 }
01478
01479
01480 void takeoff_item(void)
01481 {
01482 int which;
01483
01484 if( filter_matches(filter_worn) == 0 )
01485 {
01486 message(gettext("You don't have anything to unequip."));
01487 return;
01488 }
01489
01490 message(gettext("Unequip what?"));
01491 which = pick_item(filter_worn);
01492
01493 if(which<0)
01494 {
01495 message(gettext("Never mind."));
01496 return;
01497 }
01498
01499 do_takeoff_item(which);
01500 }
01501
01502
01503 void takeoff_multiple(void)
01504 {
01505 if( filter_matches(filter_worn) == 0 )
01506 {
01507 message(gettext("You don't have anything to unequip."));
01508 return;
01509 }
01510 message(gettext("Unequip what?"));
01511
01512 pick_multiple(filter_worn, &do_takeoff_item);
01513 }
01514
01515
01516 static void do_takeoff_item(int which)
01517 {
01518 int slot = ITEMDESC(INVENTORY(which)).spot;
01519
01520 if( !(INVENTORY(which).flags & ITEMFLAG_EQUIPPED) )
01521 {
01522 message(gettext("You aren't wearing/wielding that."));
01523 return;
01524 }
01525
01526 if( INVENTORY(which).flags & ITEMFLAG_CURSED )
01527 {
01528 message(gettext("You can't. It seems to be cursed."));
01529 return;
01530 }
01531
01532 INVENTORY(which).flags &= ~ITEMFLAG_EQUIPPED;
01533
01534 message(
01535 (slot>1) ? gettext("You take off the %s.") :
01536 (slot==1) ? gettext("You unready the %s.") :
01537 gettext("You unwield the %s."),
01538 itemname(&INVENTORY(which)));
01539
01540 update_player();
01541
01542 timepass(1);
01543 }
01544
01545
01546 void item_step(void)
01547 {
01548 int num;
01549 switch(w->options[OPTION_AUTOPICKUP])
01550 {
01551 case OPTION_AUTO_NO:
01552 num = count_items(w->plr.x, w->plr.y);
01553 if(num > 5)
01554 message(gettext("You see many items here."));
01555 else if(num > 1)
01556 message(gettext("You see several items here."));
01557 else
01558 message(gettext("You see a %s here."),
01559 itemname(&w->items.items[top_item(w->plr.x, w->plr.y)]));
01560 break;
01561 case OPTION_AUTO_YES:
01562 pickup(0);
01563 break;
01564 case OPTION_AUTO_PROMPT:
01565 pickup(1);
01566 break;
01567 }
01568 }
01569
01570
01571 void pickup(uint pickup_prompt)
01572 {
01573 sshort x=w->plr.x, y=w->plr.y;
01574 uint pos, next;
01575 uint top;
01576 uint do_pickup = 0;
01577 item *i;
01578
01579 top = top_item(x, y);
01580 pos = top;
01581
01582 while(pos)
01583 {
01584 next = w->items.items[pos].next;
01585 i = &w->items.items[pos];
01586
01587 if(pickup_prompt || (i->flags & ITEMFLAG_UNPAID))
01588 {
01589 do_pickup = prompt(
01590 retprintf(
01591 ((i->flags&ITEMFLAG_UNPAID)?
01592 gettext("Buy the %s?") :
01593 gettext("Pick up the %s?")),
01594 itemname(&w->items.items[pos])
01595 ) );
01596 } else {
01597 do_pickup = 1;
01598 }
01599 if( do_pickup )
01600 {
01601 if(item_cost(i) > player_gold())
01602 {
01603 message(gettext("You can't afford that."));
01604 return;
01605 }
01606 if(i->flags & ITEMFLAG_UNPAID)
01607 {
01608 identify_type(i->type);
01609 debit_gold(item_cost(i));
01610 i->flags &= ~ITEMFLAG_UNPAID;
01611 }
01612 if(give_item(&w->items.items[pos], 0))
01613 {
01614 if(pos==top) {
01615 if(w->items.items[pos].next) {
01616 top = w->items.items[pos].next;
01617 w->items.items[ top ].flags |= ITEMFLAG_TOP;
01618 } else {
01619 w->t[y][x].flags &= ~TFLAG_ITEM;
01620 }
01621 }
01622 delete_item(pos);
01623 timepass(1);
01624 }
01625 }
01626 pos = next;
01627 }
01628 update_player();
01629 }
01630
01631
01632 void hotkey_item(void)
01633 {
01634 int which, hotkey;
01635 message(gettext("Hotkey which item?"));
01636 which = pick_item(&filter_none);
01637 if(w<0)
01638 {
01639 message(gettext("Never mind."));
01640 return;
01641 }
01642 message(gettext("Use what hotkey? [0-9]"));
01643 draw();
01644 hotkey = read_char();
01645 if(hotkey<'0' || hotkey>'9')
01646 {
01647 if(hotkey==KEY_ESC)
01648 INVENTORY(which).hotkey = 0;
01649 else
01650 message(gettext("Invalid selection."));
01651 }
01652 else
01653 INVENTORY(which).hotkey = hotkey;
01654 }
01655
01656