ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/living.c
Revision: 1.1
Committed: Fri Feb 3 07:11:32 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Branch point for: UPSTREAM
Log Message:
Initial revision

File Contents

# User Rev Content
1 root 1.1 /*
2     * static char *rcsid_living_c =
3     * "$Id: living.c,v 1.75 2006/01/09 19:35:10 cavesomething Exp $";
4     */
5    
6     /*
7     CrossFire, A Multiplayer game for X-windows
8    
9     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
10     Copyright (C) 1992 Frank Tore Johansen
11    
12     This program is free software; you can redistribute it and/or modify
13     it under the terms of the GNU General Public License as published by
14     the Free Software Foundation; either version 2 of the License, or
15     (at your option) any later version.
16    
17     This program is distributed in the hope that it will be useful,
18     but WITHOUT ANY WARRANTY; without even the implied warranty of
19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20     GNU General Public License for more details.
21    
22     You should have received a copy of the GNU General Public License
23     along with this program; if not, write to the Free Software
24     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25    
26     The authors can be reached via e-mail at crossfire-devel@real-time.com
27     */
28    
29     #include <global.h>
30     #include <funcpoint.h>
31    
32     /* Handy little macro that adds exp and keeps it within bounds. Since
33     * we are now using 64 bit values, I'm not all concerned about overflow issues
34     * with exptotal wrapping. exptotal is typically op->exp, or op->perm_exp
35     */
36     #define ADD_EXP(exptotal, exp) {exptotal += exp; if (exptotal > MAX_EXPERIENCE) exptotal = MAX_EXPERIENCE; }
37    
38     static const int con_bonus[MAX_STAT + 1]={
39     -6,-5,-4,-3,-2,-1,-1,0,0,0,0,1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,
40     22,25,30,40,50
41     };
42    
43     /* changed the name of this to "sp_bonus" from "int_bonus"
44     * because Pow can now be the stat that controls spellpoint
45     * advancement. -b.t.
46     */
47     static const int sp_bonus[MAX_STAT + 1]={
48     -10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12,15,20,25,
49     30,40,50,70,100
50     };
51    
52     static const int grace_bonus[MAX_STAT +1] = {
53     -10,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,12,15,20,25,
54     30,40,50,70,100
55     };
56    
57     /* 0.92.7 Changed way charisma works. Values now
58     * represent how much more it costs to buy something than to sell it
59     * (10, a value of 10 means it is that if it costs 50 gp to buy, you
60     * would only get 5 gp when you sell.) Let query_cost do the calculations
61     * on how to really do this. Buy keeping it this simple number, it is
62     * much easier to know how things will be influenced. A value of '1' means
63     * buying and selling is both the same value - any value less than or equal
64     * to 1 should not be used.
65     * At least as of now, the only place that uses this code is query_cost,
66     * in server/shop.c. This bonus is split evenly between buying and selling
67     * (ie, if the bonus is 2.0, then items are bought for 1.33 list, and sold
68     * at .667
69     * This is figured by diff=(y-1)/(1+y), and for buy, it is 1+diff, for sell
70     * it is 1-diff
71     */
72    
73     const float cha_bonus[MAX_STAT + 1]={10.0, 10.0, 9.0, 8.0, 7.0, 6.0, /*<-5*/
74     5.0, 4.5, 4.0, 3.5, 3.0, /*<-10*/ 2.9, 2.8, 2.7, 2.6, 2.5, /*<-15*/
75     2.4, 2.3, 2.2, 2.1, 2.0, /*<-20*/ 1.95, 1.90, 1.85, 1.80, 1.75, /*25 */
76     1.70, 1.65, 1.60, 1.55, 1.50 /*30 */
77     };
78    
79     const int dex_bonus[MAX_STAT + 1]={
80     -4,-3,-2,-2,-1,-1,-1,0,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,7
81     };
82    
83     /* speed_bonus uses dex as its stat */
84     const float speed_bonus[MAX_STAT + 1]={
85     -0.4, -0.4, -0.3, -0.3, -0.2, -0.2, -0.2, -0.1, -0.1, -0.1, -0.05, 0, 0, 0,
86     0.05, 0.1, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.4,
87     1.6, 1.8, 2.0, 2.5, 3.0
88     };
89    
90     /* dam_bonus, thaco_bonus, max_carry, weight limit all are based on
91     * strength.
92     */
93     const int dam_bonus[MAX_STAT + 1]={
94     -2,-2,-2,-1,-1,-1,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,6,6,7,8,10,15
95     };
96    
97     const int thaco_bonus[MAX_STAT + 1]={
98     -2,-2,-1,-1,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,6,7,8,10
99     };
100    
101     /* Max you can carry before you start getting extra speed penalties */
102     const int max_carry[MAX_STAT + 1]={
103     2,4,7,11,16,22,29,37,46,56,67,79,92,106,121,137,154,172,191,211,232,254,277,
104     301,326,352,400,450,500,600,1000
105     };
106    
107     /* weight_limit - the absolute most a character can carry - a character can't
108     * pick stuff up if it would put him above this limit.
109     * value is in grams, so we don't need to do conversion later
110     * These limits are probably overly generous, but being there were no values
111     * before, you need to start someplace.
112     */
113    
114     const uint32 weight_limit[MAX_STAT+ 1] = {
115     200000, /* 0 */
116     250000,300000,350000,400000,500000, /* 5*/
117     600000,700000,800000,900000,1000000, /* 10 */
118     1100000,1200000,1300000,1400000,1500000,/* 15 */
119     1650000,1800000,1950000,2100000,2250000,/* 20 */
120     2400000,2550000,2700000,2850000,3000000, /* 25 */
121     3250000,3500000,3750000,4000000,4500000 /*30 */
122     };
123    
124     const int learn_spell[MAX_STAT + 1]={
125     0,0,0,1,2,4,8,12,16,25,36,45,55,65,70,75,80,85,90,95,100,100,100,100,100,
126     100,100,100,100,100,100
127     };
128    
129     const int cleric_chance[MAX_STAT + 1]={
130     100,100,100,100,90,80,70,60,50,40,35,30,25,20,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0
131     };
132    
133     const int turn_bonus[MAX_STAT + 1]={
134     -1,-1,-1,-1,-1,-1,-1,-1,0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5,6,7,8,9,10,12,15
135     };
136    
137     const int fear_bonus[MAX_STAT + 1]={
138     3,3,3,3,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
139     };
140    
141     /*
142     Since this is nowhere defined ...
143     Both come in handy at least in function add_exp()
144     */
145    
146     #define MAX_EXPERIENCE levels[settings.max_level]
147    
148     /* because exp_obj sum to make the total score,
149     * we cannot allow that sum to exceed the maximum
150     * amount of experience a player can gain. Thus
151     * we define MAX_EXP_IN_OBJ. It is important to try
152     * to make the value of MAX_EXP_CAT close to the
153     * actual number of experience objects in the game,
154     * otherwise the maximum level in any experience
155     * category could be quite low. To help the situation
156     * out a little I added 10 more levels, and jacked
157     * up the last level experience value. Its out of
158     * line with progression of previous levels, so
159     * if more levels are desired, this should be fixed.
160     * -b.t.
161     */
162    
163     #define MAX_EXP_IN_OBJ levels[settings.max_level]/(MAX_EXP_CAT - 1)
164    
165     #ifndef WIN32
166     extern uint64 *levels;
167     #else
168     extern sint64 *levels;
169     #endif
170    
171     #define MAX_SAVE_LEVEL 110
172     /* This no longer needs to be changed anytime the number of
173     * levels is increased - rather, did_make_save will do the
174     * right thing and always use range within this table.
175     * for safety, savethrow should not be accessed directly anymore,
176     * and instead did_make_save should be used instead.
177     */
178     static const int savethrow[MAX_SAVE_LEVEL+1]={
179     18,
180     18,17,16,15,14,14,13,13,12,12,12,11,11,11,11,10,10,10,10, 9,
181     9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
182     6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4,
183     4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2,
184     2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
185     1, 1, 1, 1, 1, 1, 1, 1, 1, 1
186     };
187    
188     const char *const attacks[NROFATTACKS] = {
189     "physical", "magical", "fire", "electricity", "cold", "confusion",
190     "acid", "drain", "weaponmagic", "ghosthit", "poison", "slow",
191     "paralyze", "turn undead", "fear", "cancellation", "depletion", "death",
192     "chaos","counterspell","god power","holy power","blinding", "",
193     "life stealing"
194     };
195    
196     static const char *const drain_msg[NUM_STATS] = {
197     "Oh no! You are weakened!",
198     "You're feeling clumsy!",
199     "You feel less healthy",
200     "You suddenly begin to lose your memory!",
201     "Your face gets distorted!",
202     "Watch out, your mind is going!",
203     "Your spirit feels drained!"
204     };
205     const char *const restore_msg[NUM_STATS] = {
206     "You feel your strength return.",
207     "You feel your agility return.",
208     "You feel your health return.",
209     "You feel your wisdom return.",
210     "You feel your charisma return.",
211     "You feel your memory return.",
212     "You feel your spirits return."
213     };
214     const char *const gain_msg[NUM_STATS] = {
215     "You feel stronger.",
216     "You feel more agile.",
217     "You feel healthy.",
218     "You feel wiser.",
219     "You seem to look better.",
220     "You feel smarter.",
221     "You feel more potent."
222     };
223     const char *const lose_msg[NUM_STATS] = {
224     "You feel weaker!",
225     "You feel clumsy!",
226     "You feel less healthy!",
227     "You lose some of your memory!",
228     "You look ugly!",
229     "You feel stupid!",
230     "You feel less potent!"
231     };
232    
233     const char *const statname[NUM_STATS] = {
234     "strength", "dexterity", "constitution", "wisdom", "charisma", "intelligence","power"
235     };
236    
237     const char *const short_stat_name[NUM_STATS] = {
238     "Str", "Dex", "Con", "Wis", "Cha", "Int","Pow"
239     };
240    
241     /*
242     * sets Str/Dex/con/Wis/Cha/Int/Pow in stats to value, depending on
243     * what attr is (STR to POW).
244     */
245    
246     void
247     set_attr_value(living *stats,int attr,sint8 value) {
248     switch(attr) {
249     case STR:
250     stats->Str=value;
251     break;
252     case DEX:
253     stats->Dex=value;
254     break;
255     case CON:
256     stats->Con=value;
257     break;
258     case WIS:
259     stats->Wis=value;
260     break;
261     case POW:
262     stats->Pow=value;
263     break;
264     case CHA:
265     stats->Cha=value;
266     break;
267     case INT:
268     stats->Int=value;
269     break;
270     }
271     }
272    
273     /*
274     * Like set_attr_value(), but instead the value (which can be negative)
275     * is added to the specified stat.
276     */
277    
278     void
279     change_attr_value(living *stats,int attr,sint8 value) {
280     if (value==0) return;
281     switch(attr) {
282     case STR:
283     stats->Str+=value;
284     break;
285     case DEX:
286     stats->Dex+=value;
287     break;
288     case CON:
289     stats->Con+=value;
290     break;
291     case WIS:
292     stats->Wis+=value;
293     break;
294     case POW:
295     stats->Pow+=value;
296     break;
297     case CHA:
298     stats->Cha+=value;
299     break;
300     case INT:
301     stats->Int+=value;
302     break;
303     default:
304     LOG(llevError,"Invalid attribute in change_attr_value: %d\n", attr);
305     }
306     }
307    
308     /*
309     * returns the specified stat. See also set_attr_value().
310     */
311    
312     sint8
313     get_attr_value(living *stats,int attr) {
314     switch(attr) {
315     case STR:
316     return(stats->Str);
317     case DEX:
318     return(stats->Dex);
319     case CON:
320     return(stats->Con);
321     case WIS:
322     return(stats->Wis);
323     case CHA:
324     return(stats->Cha);
325     case INT:
326     return(stats->Int);
327     case POW:
328     return(stats->Pow);
329     }
330     return 0;
331     }
332    
333     /*
334     * Ensures that all stats (str/dex/con/wis/cha/int) are within the
335     * 1-30 stat limit.
336     */
337    
338     void check_stat_bounds(living *stats) {
339     int i,v;
340     for(i=0;i<NUM_STATS;i++)
341     if((v=get_attr_value(stats,i))>MAX_STAT)
342     set_attr_value(stats,i,MAX_STAT);
343     else if(v<MIN_STAT)
344     set_attr_value(stats,i,MIN_STAT);
345     }
346    
347     #define ORIG_S(xyz,abc) (op->contr->orig_stats.abc)
348    
349     /* Rather than having a whole bunch of if (flag) new_draw.. else new_draw,
350     * make this macro to clean those up. Not usuable outside change_abil
351     * function since some of the values passed to new_draw_info are hardcoded.
352     */
353     #define DIFF_MSG(flag, msg1, msg2) \
354     new_draw_info(NDI_UNIQUE, 0, op, (flag>0)?msg1:msg2);
355    
356     /* return 1 if we sucessfully changed a stat, 0 if nothing was changed. */
357     /* flag is set to 1 if we are applying the object, -1 if we are removing
358     * the object.
359     * It is the calling functions responsibilty to check to see if the object
360     * can be applied or not.
361     * The main purpose of calling this function is the messages that are
362     * displayed - fix_player should really always be called after this when
363     * removing an object - that is because it is impossible to know if some object
364     * is the only source of an attacktype or spell attunement, so this function
365     * will clear the bits, but the player may still have some other object
366     * that gives them that ability.
367     */
368     int change_abil(object *op, object *tmp) {
369     int flag=QUERY_FLAG(tmp,FLAG_APPLIED)?1:-1,i,j,success=0;
370     object refop;
371     char message[MAX_BUF];
372     int potion_max=0;
373    
374     /* remember what object was like before it was changed. note that
375     * refop is a local copy of op only to be used for detecting changes
376     * found by fix_player. refop is not a real object
377     */
378     memcpy(&refop, op, sizeof(object));
379    
380     if(op->type==PLAYER) {
381     if (tmp->type==POTION) {
382     potion_max=1;
383     for(j=0;j<NUM_STATS;j++) {
384     int nstat, ostat;
385    
386     ostat = get_attr_value(&(op->contr->orig_stats),j);
387     i = get_attr_value(&(tmp->stats),j);
388    
389     /* nstat is what the stat will be after use of the potion */
390     nstat = flag*i + ostat;
391    
392     /* Do some bounds checking. While I don't think any
393     * potions do so right now, there is the potential for potions
394     * that adjust that stat by more than one point, so we need
395     * to allow for that.
396     */
397     if (nstat < 1 && i*flag < 0 ) nstat = 1;
398     else if (nstat > 20 + get_attr_value(&(op->arch->clone.stats),j)) {
399     nstat = 20 + get_attr_value(&(op->arch->clone.stats),j);
400     }
401     if (nstat != ostat) {
402     set_attr_value(&(op->contr->orig_stats), j, nstat);
403     potion_max=0;
404     }
405     else if (i) {
406     /* potion is useless - player has already hit the natural maximum */
407     potion_max = 1;
408     }
409     }
410     /* This section of code ups the characters normal stats also. I am not
411     * sure if this is strictly necessary, being that fix_player probably
412     * recalculates this anyway.
413     */
414     for(j=0;j<NUM_STATS;j++)
415     change_attr_value(&(op->stats),j,flag*get_attr_value(&(tmp->stats),j));
416     check_stat_bounds(&(op->stats));
417     } /* end of potion handling code */
418     }
419    
420     /* reset attributes that fix_player doesn't reset since it doesn't search
421     * everything to set
422     */
423     if(flag == -1) {
424     op->attacktype&=~tmp->attacktype;
425     op->path_attuned&=~tmp->path_attuned;
426     op->path_repelled&=~tmp->path_repelled;
427     op->path_denied&=~tmp->path_denied;
428     /* Presuming here that creatures only have move_type,
429     * and not the other move_ fields.
430     */
431     op->move_type &= ~tmp->move_type;
432     }
433    
434     /* call fix_player since op object could have whatever attribute due
435     * to multiple items. if fix_player always has to be called after
436     * change_ability then might as well call it from here
437     */
438     fix_player(op);
439    
440     /* Fix player won't add the bows ability to the player, so don't
441     * print out message if this is a bow.
442     */
443     if(tmp->attacktype & AT_CONFUSION && tmp->type != BOW) {
444     success=1;
445     DIFF_MSG(flag, "Your hands begin to glow red.",
446     "Your hands stop glowing red.");
447     }
448     if ( QUERY_FLAG(op,FLAG_LIFESAVE) != QUERY_FLAG(&refop,FLAG_LIFESAVE)){
449     success=1;
450     DIFF_MSG(flag, "You feel very protected.",
451     "You don't feel protected anymore.");
452     }
453     if ( QUERY_FLAG(op,FLAG_REFL_MISSILE) != QUERY_FLAG(&refop,FLAG_REFL_MISSILE)){
454     success=1;
455     DIFF_MSG(flag, "A magic force shimmers around you.",
456     "The magic force fades away.");
457     }
458     if ( QUERY_FLAG(op,FLAG_REFL_SPELL) != QUERY_FLAG(&refop,FLAG_REFL_SPELL)){
459     success=1;
460     DIFF_MSG(flag, "You feel more safe now, somehow.",
461     "Suddenly you feel less safe, somehow.");
462     }
463     /* movement type has changed. We don't care about cases where
464     * user has multiple items giving the same type appled like we
465     * used to - that is more work than what we gain, plus messages
466     * can be misleading (a little higher could be miscontrued from
467     * from fly high)
468     */
469     if (tmp->move_type && op->move_type != refop.move_type) {
470     success=1;
471    
472     /* MOVE_FLY_HIGH trumps MOVE_FLY_LOW - changing your move_fly_low
473     * status doesn't make a difference if you are flying high
474     */
475     if (tmp->move_type & MOVE_FLY_LOW && !(op->move_type & MOVE_FLY_HIGH)) {
476     DIFF_MSG(flag, "You start to float in the air!.", "You float down to the ground.");
477     }
478    
479     if (tmp->move_type & MOVE_FLY_HIGH) {
480     /* double conditional - second case covers if you have move_fly_low -
481     * in that case, you don't actually land
482     */
483     DIFF_MSG(flag, "You soar into the air air!.",
484     (op->move_type&MOVE_FLY_LOW ? "You fly lower in the air":
485     "You float down to the ground."));
486     }
487     if (tmp->move_type & MOVE_SWIM)
488     DIFF_MSG(flag,"You feel ready for a swim", "You no longer feel like swimming");
489    
490     /* Changing move status may mean you are affected by things you weren't before */
491     check_move_on(op, op);
492     }
493    
494     /* becoming UNDEAD... a special treatment for this flag. Only those not
495     * originally undead may change their status
496     */
497     if(!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD))
498     if ( QUERY_FLAG(op,FLAG_UNDEAD) != QUERY_FLAG(&refop,FLAG_UNDEAD)) {
499     success=1;
500     if(flag>0) {
501     if(op->race) free_string(op->race);
502     op->race=add_string("undead");
503     new_draw_info(NDI_UNIQUE, 0, op,"Your lifeforce drains away!");
504     } else {
505     if(op->race) free_string(op->race);
506     if(op->arch->clone.race)
507     op->race=add_string(op->arch->clone.race);
508     else
509     op->race = NULL;
510     new_draw_info(NDI_UNIQUE, 0, op,"Your lifeforce returns!");
511     }
512     }
513    
514     if ( QUERY_FLAG(op,FLAG_STEALTH) != QUERY_FLAG(&refop,FLAG_STEALTH)){
515     success=1;
516     DIFF_MSG(flag, "You walk more quietly.", "You walk more noisily.");
517     }
518     if ( QUERY_FLAG(op,FLAG_MAKE_INVIS) != QUERY_FLAG(&refop,FLAG_MAKE_INVIS)){
519     success=1;
520     DIFF_MSG(flag, "You become transparent.", "You can see yourself.");
521     }
522     /* blinded you can tell if more blinded since blinded player has minimal
523     * vision
524     */
525     if(QUERY_FLAG(tmp,FLAG_BLIND)) {
526     success=1;
527     if(flag>0) {
528     if(QUERY_FLAG(op,FLAG_WIZ))
529     new_draw_info(NDI_UNIQUE, 0, op,"Your mortal self is blinded.");
530     else {
531     new_draw_info(NDI_UNIQUE, 0, op,"You are blinded.");
532     SET_FLAG(op,FLAG_BLIND);
533     if(op->type==PLAYER)
534     op->contr->do_los=1;
535     }
536     } else {
537     if(QUERY_FLAG(op,FLAG_WIZ))
538     new_draw_info(NDI_UNIQUE, 0, op,"Your mortal self can now see again.");
539     else {
540     new_draw_info(NDI_UNIQUE, 0, op,"Your vision returns.");
541     CLEAR_FLAG(op,FLAG_BLIND);
542     if(op->type==PLAYER)
543     op->contr->do_los=1;
544     }
545     }
546     }
547    
548     if ( QUERY_FLAG(op,FLAG_SEE_IN_DARK) != QUERY_FLAG(&refop,FLAG_SEE_IN_DARK)){
549     success=1;
550     if(op->type==PLAYER)
551     op->contr->do_los=1;
552     DIFF_MSG(flag, "Your vision is better in the dark.", "You see less well in the dark.");
553     }
554    
555     if ( QUERY_FLAG(op,FLAG_XRAYS) != QUERY_FLAG(&refop,FLAG_XRAYS)){
556     success=1;
557     if(flag>0) {
558     if(QUERY_FLAG(op,FLAG_WIZ))
559     new_draw_info(NDI_UNIQUE, 0, op,"Your vision becomes a little clearer.");
560     else {
561     new_draw_info(NDI_UNIQUE, 0, op,"Everything becomes transparent.");
562     if(op->type==PLAYER)
563     op->contr->do_los=1;
564     }
565     } else {
566     if(QUERY_FLAG(op,FLAG_WIZ))
567     new_draw_info(NDI_UNIQUE, 0, op,"Your vision becomes a bit out of focus.");
568     else {
569     new_draw_info(NDI_UNIQUE, 0, op,"Everything suddenly looks very solid.");
570     if(op->type==PLAYER)
571     op->contr->do_los=1;
572     }
573     }
574     }
575    
576     if(tmp->stats.luck) {
577     success=1;
578     DIFF_MSG(flag*tmp->stats.luck, "You feel more lucky.", "You feel less lucky.");
579     }
580    
581     if(tmp->stats.hp && op->type==PLAYER) {
582     success=1;
583     DIFF_MSG(flag*tmp->stats.hp, "You feel much more healthy!",
584     "You feel much less healthy!");
585     }
586    
587     if(tmp->stats.sp && op->type==PLAYER && tmp->type!=SKILL) {
588     success=1;
589     DIFF_MSG(flag*tmp->stats.sp, "You feel one with the powers of magic!",
590     "You suddenly feel very mundane.");
591     }
592    
593     /* for the future when artifacts set this -b.t. */
594     if(tmp->stats.grace && op->type==PLAYER) {
595     success=1;
596     DIFF_MSG(flag*tmp->stats.grace, "You feel closer to your god!",
597     "You suddenly feel less holy.");
598     }
599    
600     if(tmp->stats.food && op->type==PLAYER) {
601     success=1;
602     DIFF_MSG(flag*tmp->stats.food, "You feel your digestion slowing down.",
603     "You feel your digestion speeding up.");
604     }
605    
606     /* Messages for changed resistance */
607     for (i=0; i<NROFATTACKS; i++) {
608     if (i==ATNR_PHYSICAL) continue; /* Don't display about armour */
609    
610     if (op->resist[i] != refop.resist[i]) {
611     success=1;
612     if (op->resist[i] > refop.resist[i])
613     sprintf(message, "Your resistance to %s rises to %d%%.",
614     change_resist_msg[i], op->resist[i]);
615     else
616     sprintf(message, "Your resistance to %s drops to %d%%.",
617     change_resist_msg[i], op->resist[i]);
618    
619     new_draw_info(NDI_UNIQUE|NDI_BLUE, 0, op, message);
620     }
621     }
622    
623     if(tmp->type!=EXPERIENCE && !potion_max) {
624     for (j=0; j<NUM_STATS; j++) {
625     if ((i=get_attr_value(&(tmp->stats),j))!=0) {
626     success=1;
627     DIFF_MSG(i * flag, gain_msg[j], lose_msg[j]);
628     }
629     }
630     }
631     return success;
632     }
633    
634     /*
635     * Stat draining by Vick 930307
636     * (Feeling evil, I made it work as well now. -Frank 8)
637     */
638    
639     void drain_stat(object *op) {
640     drain_specific_stat(op, RANDOM()%NUM_STATS);
641     }
642    
643     void drain_specific_stat(object *op, int deplete_stats) {
644     object *tmp;
645     archetype *at;
646    
647     at = find_archetype(ARCH_DEPLETION);
648     if (!at) {
649     LOG(llevError, "Couldn't find archetype depletion.\n");
650     return;
651     } else {
652     tmp = present_arch_in_ob(at, op);
653     if (!tmp) {
654     tmp = arch_to_object(at);
655     tmp = insert_ob_in_ob(tmp, op);
656     SET_FLAG(tmp,FLAG_APPLIED);
657     }
658     }
659    
660     new_draw_info(NDI_UNIQUE, 0, op, drain_msg[deplete_stats]);
661     change_attr_value(&tmp->stats, deplete_stats, -1);
662     fix_player(op);
663     }
664    
665     /*
666     * A value of 0 indicates timeout, otherwise change the luck of the object.
667     * via an applied bad_luck object.
668     */
669    
670     void change_luck(object *op, int value) {
671     object *tmp;
672     archetype *at;
673     int new_luck;
674    
675     at = find_archetype("luck");
676     if (!at)
677     LOG(llevError, "Couldn't find archetype luck.\n");
678     else {
679     tmp = present_arch_in_ob(at, op);
680     if (!tmp) {
681     if (!value)
682     return;
683     tmp = arch_to_object(at);
684     tmp = insert_ob_in_ob(tmp, op);
685     SET_FLAG(tmp,FLAG_APPLIED);
686     }
687     if (value) {
688     /* Limit the luck value of the bad luck object to +/-100. This
689     * (arbitrary) value prevents overflows (both in the bad luck object and
690     * in op itself).
691     */
692     new_luck = tmp->stats.luck+value;
693     if (new_luck >= -100 && new_luck <= 100) {
694     op->stats.luck+=value;
695     tmp->stats.luck = new_luck;
696     }
697     } else {
698     if (!tmp->stats.luck) {
699     return;
700     }
701     /* Randomly change the players luck. Basically, we move it
702     * back neutral (if greater>0, subtract, otherwise add)
703     */
704     if (RANDOM()%(FABS(tmp->stats.luck)) >= RANDOM()%30) {
705     int diff = tmp->stats.luck>0?-1:1;
706     op->stats.luck += diff;
707     tmp->stats.luck += diff;
708     }
709     }
710     }
711     }
712    
713     /*
714     * Subtracts stat-bonuses given by the class which the player has chosen.
715     */
716    
717     void remove_statbonus(object *op) {
718     op->stats.Str -= op->arch->clone.stats.Str;
719     op->stats.Dex -= op->arch->clone.stats.Dex;
720     op->stats.Con -= op->arch->clone.stats.Con;
721     op->stats.Wis -= op->arch->clone.stats.Wis;
722     op->stats.Pow -= op->arch->clone.stats.Pow;
723     op->stats.Cha -= op->arch->clone.stats.Cha;
724     op->stats.Int -= op->arch->clone.stats.Int;
725     op->contr->orig_stats.Str -= op->arch->clone.stats.Str;
726     op->contr->orig_stats.Dex -= op->arch->clone.stats.Dex;
727     op->contr->orig_stats.Con -= op->arch->clone.stats.Con;
728     op->contr->orig_stats.Wis -= op->arch->clone.stats.Wis;
729     op->contr->orig_stats.Pow -= op->arch->clone.stats.Pow;
730     op->contr->orig_stats.Cha -= op->arch->clone.stats.Cha;
731     op->contr->orig_stats.Int -= op->arch->clone.stats.Int;
732     }
733    
734     /*
735     * Adds stat-bonuses given by the class which the player has chosen.
736     */
737    
738     void add_statbonus(object *op) {
739     op->stats.Str += op->arch->clone.stats.Str;
740     op->stats.Dex += op->arch->clone.stats.Dex;
741     op->stats.Con += op->arch->clone.stats.Con;
742     op->stats.Wis += op->arch->clone.stats.Wis;
743     op->stats.Pow += op->arch->clone.stats.Pow;
744     op->stats.Cha += op->arch->clone.stats.Cha;
745     op->stats.Int += op->arch->clone.stats.Int;
746     op->contr->orig_stats.Str += op->arch->clone.stats.Str;
747     op->contr->orig_stats.Dex += op->arch->clone.stats.Dex;
748     op->contr->orig_stats.Con += op->arch->clone.stats.Con;
749     op->contr->orig_stats.Wis += op->arch->clone.stats.Wis;
750     op->contr->orig_stats.Pow += op->arch->clone.stats.Pow;
751     op->contr->orig_stats.Cha += op->arch->clone.stats.Cha;
752     op->contr->orig_stats.Int += op->arch->clone.stats.Int;
753     }
754    
755     /*
756     * Updates all abilities given by applied objects in the inventory
757     * of the given object. Note: This function works for both monsters
758     * and players; the "player" in the name is purely an archaic inheritance.
759     * This functions starts from base values (archetype or player object)
760     * and then adjusts them according to what the player has equipped.
761     */
762     /* July 95 - inserted stuff to handle new skills/exp system - b.t.
763     spell system split, grace points now added to system --peterm
764     */
765    
766     void fix_player(object *op) {
767     int i,j;
768     event *evt;
769     float f,max=9,added_speed=0,bonus_speed=0, sp_tmp,speed_reduce_from_disease=1;
770     int weapon_weight=0,weapon_speed=0;
771     int best_wc=0, best_ac=0, wc=0, ac=0;
772     int prot[NROFATTACKS], vuln[NROFATTACKS], potion_resist[NROFATTACKS];
773     object *grace_obj=NULL,*mana_obj=NULL,*wc_obj=NULL,*tmp;
774    
775     /* First task is to clear all the values back to their original values */
776     if(op->type==PLAYER) {
777     for(i=0;i<NUM_STATS;i++) {
778     set_attr_value(&(op->stats),i,get_attr_value(&(op->contr->orig_stats),i));
779     }
780     if (settings.spell_encumbrance == TRUE)
781     op->contr->encumbrance=0;
782    
783     op->attacktype=0;
784     op->contr->digestion = 0;
785     op->contr->gen_hp = 0;
786     op->contr->gen_sp = 0;
787     op->contr->gen_grace = 0;
788     op->contr->gen_sp_armour = 10;
789     op->contr->item_power = 0;
790    
791     /* Don't clobber all the range_ values. range_golem otherwise
792     * gets reset for no good reason, and we don't want to reset
793     * range_magic (what spell is readied). These three below
794     * well get filled in based on what the player has equipped.
795     */
796     op->contr->ranges[range_bow] = NULL;
797     op->contr->ranges[range_misc] = NULL;
798     op->contr->ranges[range_skill] = NULL;
799     }
800     memcpy(op->body_used, op->body_info, sizeof(op->body_info));
801    
802     if(op->slaying!=NULL) {
803     free_string(op->slaying);
804     op->slaying=NULL;
805     }
806     if(!QUERY_FLAG(op,FLAG_WIZ)) {
807     CLEAR_FLAG(op, FLAG_XRAYS);
808     CLEAR_FLAG(op, FLAG_MAKE_INVIS);
809     }
810    
811     CLEAR_FLAG(op,FLAG_LIFESAVE);
812     CLEAR_FLAG(op,FLAG_STEALTH);
813     CLEAR_FLAG(op,FLAG_BLIND);
814     if ( ! QUERY_FLAG (&op->arch->clone, FLAG_REFL_SPELL))
815     CLEAR_FLAG(op,FLAG_REFL_SPELL);
816     if ( ! QUERY_FLAG (&op->arch->clone, FLAG_REFL_MISSILE))
817     CLEAR_FLAG(op,FLAG_REFL_MISSILE);
818     if(!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD))
819     CLEAR_FLAG(op,FLAG_UNDEAD);
820     if ( ! QUERY_FLAG (&op->arch->clone, FLAG_SEE_IN_DARK))
821     CLEAR_FLAG(op,FLAG_SEE_IN_DARK);
822    
823     op->path_attuned=op->arch->clone.path_attuned;
824     op->path_repelled=op->arch->clone.path_repelled;
825     op->path_denied=op->arch->clone.path_denied;
826     op->glow_radius=op->arch->clone.glow_radius;
827     op->move_type = op->arch->clone.move_type;
828     op->chosen_skill = NULL;
829    
830     /* initializing resistances from the values in player/monster's
831     * archetype clone
832     */
833     memcpy(&op->resist, &op->arch->clone.resist, sizeof(op->resist));
834    
835     for (i=0;i<NROFATTACKS;i++) {
836     if (op->resist[i] > 0)
837     prot[i]= op->resist[i], vuln[i]=0;
838     else
839     vuln[i]= -(op->resist[i]), prot[i]=0;
840     potion_resist[i]=0;
841     }
842    
843     wc=op->arch->clone.stats.wc;
844     op->stats.dam=op->arch->clone.stats.dam;
845    
846     /* for players which cannot use armour, they gain AC -1 per 3 levels,
847     * plus a small amount of physical resist, those poor suckers. ;)
848     * the fact that maxlevel is factored in could be considered sort of bogus -
849     * we should probably give them some bonus and cap it off - otherwise,
850     * basically, if a server updates its max level, these playes may find
851     * that their protection from physical goes down
852     */
853     if(!QUERY_FLAG(op,FLAG_USE_ARMOUR) && op->type==PLAYER) {
854     ac=MAX(-10,op->arch->clone.stats.ac - op->level/3);
855     prot[ATNR_PHYSICAL] += ((100-prot[AT_PHYSICAL])*(80*op->level/settings.max_level))/100;
856     }
857     else
858     ac=op->arch->clone.stats.ac;
859    
860     op->stats.luck=op->arch->clone.stats.luck;
861     op->speed = op->arch->clone.speed;
862    
863     /* OK - we've reset most all the objects attributes to sane values.
864     * now go through and make adjustments for what the player has equipped.
865     */
866    
867     for(tmp=op->inv;tmp!=NULL;tmp=tmp->below) {
868     /* See note in map.c:update_position about making this additive
869     * since light sources are never applied, need to put check here.
870     */
871     if (tmp->glow_radius > op->glow_radius) op->glow_radius=tmp->glow_radius;
872    
873     /* This happens because apply_potion calls change_abil with the potion
874     * applied so we can tell the player what chagned. But change_abil
875     * then calls this function.
876     */
877     if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == POTION) {
878     continue;
879     }
880    
881     /* For some things, we don't care what is equipped */
882     if (tmp->type == SKILL) {
883     /* Want to take the highest skill here. */
884     if (IS_MANA_SKILL(tmp->subtype)) {
885     if (!mana_obj) mana_obj=tmp;
886     else if (tmp->level > mana_obj->level) mana_obj = tmp;
887     }
888     if (IS_GRACE_SKILL(tmp->subtype)) {
889     if (!grace_obj) grace_obj=tmp;
890     else if (tmp->level > grace_obj->level) grace_obj = tmp;
891     }
892     }
893    
894     /* Container objects are not meant to adjust a players, but other applied
895     * objects need to make adjustments.
896     * This block should handle all player specific changes
897     * The check for Praying is a bit of a hack - god given bonuses are put
898     * in the praying skill, and the player should always get those.
899     * It also means we need to put in additional checks for applied below,
900     * because the skill shouldn't count against body positions being used
901     * up, etc.
902     */
903     if ((QUERY_FLAG(tmp,FLAG_APPLIED) && tmp->type!=CONTAINER && tmp->type!=CLOSE_CON) ||
904     (tmp->type == SKILL && tmp->subtype == SK_PRAYING)) {
905     if(op->type==PLAYER) {
906     if (tmp->type == BOW)
907     op->contr->ranges[range_bow] = tmp;
908    
909     if (tmp->type == WAND || tmp->type == ROD || tmp->type==HORN)
910     op->contr->ranges[range_misc] = tmp;
911    
912     for(i=0;i<NUM_STATS;i++)
913     change_attr_value(&(op->stats),i,get_attr_value(&(tmp->stats),i));
914    
915     /* these are the items that currently can change digestion, regeneration,
916     * spell point recovery and mana point recovery. Seems sort of an arbitary
917     * list, but other items store other info into stats array.
918     */
919     if ((tmp->type == EXPERIENCE) || (tmp->type == WEAPON) ||
920     (tmp->type == ARMOUR) || (tmp->type == HELMET) ||
921     (tmp->type == SHIELD) || (tmp->type == RING) ||
922     (tmp->type == BOOTS) || (tmp->type == GLOVES) ||
923     (tmp->type == AMULET ) || (tmp->type == GIRDLE) ||
924     (tmp->type == BRACERS ) || (tmp->type == CLOAK) ||
925     (tmp->type == DISEASE) || (tmp->type == FORCE) ||
926     (tmp->type == SKILL)) {
927     op->contr->digestion += tmp->stats.food;
928     op->contr->gen_hp += tmp->stats.hp;
929     op->contr->gen_sp += tmp->stats.sp;
930     op->contr->gen_grace += tmp->stats.grace;
931     op->contr->gen_sp_armour+= tmp->gen_sp_armour;
932     op->contr->item_power += tmp->item_power;
933     }
934     } /* if this is a player */
935    
936     /* Update slots used for items */
937     if (QUERY_FLAG(tmp,FLAG_APPLIED)) {
938     for (i=0; i<NUM_BODY_LOCATIONS; i++)
939     op->body_used[i] += tmp->body_info[i];
940     }
941    
942     if(tmp->type==SYMPTOM) {
943     speed_reduce_from_disease = tmp->last_sp / 100.0;
944     if(speed_reduce_from_disease ==0) speed_reduce_from_disease = 1;
945     }
946    
947     /* Pos. and neg. protections are counted seperate (-> pro/vuln).
948     * (Negative protections are calculated extactly like positive.)
949     * Resistance from potions are treated special as well. If there's
950     * more than one potion-effect, the bigger prot.-value is taken.
951     */
952     if (tmp->type != POTION) {
953     for (i=0; i<NROFATTACKS; i++) {
954     /* Potential for cursed potions, in which case we just can use
955     * a straight MAX, as potion_resist is initialized to zero.
956     */
957     if (tmp->type==POTION_EFFECT) {
958     if (potion_resist[i])
959     potion_resist[i] = MAX(potion_resist[i], tmp->resist[i]);
960     else
961     potion_resist[i] = tmp->resist[i];
962     }
963     else if (tmp->resist[i] > 0)
964     prot[i] += ((100-prot[i])*tmp->resist[i])/100;
965     else if (tmp->resist[i] < 0)
966     vuln[i] += ((100-vuln[i])*(-tmp->resist[i]))/100;
967     }
968     }
969    
970     /* There may be other things that should not adjust the attacktype */
971     if (tmp->type!=BOW && tmp->type != SYMPTOM)
972     op->attacktype|=tmp->attacktype;
973    
974     op->path_attuned|=tmp->path_attuned;
975     op->path_repelled|=tmp->path_repelled;
976     op->path_denied|=tmp->path_denied;
977     op->stats.luck+=tmp->stats.luck;
978     op->move_type |= tmp->move_type;
979    
980     if(QUERY_FLAG(tmp,FLAG_LIFESAVE)) SET_FLAG(op,FLAG_LIFESAVE);
981     if(QUERY_FLAG(tmp,FLAG_REFL_SPELL)) SET_FLAG(op,FLAG_REFL_SPELL);
982     if(QUERY_FLAG(tmp,FLAG_REFL_MISSILE)) SET_FLAG(op,FLAG_REFL_MISSILE);
983     if(QUERY_FLAG(tmp,FLAG_STEALTH)) SET_FLAG(op,FLAG_STEALTH);
984     if(QUERY_FLAG(tmp,FLAG_XRAYS)) SET_FLAG(op,FLAG_XRAYS);
985     if(QUERY_FLAG(tmp,FLAG_BLIND)) SET_FLAG(op,FLAG_BLIND);
986     if(QUERY_FLAG(tmp,FLAG_SEE_IN_DARK)) SET_FLAG(op,FLAG_SEE_IN_DARK);
987    
988     if(QUERY_FLAG(tmp,FLAG_UNDEAD)&&!QUERY_FLAG(&op->arch->clone,FLAG_UNDEAD))
989     SET_FLAG(op,FLAG_UNDEAD);
990    
991     if(QUERY_FLAG(tmp,FLAG_MAKE_INVIS)) {
992     SET_FLAG(op,FLAG_MAKE_INVIS);
993     op->invisible=1;
994     }
995    
996     if(tmp->stats.exp && tmp->type!=SKILL) {
997     if(tmp->stats.exp > 0) {
998     added_speed+=(float)tmp->stats.exp/3.0;
999     bonus_speed+=1.0+(float)tmp->stats.exp/3.0;
1000     } else
1001     added_speed+=(float)tmp->stats.exp;
1002     }
1003    
1004     switch(tmp->type) {
1005     /* skills modifying the character -b.t. */
1006     /* for all skills and skill granting objects */
1007     case SKILL:
1008     if (!QUERY_FLAG(tmp,FLAG_APPLIED)) break;
1009    
1010     if (IS_COMBAT_SKILL(tmp->subtype)) wc_obj=tmp;
1011    
1012     if (op->chosen_skill) {
1013     LOG(llevDebug, "fix_player, op %s has multiple skills applied\n", op->name);
1014     }
1015     op->chosen_skill = tmp;
1016     if(tmp->stats.dam>0) { /* skill is a 'weapon' */
1017     if(!QUERY_FLAG(op,FLAG_READY_WEAPON))
1018     weapon_speed = (int) WEAPON_SPEED(tmp);
1019     if(weapon_speed<0) weapon_speed = 0;
1020     weapon_weight=tmp->weight;
1021     op->stats.dam+=tmp->stats.dam*(1 + (op->chosen_skill->level/9));
1022     if(tmp->magic) op->stats.dam += tmp->magic;
1023     }
1024     if(tmp->stats.wc)
1025     wc-=(tmp->stats.wc+tmp->magic);
1026    
1027     if(tmp->slaying!=NULL) {
1028     if (op->slaying != NULL)
1029     free_string (op->slaying);
1030     add_refcount(op->slaying = tmp->slaying);
1031     }
1032    
1033     if(tmp->stats.ac)
1034     ac-=(tmp->stats.ac+tmp->magic);
1035     if(settings.spell_encumbrance == TRUE && op->type==PLAYER)
1036     op->contr->encumbrance+=(int)3*tmp->weight/1000;
1037     if (op->type == PLAYER)
1038     op->contr->ranges[range_skill] = op;
1039     break;
1040    
1041     case SKILL_TOOL:
1042     if (op->chosen_skill) {
1043     LOG(llevDebug, "fix_player, op %s has multiple skills applied\n", op->name);
1044     }
1045     op->chosen_skill = tmp;
1046     if (op->type == PLAYER)
1047     op->contr->ranges[range_skill] = op;
1048     break;
1049    
1050     case SHIELD:
1051     if(settings.spell_encumbrance == TRUE && op->type==PLAYER)
1052     op->contr->encumbrance+=(int)tmp->weight/2000;
1053     case RING:
1054     case AMULET:
1055     case GIRDLE:
1056     case HELMET:
1057     case BOOTS:
1058     case GLOVES:
1059     case CLOAK:
1060     if(tmp->stats.wc)
1061     wc-=(tmp->stats.wc+tmp->magic);
1062     if(tmp->stats.dam)
1063     op->stats.dam+=(tmp->stats.dam+tmp->magic);
1064     if(tmp->stats.ac)
1065     ac-=(tmp->stats.ac+tmp->magic);
1066     break;
1067    
1068     case WEAPON:
1069     wc-=(tmp->stats.wc+tmp->magic);
1070     if(tmp->stats.ac&&tmp->stats.ac+tmp->magic>0)
1071     ac-=tmp->stats.ac+tmp->magic;
1072     op->stats.dam+=(tmp->stats.dam+tmp->magic);
1073     weapon_weight=tmp->weight;
1074     weapon_speed=((int)WEAPON_SPEED(tmp)*2-tmp->magic)/2;
1075     if(weapon_speed<0) weapon_speed=0;
1076     if(tmp->slaying!=NULL) {
1077     if (op->slaying != NULL)
1078     free_string (op->slaying);
1079     add_refcount(op->slaying = tmp->slaying);
1080     }
1081     /* If there is desire that two handed weapons should do
1082     * extra strength damage, this is where the code should
1083     * go.
1084     */
1085     evt = find_event(tmp,EVENT_ATTACK);
1086     if (evt != NULL) {
1087     if (op->current_weapon_script)
1088     free_string(op->current_weapon_script);
1089     op->current_weapon_script=add_string(query_name(tmp));
1090     }
1091     op->current_weapon = tmp;
1092     if(settings.spell_encumbrance == TRUE && op->type==PLAYER)
1093     op->contr->encumbrance+=(int)3*tmp->weight/1000;
1094     break;
1095    
1096     case ARMOUR: /* Only the best of these three are used: */
1097     if(settings.spell_encumbrance == TRUE && op->type==PLAYER)
1098     op->contr->encumbrance+=(int)tmp->weight/1000;
1099    
1100     case BRACERS:
1101     case FORCE:
1102     if(tmp->stats.wc) {
1103     if(best_wc<tmp->stats.wc+tmp->magic) {
1104     wc+=best_wc;
1105     best_wc=tmp->stats.wc+tmp->magic;
1106     } else
1107     wc+=tmp->stats.wc+tmp->magic;
1108     }
1109     if(tmp->stats.ac) {
1110     if(best_ac<tmp->stats.ac+tmp->magic) {
1111     ac+=best_ac; /* Remove last bonus */
1112     best_ac=tmp->stats.ac+tmp->magic;
1113     }
1114     else /* To nullify the below effect */
1115     ac+=tmp->stats.ac+tmp->magic;
1116     }
1117     if(tmp->stats.wc) wc-=(tmp->stats.wc+tmp->magic);
1118     if(tmp->stats.ac) ac-=(tmp->stats.ac+tmp->magic);
1119     if(ARMOUR_SPEED(tmp)&&ARMOUR_SPEED(tmp)/10.0<max)
1120     max=ARMOUR_SPEED(tmp)/10.0;
1121     break;
1122     } /* switch tmp->type */
1123     } /* item is equipped */
1124     } /* for loop of items */
1125    
1126     /* We've gone through all the objects the player has equipped. For many things, we
1127     * have generated intermediate values which we now need to assign.
1128     */
1129    
1130     /* 'total resistance = total protections - total vulnerabilities'.
1131     * If there is an uncursed potion in effect, granting more protection
1132     * than that, we take: 'total resistance = resistance from potion'.
1133     * If there is a cursed (and no uncursed) potion in effect, we take
1134     * 'total resistance = vulnerability from cursed potion'.
1135     */
1136     for (i=0; i<NROFATTACKS; i++) {
1137     op->resist[i] = prot[i] - vuln[i];
1138     if (potion_resist[i] && ((potion_resist[i] > op->resist[i]) ||
1139     (potion_resist[i] < 0)))
1140     op->resist[i] = potion_resist[i];
1141     }
1142    
1143     /* Figure out the players sp/mana/hp totals. */
1144     if(op->type==PLAYER) {
1145     int pl_level;
1146    
1147     check_stat_bounds(&(op->stats));
1148     pl_level=op->level;
1149    
1150     if(pl_level<1) pl_level=1; /* safety, we should always get 1 levels worth of hp! */
1151    
1152     /* You basically get half a con bonus/level. But we do take into account rounding,
1153     * so if your bonus is 7, you still get 7 worth of bonus every 2 levels.
1154     */
1155     for(i=1,op->stats.maxhp=0;i<=pl_level&&i<=10;i++) {
1156     j=op->contr->levhp[i]+con_bonus[op->stats.Con]/2;
1157     if(i%2 && con_bonus[op->stats.Con]%2) {
1158     if (con_bonus[op->stats.Con]>0)
1159     j++;
1160     else
1161     j--;
1162     }
1163     op->stats.maxhp+=j>1?j:1; /* always get at least 1 hp/level */
1164     }
1165    
1166     for(i=11;i<=op->level;i++)
1167     op->stats.maxhp+=2;
1168    
1169     if(op->stats.hp>op->stats.maxhp)
1170     op->stats.hp=op->stats.maxhp;
1171    
1172     /* Sp gain is controlled by the level of the player's
1173     * relevant experience object (mana_obj, see above)
1174     */
1175     /* following happen when skills system is not used */
1176     if(!mana_obj) mana_obj = op;
1177     if(!grace_obj) grace_obj = op;
1178     /* set maxsp */
1179     if(!mana_obj || !mana_obj->level || op->type!=PLAYER) mana_obj = op;
1180    
1181     if (mana_obj == op && op->type == PLAYER) {
1182     op->stats.maxsp = 1;
1183     } else {
1184     sp_tmp=0.0;
1185     for(i=1;i<=mana_obj->level&&i<=10;i++) {
1186     float stmp;
1187    
1188     /* Got some extra bonus at first level */
1189     if(i<2) {
1190     stmp = op->contr->levsp[i] +((2.0 * (float)sp_bonus[op->stats.Pow] +
1191     (float)sp_bonus[op->stats.Int])/6.0);
1192     } else {
1193     stmp=(float)op->contr->levsp[i]
1194     +(2.0 * (float)sp_bonus[op->stats.Pow] +
1195     (float)sp_bonus[op->stats.Int])/12.0;
1196     }
1197     if (stmp<1.0) stmp=1.0;
1198     sp_tmp+=stmp;
1199     }
1200     op->stats.maxsp=(int)sp_tmp;
1201    
1202     for(i=11;i<=mana_obj->level;i++)
1203     op->stats.maxsp+=2;
1204     }
1205     /* Characters can get their sp supercharged via rune of transferrance */
1206     if(op->stats.sp>op->stats.maxsp*2)
1207     op->stats.sp=op->stats.maxsp*2;
1208    
1209     /* set maxgrace, notice 3-4 lines below it depends on both Wis and Pow */
1210     if(!grace_obj || !grace_obj->level || op->type!=PLAYER) grace_obj = op;
1211    
1212     if (grace_obj == op && op->type == PLAYER) {
1213     op->stats.maxgrace = 1;
1214     } else {
1215     /* store grace in a float - this way, the divisions below don't create
1216     * big jumps when you go from level to level - with int's, it then
1217     * becomes big jumps when the sums of the bonuses jump to the next
1218     * step of 8 - with floats, even fractional ones are useful.
1219     */
1220     sp_tmp=0.0;
1221     for(i=1,op->stats.maxgrace=0;i<=grace_obj->level&&i<=10;i++) {
1222     float grace_tmp=0.0;
1223    
1224     /* Got some extra bonus at first level */
1225     if(i<2) {
1226     grace_tmp = op->contr->levgrace[i]+(((float)grace_bonus[op->stats.Pow] +
1227     2.0 * (float)grace_bonus[op->stats.Wis])/6.0);
1228     } else {
1229     grace_tmp=(float)op->contr->levgrace[i]
1230     +((float)grace_bonus[op->stats.Pow] +
1231     2.0 * (float)grace_bonus[op->stats.Wis])/12.0;
1232     }
1233     if (grace_tmp<1.0) grace_tmp=1.0;
1234     sp_tmp+=grace_tmp;
1235     }
1236     op->stats.maxgrace=(int)sp_tmp;
1237    
1238     /* two grace points per level after 11 */
1239     for(i=11;i<=grace_obj->level;i++)
1240     op->stats.maxgrace+=2;
1241     }
1242     /* No limit on grace vs maxgrace */
1243    
1244     if(op->contr->braced) {
1245     ac+=2;
1246     wc+=4;
1247     }
1248     else
1249     ac-=dex_bonus[op->stats.Dex];
1250    
1251     /* In new exp/skills system, wc bonuses are related to
1252     * the players level in a relevant exp object (wc_obj)
1253     * not the general player level -b.t.
1254     * I changed this slightly so that wc bonuses are better
1255     * than before. This is to balance out the fact that
1256     * the player no longer gets a personal weapon w/ 1
1257     * improvement every level, now its fighterlevel/5. So
1258     * we give the player a bonus here in wc and dam
1259     * to make up for the change. Note that I left the
1260     * monster bonus the same as before. -b.t.
1261     */
1262    
1263     if(op->type==PLAYER && wc_obj && wc_obj->level>1) {
1264     wc-=(wc_obj->level+thaco_bonus[op->stats.Str]);
1265     for(i=1;i<wc_obj->level;i++) {
1266     /* addtional wc every 6 levels */
1267     if(!(i%6)) wc--;
1268     /* addtional dam every 4 levels. */
1269     if(!(i%4) && (dam_bonus[op->stats.Str]>=0))
1270     op->stats.dam+=(1+(dam_bonus[op->stats.Str]/5));
1271     }
1272     } else
1273     wc-=(op->level+thaco_bonus[op->stats.Str]);
1274    
1275     op->stats.dam+=dam_bonus[op->stats.Str];
1276    
1277     if(op->stats.dam<1)
1278     op->stats.dam=1;
1279    
1280     op->speed=1.0+speed_bonus[op->stats.Dex];
1281     if (settings.search_items && op->contr->search_str[0])
1282     op->speed -= 1;
1283     if (op->attacktype==0)
1284     op->attacktype=op->arch->clone.attacktype;
1285    
1286     } /* End if player */
1287    
1288     if(added_speed>=0)
1289     op->speed+=added_speed/10.0;
1290     else /* Something wrong here...: */
1291     op->speed /= (float)(1.0-added_speed);
1292    
1293     /* Max is determined by armour */
1294     if(op->speed>max)
1295     op->speed=max;
1296    
1297     if(op->type == PLAYER) {
1298     /* f is a number the represents the number of kg above (positive num)
1299     * or below (negative number) that the player is carrying. If above
1300     * weight limit, then player suffers a speed reduction based on how
1301     * much above he is, and what is max carry is
1302     */
1303     f=(op->carrying/1000)-max_carry[op->stats.Str];
1304     if(f>0) op->speed=op->speed/(1.0+f/max_carry[op->stats.Str]);
1305     }
1306    
1307     op->speed+=bonus_speed/10.0; /* Not affected by limits */
1308    
1309     /* Put a lower limit on speed. Note with this speed, you move once every
1310     * 100 ticks or so. This amounts to once every 12 seconds of realtime.
1311     */
1312     op->speed = op->speed * speed_reduce_from_disease;
1313    
1314     if (op->speed<0.01 && op->type==PLAYER) op->speed=0.01;
1315    
1316     if(op->type == PLAYER) {
1317     float M,W,s,D,K,S,M2;
1318    
1319     /* (This formula was made by vidarl@ifi.uio.no)
1320     * Note that we never used these values again - basically
1321     * all of these could be subbed into one big equation, but
1322     * that would just be a real pain to read.
1323     */
1324     M=(max_carry[op->stats.Str]-121)/121.0;
1325     M2=max_carry[op->stats.Str]/100.0;
1326     W=weapon_weight/20000.0;
1327     s=2-weapon_speed/10.0;
1328     D=(op->stats.Dex-14)/14.0;
1329     K=1 + M/3.0 - W/(3*M2) + op->speed/5.0 + D/2.0;
1330     K*=(4+op->level)/(float)(6+op->level)*1.2;
1331     if(K<=0) K=0.01;
1332     S=op->speed/(K*s);
1333     op->contr->weapon_sp=S;
1334     }
1335     /* I want to limit the power of small monsters with big weapons: */
1336     if(op->type!=PLAYER&&op->arch!=NULL&&
1337     op->stats.dam>op->arch->clone.stats.dam*3)
1338     op->stats.dam=op->arch->clone.stats.dam*3;
1339    
1340     /* Prevent overflows of wc - best you can get is ABS(120) - this
1341     * should be more than enough - remember, AC is also in 8 bits,
1342     * so its value is the same.
1343     */
1344     if (wc>120) wc=120;
1345     else if (wc<-120) wc=-120;
1346     op->stats.wc=wc;
1347    
1348     if (ac>120) ac=120;
1349     else if (ac<-120) ac=-120;
1350     op->stats.ac=ac;
1351    
1352     /* if for some reason the creature doesn't have any move type,
1353     * give them walking as a default.
1354     * The second case is a special case - to more closely mimic the
1355     * old behaviour - if your flying, your not walking - just
1356     * one or the other.
1357     */
1358     if (op->move_type == 0) op->move_type = MOVE_WALK;
1359     else if (op->move_type & (MOVE_FLY_LOW | MOVE_FLY_HIGH)) op->move_type &= ~MOVE_WALK;
1360    
1361     update_ob_speed(op);
1362    
1363     /* It is quite possible that a player's spell costing might have changed,
1364     * so we will check that now.
1365     */
1366     if (op->type == PLAYER) esrv_update_spells(op->contr);
1367     }
1368    
1369     /*
1370     * Returns true if the given player is a legal class.
1371     * The function to add and remove class-bonuses to the stats doesn't
1372     * check if the stat becomes negative, thus this function
1373     * merely checks that all stats are 1 or more, and returns
1374     * false otherwise.
1375     */
1376    
1377     int allowed_class(object *op) {
1378     return op->stats.Dex>0&&op->stats.Str>0&&op->stats.Con>0&&
1379     op->stats.Int>0&&op->stats.Wis>0&&op->stats.Pow>0&&
1380     op->stats.Cha>0;
1381     }
1382    
1383     /*
1384     * set the new dragon name after gaining levels or
1385     * changing ability focus (later this can be extended to
1386     * eventually change the player's face and animation)
1387     *
1388     * Note that the title is written to 'own_title' in the
1389     * player struct. This should be changed to 'ext_title'
1390     * as soon as clients support this!
1391     * Please, anyone, write support for 'ext_title'.
1392     */
1393     void set_dragon_name(object *pl, object *abil, object *skin) {
1394     int atnr=-1; /* attacknumber of highest level */
1395     int level=0; /* highest level */
1396     int i;
1397    
1398     /* Perhaps do something more clever? */
1399     if (!abil || !skin) return;
1400    
1401     /* first, look for the highest level */
1402     for(i=0; i<NROFATTACKS; i++) {
1403     if (atnr_is_dragon_enabled(i) &&
1404     (atnr==-1 || abil->resist[i] > abil->resist[atnr])) {
1405     level = abil->resist[i];
1406     atnr = i;
1407     }
1408     }
1409    
1410     /* now if there are equals at highest level, pick the one with focus,
1411     or else at random */
1412     if (atnr_is_dragon_enabled(abil->stats.exp) &&
1413     abil->resist[abil->stats.exp] >= level)
1414     atnr = abil->stats.exp;
1415    
1416     level = (int)(level/5.);
1417    
1418     /* now set the new title */
1419     if (pl->contr != NULL) {
1420     if(level == 0)
1421     sprintf(pl->contr->title, "%s hatchling", attacks[atnr]);
1422     else if (level == 1)
1423     sprintf(pl->contr->title, "%s wyrm", attacks[atnr]);
1424     else if (level == 2)
1425     sprintf(pl->contr->title, "%s wyvern", attacks[atnr]);
1426     else if (level == 3)
1427     sprintf(pl->contr->title, "%s dragon", attacks[atnr]);
1428     else {
1429     /* special titles for extra high resistance! */
1430     if (skin->resist[atnr] > 80)
1431     sprintf(pl->contr->title, "legendary %s dragon", attacks[atnr]);
1432     else if (skin->resist[atnr] > 50)
1433     sprintf(pl->contr->title, "ancient %s dragon", attacks[atnr]);
1434     else
1435     sprintf(pl->contr->title, "big %s dragon", attacks[atnr]);
1436     }
1437     }
1438    
1439     strcpy(pl->contr->own_title, "");
1440     }
1441    
1442     /*
1443     * This function is called when a dragon-player gains
1444     * an overall level. Here, the dragon might gain new abilities
1445     * or change the ability-focus.
1446     */
1447     void dragon_level_gain(object *who) {
1448     object *abil = NULL; /* pointer to dragon ability force*/
1449     object *skin = NULL; /* pointer to dragon skin force*/
1450     object *tmp = NULL; /* tmp. object */
1451     char buf[MAX_BUF]; /* tmp. string buffer */
1452    
1453     /* now grab the 'dragon_ability'-forces from the player's inventory */
1454     for (tmp=who->inv; tmp!=NULL; tmp=tmp->below) {
1455     if (tmp->type == FORCE) {
1456     if (strcmp(tmp->arch->name, "dragon_ability_force")==0)
1457     abil = tmp;
1458     if (strcmp(tmp->arch->name, "dragon_skin_force")==0)
1459     skin = tmp;
1460     }
1461     }
1462     /* if the force is missing -> bail out */
1463     if (abil == NULL) return;
1464    
1465     /* The ability_force keeps track of maximum level ever achieved.
1466     * New abilties can only be gained by surpassing this max level
1467     */
1468     if (who->level > abil->level) {
1469     /* increase our focused ability */
1470     abil->resist[abil->stats.exp]++;
1471    
1472    
1473     if (abil->resist[abil->stats.exp]>0 && abil->resist[abil->stats.exp]%5 == 0) {
1474     /* time to hand out a new ability-gift */
1475     dragon_ability_gain(who, (int)abil->stats.exp,
1476     (int)((1+abil->resist[abil->stats.exp])/5.));
1477     }
1478    
1479     if (abil->last_eat > 0 && atnr_is_dragon_enabled(abil->last_eat)) {
1480     /* apply new ability focus */
1481     sprintf(buf, "Your metabolism now focuses on %s!",
1482     change_resist_msg[abil->last_eat]);
1483     new_draw_info(NDI_UNIQUE|NDI_BLUE, 0, who, buf);
1484    
1485     abil->stats.exp = abil->last_eat;
1486     abil->last_eat = 0;
1487     }
1488    
1489     abil->level = who->level;
1490     }
1491    
1492     /* last but not least, set the new title for the dragon */
1493     set_dragon_name(who, abil, skin);
1494     }
1495    
1496     /* Handy function - given the skill name skill_name, we find the skill
1497     * archetype/object, set appropriate values, and insert it into
1498     * the object (op) that is passed.
1499     * We return the skill - this makes it easier for calling functions that
1500     * want to do something with it immediately.
1501     */
1502     object *give_skill_by_name(object *op, const char *skill_name)
1503     {
1504     object *skill_obj;
1505    
1506     skill_obj = get_archetype_by_skill_name(skill_name, SKILL);
1507     if (!skill_obj) {
1508     LOG(llevError, "add_player_exp: couldn't find skill %s\n", skill_name);
1509     return NULL;
1510     }
1511     /* clear the flag - exp goes into this bucket, but player
1512     * still doesn't know it.
1513     */
1514     CLEAR_FLAG(skill_obj, FLAG_CAN_USE_SKILL);
1515     skill_obj->stats.exp = 0;
1516     skill_obj->level = 1;
1517     insert_ob_in_ob(skill_obj, op);
1518     if (op->contr) {
1519     op->contr->last_skill_ob[skill_obj->subtype] = skill_obj;
1520     op->contr->last_skill_exp[skill_obj->subtype] = -1;
1521     }
1522     return skill_obj;
1523     }
1524    
1525    
1526     /* player_lvl_adj() - for the new exp system. we are concerned with
1527     * whether the player gets more hp, sp and new levels.
1528     * Note this this function should only be called for players. Monstes
1529     * don't really gain levels
1530     * who is the player, op is what we are checking to gain the level
1531     * (eg, skill)
1532     */
1533     void player_lvl_adj(object *who, object *op) {
1534     char buf[MAX_BUF];
1535    
1536     if(!op) /* when rolling stats */
1537     op = who;
1538    
1539     if(op->level < settings.max_level && op->stats.exp >= level_exp(op->level+1,who->expmul)) {
1540     op->level++;
1541    
1542     if (op != NULL && op == who && op->stats.exp > 1 && is_dragon_pl(who))
1543     dragon_level_gain(who);
1544    
1545     /* Only roll these if it is the player (who) that gained the level */
1546     if(who && op==who && (who->level < 11) && who->type==PLAYER) {
1547     who->contr->levhp[who->level] = die_roll(2, 4, who, PREFER_HIGH)+1;
1548     who->contr->levsp[who->level] = die_roll(2, 3, who, PREFER_HIGH);
1549     who->contr->levgrace[who->level]=die_roll(2, 2, who, PREFER_HIGH)-1;
1550     }
1551    
1552     if(who) fix_player(who);
1553     if(op->level>1) {
1554     if (op->type!=PLAYER)
1555     sprintf(buf,"You are now level %d in the %s skill.",op->level,op->name);
1556     else
1557     sprintf(buf,"You are now level %d.",op->level);
1558     if(who) new_draw_info(NDI_UNIQUE|NDI_RED, 0, who,buf);
1559     }
1560     player_lvl_adj(who,op); /* To increase more levels */
1561     } else if (op->level>1 && op->stats.exp<level_exp(op->level,who->expmul)) {
1562     op->level--;
1563     if(who) fix_player(who);
1564     if(op->type!=PLAYER) {
1565     sprintf(buf,"You are now level %d in the %s skill.",op->level,op->name);
1566     if(who) new_draw_info(NDI_UNIQUE|NDI_RED, 0, who,buf);
1567     }
1568     player_lvl_adj(who,op); /* To decrease more levels */
1569     }
1570     /* check if the spell data has changed */
1571     esrv_update_spells(who->contr);
1572     }
1573    
1574     /*
1575     * Returns how much experience is needed for a player to become
1576     * the given level. level should really never exceed max_level
1577     */
1578    
1579     sint64 level_exp(int level,double expmul) {
1580     if (level > settings.max_level)
1581     return expmul * levels[settings.max_level];
1582     return expmul * levels[level];
1583     }
1584    
1585     /*
1586     * Ensure that the permanent experience requirements in an exp object are met.
1587     * This really just checks 'op to make sure the perm_exp value is within
1588     * proper range. Note that the checking of what is passed through
1589     * has been reduced. Since there is now a proper field for perm_exp,
1590     * this can now work on a much larger set of objects.
1591     */
1592     void calc_perm_exp(object *op)
1593     {
1594     int p_exp_min;
1595    
1596     /* Ensure that our permanent experience minimum is met. */
1597     p_exp_min = (int)(PERM_EXP_MINIMUM_RATIO * (float)(op->stats.exp));
1598    
1599     if (op->perm_exp < p_exp_min)
1600     op->perm_exp = p_exp_min;
1601    
1602     /* Cap permanent experience. */
1603     if (op->perm_exp < 0)
1604     op->perm_exp = 0;
1605     else if (op->perm_exp > MAX_EXPERIENCE)
1606     op->perm_exp = MAX_EXPERIENCE;
1607     }
1608    
1609    
1610     /* Add experience to a player - exp should only be positive.
1611     * Updates permanent exp for the skill we are adding to.
1612     * skill_name is the skill to add exp to. Skill name can be
1613     * NULL, in which case exp increases the players general
1614     * total, but not any particular skill.
1615     * flag is what to do if the player doesn't have the skill:
1616     */
1617    
1618     static void add_player_exp(object *op, sint64 exp, const char *skill_name, int flag)
1619     {
1620     object *skill_obj=NULL;
1621     sint64 limit, exp_to_add;
1622     int i;
1623    
1624     /* prevents some forms of abuse. */
1625     if(op->contr->braced) exp=exp/5;
1626    
1627     /* Try to find the matching skill.
1628     * We do a shortcut/time saving mechanism first - see if it matches
1629     * chosen_skill. This means we don't need to search through
1630     * the players inventory.
1631     */
1632     if (skill_name) {
1633     if (op->chosen_skill && op->chosen_skill->type == SKILL &&
1634     !strcmp(skill_name, op->chosen_skill->skill))
1635     skill_obj = op->chosen_skill;
1636     else {
1637     for (i=0; i<NUM_SKILLS; i++)
1638     if (op->contr->last_skill_ob[i] &&
1639     !strcmp(op->contr->last_skill_ob[i]->skill, skill_name)) {
1640     skill_obj = op->contr->last_skill_ob[i];
1641     break;
1642     }
1643    
1644     /* Player doesn't have the skill. Check to see what to do, and give
1645     * it to the player if necessary
1646     */
1647     if (!skill_obj) {
1648     if (flag == SK_EXP_NONE) return;
1649     else if (flag == SK_EXP_ADD_SKILL)
1650     give_skill_by_name(op, skill_name);
1651     }
1652     }
1653     }
1654    
1655     /* Basically, you can never gain more experience in one shot
1656     * than half what you need to gain for next level.
1657     */
1658     exp_to_add = exp;
1659     limit=(levels[op->level+1]-levels[op->level])/2;
1660     if (exp_to_add > limit) exp_to_add=limit;
1661    
1662     ADD_EXP(op->stats.exp, (float) exp_to_add * (skill_obj? skill_obj->expmul:1));
1663     if (settings.use_permanent_experience) {
1664     ADD_EXP(op->perm_exp, (float) exp_to_add * PERM_EXP_GAIN_RATIO * (skill_obj? skill_obj->expmul:1));
1665     calc_perm_exp(op);
1666     }
1667    
1668     player_lvl_adj(op,NULL);
1669     if (skill_obj) {
1670     exp_to_add = exp;
1671     limit=(levels[skill_obj->level+1]-levels[skill_obj->level])/2;
1672     if (exp_to_add > limit) exp_to_add=limit;
1673     ADD_EXP(skill_obj->stats.exp, exp_to_add);
1674     if (settings.use_permanent_experience) {
1675     skill_obj->perm_exp += exp_to_add * PERM_EXP_GAIN_RATIO;
1676     calc_perm_exp(skill_obj);
1677     }
1678     player_lvl_adj(op,skill_obj);
1679     }
1680     }
1681    
1682     /* This function checks to make sure that object 'op' can
1683     * lost 'exp' experience. It returns the amount of exp
1684     * object 'op' can in fact lose - it basically makes
1685     * adjustments based on permanent exp and the like.
1686     * This function should always be used for losing experience -
1687     * the 'exp' value passed should be positive - this is the
1688     * amount that should get subtract from the player.
1689     */
1690     sint64 check_exp_loss(object *op, sint64 exp)
1691     {
1692     sint64 del_exp;
1693    
1694     if (exp > op->stats.exp) exp = op->stats.exp;
1695     if (settings.use_permanent_experience) {
1696     del_exp = (op->stats.exp - op->perm_exp) * PERM_EXP_MAX_LOSS_RATIO;
1697     if (del_exp < 0) del_exp = 0;
1698     if (exp > del_exp) exp=del_exp;
1699     }
1700     return exp;
1701     }
1702    
1703     sint64 check_exp_adjust(object *op, sint64 exp)
1704     {
1705     if (exp<0) return check_exp_loss(op, exp);
1706     else return MIN(exp, MAX_EXPERIENCE - op->stats.exp);
1707     }
1708    
1709    
1710     /* Subtracts experience from player.
1711     * if skill is set and flag == SK_SUBTRACT_SKILL_EXP, then we
1712     * only subtract from the matching skill. Otherwise,
1713     * this subtracts a portion from all
1714     * skills the player has. Eg, if we figure the player is losing 10%
1715     * of his total exp, what happens is he loses 10% from all his skills.
1716     * Note that if permanent exp is used, player may not in fact lose
1717     * as much as listed. Eg, if player has gotten reduced to the point
1718     * where everything is at the minimum perm exp, he would lose nothing.
1719     * exp is the amount of exp to subtract - thus, it should be
1720     * a postive number.
1721     */
1722     static void subtract_player_exp(object *op, sint64 exp, const char *skill, int flag)
1723     {
1724     float fraction = (float) exp/(float) op->stats.exp;
1725     object *tmp;
1726     sint64 del_exp;
1727    
1728     for(tmp=op->inv;tmp;tmp=tmp->below)
1729     if(tmp->type==SKILL && tmp->stats.exp) {
1730     if (flag == SK_SUBTRACT_SKILL_EXP && skill && !strcmp(tmp->skill, skill)) {
1731     del_exp = check_exp_loss(tmp, exp);
1732     tmp->stats.exp -= del_exp;
1733     player_lvl_adj(op, tmp);
1734     } else if (flag != SK_SUBTRACT_SKILL_EXP) {
1735     /* only want to process other skills if we are not trying
1736     * to match a specific skill.
1737     */
1738     del_exp = check_exp_loss(tmp, tmp->stats.exp * fraction);
1739     tmp->stats.exp -= del_exp;
1740     player_lvl_adj(op, tmp);
1741     }
1742     }
1743     if (flag != SK_SUBTRACT_SKILL_EXP) {
1744     del_exp = check_exp_loss(op, exp);
1745     op->stats.exp -= del_exp;
1746     player_lvl_adj(op,NULL);
1747     }
1748     }
1749    
1750    
1751    
1752     /* change_exp() - changes experience to a player/monster. This
1753     * does bounds checking to make sure we don't overflow the max exp.
1754     *
1755     * The exp passed is typically not modified much by this function -
1756     * it is assumed the caller has modified the exp as needed.
1757     * skill_name is the skill that should get the exp added.
1758     * flag is what to do if player doesn't have the skill.
1759     * these last two values are only used for players.
1760     */
1761    
1762     void change_exp(object *op, sint64 exp, const char *skill_name, int flag) {
1763    
1764     #ifdef EXP_DEBUG
1765     #ifndef WIN32
1766     LOG(llevDebug,"change_exp() called for %s, exp = %lld\n",query_name(op),exp);
1767     #else
1768     LOG(llevDebug,"change_exp() called for %s, exp = %I64d\n",query_name(op),exp);
1769     #endif
1770     #endif
1771    
1772     /* safety */
1773     if(!op) {
1774     LOG(llevError,"change_exp() called for null object!\n");
1775     return;
1776     }
1777    
1778     /* if no change in exp, just return - most of the below code
1779     * won't do anything if the value is 0 anyways.
1780     */
1781     if (exp == 0) return;
1782    
1783     /* Monsters are easy - we just adjust their exp - we
1784     * don't adjust level, since in most cases it is unrelated to
1785     * the exp they have - the monsters exp represents what its
1786     * worth.
1787     */
1788     if(op->type != PLAYER) {
1789     /* Sanity check */
1790     if (!QUERY_FLAG(op, FLAG_ALIVE)) return;
1791    
1792     /* reset exp to max allowed value. We subtract from
1793     * MAX_EXPERIENCE to prevent overflows. If the player somehow has
1794     * more than max exp, just return.
1795     */
1796     if (exp > 0 && ( op->stats.exp > (MAX_EXPERIENCE - exp))) {
1797     exp = MAX_EXPERIENCE - op->stats.exp;
1798     if (exp < 0) return;
1799     }
1800    
1801     op->stats.exp += exp;
1802     }
1803     else { /* Players only */
1804     if(exp>0)
1805     add_player_exp(op, exp, skill_name, flag);
1806     else
1807     /* note that when you lose exp, it doesn't go against
1808     * a particular skill, so we don't need to pass that
1809     * along.
1810     */
1811     subtract_player_exp(op, FABS(exp), skill_name, flag);
1812    
1813     }
1814     }
1815    
1816     /* Applies a death penalty experience. 20% or 3 levels, whichever is
1817     * less experience lost.
1818     */
1819    
1820     void apply_death_exp_penalty(object *op) {
1821     object *tmp;
1822     sint64 loss;
1823     sint64 loss_20p; /* 20 percent experience loss */
1824     sint64 loss_3l; /* 3 level experience loss */
1825    
1826     for(tmp=op->inv;tmp;tmp=tmp->below)
1827     if(tmp->type==SKILL && tmp->stats.exp) {
1828    
1829     loss_20p = tmp->stats.exp * 0.20;
1830     loss_3l = tmp->stats.exp - levels[MAX(0,tmp->level -3)];
1831    
1832     /* With the revised exp system, you can get cases where
1833     * losing 3 levels would still require that you have more
1834     * exp than you current have - this is true if the levels
1835     * tables is a lot harder.
1836     */
1837     if (loss_3l < 0) loss_3l = 0;
1838    
1839     loss = check_exp_loss(tmp, MIN(loss_3l, loss_20p));
1840    
1841     tmp->stats.exp -= loss;
1842     player_lvl_adj(op,tmp);
1843     }
1844    
1845     loss_20p = op->stats.exp * 0.20;
1846     loss_3l = op->stats.exp - levels[MAX(0,op->level -3)];
1847     if (loss_3l < 0) loss_3l = 0;
1848     loss = check_exp_loss(op, MIN(loss_3l, loss_20p));
1849    
1850     op->stats.exp -= loss;
1851     player_lvl_adj(op,NULL);
1852     }
1853    
1854     /* This function takes an object (monster/player, op), and
1855     * determines if it makes a basic save throw by looking at the
1856     * save_throw table. level is the effective level to make
1857     * the save at, and bonus is any plus/bonus (typically based on
1858     * resistance to particular attacktype.
1859     * Returns 1 if op makes his save, 0 if he failed
1860     */
1861     int did_make_save(object *op, int level, int bonus)
1862     {
1863     if (level > MAX_SAVE_LEVEL) level = MAX_SAVE_LEVEL;
1864    
1865     if ((random_roll(1, 20, op, PREFER_HIGH) + bonus) < savethrow[level])
1866     return 0;
1867     return 1;
1868     }
1869