Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

src/items.c

Go to the documentation of this file.
00001 /* {{{
00002  * CalcRogue, a roguelike game for PCs, calculators and PDAs
00003  * Copyright (C) 2003 Jim Babcock
00004  * 
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  * 
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  * 
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018  * }}} */
00019 // items.c
00022 
00023 #include "crogue.h"
00024 #include "export.h"
00025 
00026 // Inventory filters
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 // Inventory menus
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 // Player inventory
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 // Equipment handling
00053 item *player_equipment(int slot);
00054 static int num_player_equipment(int slot);
00055 
00056 // Utility functions
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 // Shopping
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 // Misc
00075 void list_discoveries(void);
00076 void identify_type(ushort t);
00077 ulong type_is_identified(ulong t);
00078 
00079 // Item storage
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 // Player actions
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  * Constant data
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  * Inventory filters
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         // An individual item of identified type must be (potentially) special in 
00166         // some way to be considered unidentified
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  * Inventory menus
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 // Works ONLY on a pre-sorted inventory. (use sort_inventory())
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  * Player inventory
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     // Initialize inventory empty
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     // Look for items of the same type to stack with
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     // If it can't stack, look for empty slots
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     // Otherwise, give up
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     // Look for items of the same type to stack with
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     // If it can't stack, look for empty slots
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     // Otherwise, drop it on the floor
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  * Equipment handling
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  * Utility functions
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 // Returns:
00858 //   0: Fixed an item
00859 //   1: Nothing to fix
00860 //   2: Player cancelled
00861 //   3: Not enough money or refused
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  * Shopping
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  * Misc
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  * Item storage
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     // Zero out unused items
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     // Check for items on the floor which it can stack with
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     // Add to the top of the stack
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 // In case of corruption, at least get something in the chain
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     // Allocate more memory if necessary
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         // Init newly created space
01193         for(i=w->items.alloced - items_allocinterval; i<w->items.alloced; i++)
01194             w->items.items[i].type = 0;
01195     }
01196     
01197     // Find an empty spot in the items array
01198     // i<items_alloced test unnecessary since resizing guarantees break first
01199     // Starting at 1 (wasting spot 0) to avoid confusing item 0 with 'no item'
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     // Find any items with link-fields pointing to the deleted item and redirect
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     // One in 10 items is positively enchanted, another 1 in 10 is negatively enchanted.
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  * Player actions
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 

Generated on Thu May 20 13:12:10 2004 for CalcRogue by doxygen 1.3.6