ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/skills.C
Revision: 1.35
Committed: Sat May 19 00:31:08 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_1
Changes since 1.34: +11 -9 lines
Log Message:
A player must never apply two skills. crossfire always has and will ever
complain about that.

Nevertheless, levitation (and ONLY levitation) gets applied in addition to
other skills. Now that cannot work with the new skill system. Introduce a
special flag for levitation only so it only gets half-applied.

This probably fixes quite a number of older "has two skills applied"
messages.

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.30 * CrossFire, A Multiplayer game
3 pippijn 1.23 *
4     * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5     * Copyright (C) 2003 Mark Wedel & Crossfire Development Team
6     * Copyright (C) 1992 Frank Tore Johansen
7     *
8     * This program is free software; you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation; either version 2 of the License, or
11     * (at your option) any later version.
12     *
13     * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17     *
18     * You should have received a copy of the GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21     *
22     * The authors can be reached via e-mail to <crossfire@schmorp.de>
23     */
24 elmex 1.1
25     #include <global.h>
26     #include <object.h>
27 root 1.24 #include <sproto.h>
28 elmex 1.1 #include <living.h>
29     #include <skills.h>
30     #include <spells.h>
31     #include <book.h>
32    
33 root 1.8 /* adj_stealchance() - increased values indicate better attempts */
34     static int
35     adj_stealchance (object *op, object *victim, int roll)
36     {
37     object *equip;
38    
39     if (!op || !victim || !roll)
40     return -1;
41    
42     /* Only prohibit stealing if the player does not have a free
43     * hand available and in fact does have hands.
44     */
45 root 1.30 if (op->type == PLAYER && op->slot[body_arm].used <= 0 && op->slot[body_arm].info)
46 root 1.8 {
47     new_draw_info (NDI_UNIQUE, 0, op, "But you have no free hands to steal with!");
48     return -1;
49 elmex 1.1 }
50 root 1.8
51     /* ADJUSTMENTS */
52    
53     /* Its harder to steal from hostile beings! */
54     if (!QUERY_FLAG (victim, FLAG_UNAGGRESSIVE))
55     roll = roll / 2;
56    
57     /* Easier to steal from sleeping beings, or if the thief is
58     * unseen */
59     if (QUERY_FLAG (victim, FLAG_SLEEP))
60     roll = roll * 3;
61     else if (op->invisible)
62     roll = roll * 2;
63    
64     /* check stealing 'encumberance'. Having this equipment applied makes
65     * it quite a bit harder to steal.
66     */
67     for (equip = op->inv; equip; equip = equip->below)
68     {
69     if (equip->type == WEAPON && QUERY_FLAG (equip, FLAG_APPLIED))
70     {
71     roll -= equip->weight / 10000;
72     }
73     if (equip->type == BOW && QUERY_FLAG (equip, FLAG_APPLIED))
74     roll -= equip->weight / 5000;
75     if (equip->type == SHIELD && QUERY_FLAG (equip, FLAG_APPLIED))
76     {
77     roll -= equip->weight / 2000;
78     }
79     if (equip->type == ARMOUR && QUERY_FLAG (equip, FLAG_APPLIED))
80     roll -= equip->weight / 5000;
81     if (equip->type == GLOVES && QUERY_FLAG (equip, FLAG_APPLIED))
82     roll -= equip->weight / 100;
83     }
84     if (roll < 0)
85     roll = 0;
86     return roll;
87 elmex 1.1 }
88    
89     /*
90     * When stealing: dependent on the intelligence/wisdom of whom you're
91     * stealing from (op in attempt_steal), offset by your dexterity and
92     * skill at stealing. They may notice your attempt, whether successful
93     * or not.
94     * op is the target (person being pilfered)
95     * who is the person doing the stealing.
96     * skill is the skill object (stealing).
97     */
98 root 1.8 static int
99     attempt_steal (object *op, object *who, object *skill)
100 elmex 1.1 {
101 root 1.8 object *success = NULL, *tmp = NULL, *next;
102     int roll = 0, chance = 0, stats_value;
103     rv_vector rv;
104    
105     stats_value = ((who->stats.Dex + who->stats.Int) * 3) / 2;
106    
107     /* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them)
108     * they will try to prevent stealing if they can. Only unseen theives will
109     * have much chance of success.
110     */
111     if (op->type != PLAYER && QUERY_FLAG (op, FLAG_NO_STEAL))
112     {
113     if (can_detect_enemy (op, who, &rv))
114     {
115     npc_call_help (op);
116     CLEAR_FLAG (op, FLAG_UNAGGRESSIVE);
117     new_draw_info (NDI_UNIQUE, 0, who, "Your attempt is prevented!");
118     return 0;
119     }
120     else /* help npc to detect thief next time by raising its wisdom */
121     op->stats.Wis += (op->stats.Int / 5) + 1;
122     if (op->stats.Wis > MAX_STAT)
123     op->stats.Wis = MAX_STAT;
124 elmex 1.1 }
125 root 1.32
126 root 1.8 if (op->type == PLAYER && QUERY_FLAG (op, FLAG_WIZ))
127     {
128     new_draw_info (NDI_UNIQUE, 0, who, "You can't steal from the dungeon master!\n");
129     return 0;
130 elmex 1.1 }
131 root 1.32
132     // only allow stealing between hostile players (TODO: probably should change)
133 root 1.8 if (op->type == PLAYER && who->type == PLAYER && (who->contr->peaceful || op->contr->peaceful))
134     {
135     new_draw_info (NDI_UNIQUE, 0, who, "You can't steal from other players!\n");
136 elmex 1.1 return 0;
137     }
138    
139 root 1.8 /* Ok then, go thru their inventory, stealing */
140 root 1.32 for (tmp = op->inv; tmp; tmp = next)
141 root 1.8 {
142     next = tmp->below;
143 root 1.6
144 root 1.8 /* you can't steal worn items, starting items, wiz stuff,
145     * innate abilities, or items w/o a type. Generally
146     * speaking, the invisibility flag prevents experience or
147     * abilities from being stolen since these types are currently
148     * always invisible objects. I was implicit here so as to prevent
149     * future possible problems. -b.t.
150     * Flesh items generated w/ fix_flesh_item should have FLAG_NO_STEAL
151     * already -b.t.
152     */
153    
154 root 1.32 if (QUERY_FLAG (tmp, FLAG_APPLIED)
155     || !tmp->type
156 elmex 1.17 || tmp->type == SPELL
157 root 1.32 || QUERY_FLAG (tmp, FLAG_STARTEQUIP)
158     || QUERY_FLAG (tmp, FLAG_NO_STEAL)
159     || tmp->invisible)
160 root 1.8 continue;
161    
162     /* Okay, try stealing this item. Dependent on dexterity of thief,
163     * skill level, see the adj_stealroll fctn for more detail.
164     */
165    
166     roll = die_roll (2, 100, who, PREFER_LOW) / 2; /* weighted 1-100 */
167    
168     if ((chance = adj_stealchance (who, op, (stats_value + skill->level * 10 - op->level * 3))) == -1)
169     return 0;
170     else if (roll < chance)
171     {
172     pick_up (who, tmp);
173     /* need to see if the player actually stole this item -
174     * if it is in the players inv, assume it is. This prevents
175     * abuses where the player can not carry the item, so just
176     * keeps stealing it over and over.
177     */
178 root 1.10 if (tmp->destroyed () || tmp->env != op)
179 root 1.8 {
180     /* for players, play_sound: steals item */
181     success = tmp;
182     CLEAR_FLAG (tmp, FLAG_INV_LOCKED);
183    
184     /* Don't delete it from target player until we know
185     * the thief has picked it up. can't just look at tmp->count,
186     * as it's possible that it got merged when picked up.
187     */
188     if (op->type == PLAYER)
189 root 1.10 esrv_del_item (op->contr, tmp->count);
190 root 1.6 }
191 root 1.8 break;
192 root 1.6 }
193 root 1.8 } /* for loop looking for an item */
194 elmex 1.1
195 root 1.8 if (!tmp)
196     {
197     new_draw_info_format (NDI_UNIQUE, 0, who, "%s%s has nothing you can steal!", op->type == PLAYER ? "" : "The ", query_name (op));
198     return 0;
199 elmex 1.1 }
200    
201 root 1.8 /* If you arent high enough level, you might get something BUT
202     * the victim will notice your stealing attempt. Ditto if you
203     * attempt to steal something heavy off them, they're bound to notice
204     */
205    
206     if ((roll >= skill->level) || !chance
207     || (tmp && tmp->weight > (250 * (random_roll (0, stats_value + skill->level * 10 - 1, who, PREFER_LOW)))))
208     {
209    
210     /* victim figures out where the thief is! */
211     if (who->hide)
212     make_visible (who);
213    
214     if (op->type != PLAYER)
215     {
216     /* The unaggressives look after themselves 8) */
217     if (who->type == PLAYER)
218     {
219     npc_call_help (op);
220     new_draw_info_format (NDI_UNIQUE, 0, who, "%s notices your attempted pilfering!", query_name (op));
221     }
222     CLEAR_FLAG (op, FLAG_UNAGGRESSIVE);
223     /* all remaining npc items are guarded now. Set flag NO_STEAL
224     * on the victim.
225     */
226     SET_FLAG (op, FLAG_NO_STEAL);
227     }
228     else
229     { /* stealing from another player */
230     char buf[MAX_BUF];
231    
232     /* Notify the other player */
233     if (success && who->stats.Int > random_roll (0, 19, op, PREFER_LOW))
234     {
235     sprintf (buf, "Your %s is missing!", query_name (success));
236 root 1.6 }
237 root 1.8 else
238     {
239     sprintf (buf, "Your pack feels strangely lighter.");
240 root 1.6 }
241 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, buf);
242     if (!success)
243     {
244     if (who->invisible)
245     {
246     sprintf (buf, "you feel itchy fingers getting at your pack.");
247     }
248     else
249     {
250     sprintf (buf, "%s looks very shifty.", query_name (who));
251 root 1.6 }
252 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, buf);
253 root 1.6 }
254 root 1.8 } /* else stealing from another player */
255     /* play_sound("stop! thief!"); kindofthing */
256     } /* if you weren't 100% successful */
257     return success ? 1 : 0;
258 elmex 1.1 }
259    
260 root 1.8 int
261     steal (object *op, int dir, object *skill)
262 elmex 1.1 {
263 root 1.8 object *tmp, *next;
264     sint16 x, y;
265 root 1.11 maptile *m;
266 root 1.8 int mflags;
267 elmex 1.1
268 root 1.8 x = op->x + freearr_x[dir];
269     y = op->y + freearr_y[dir];
270    
271     if (dir == 0)
272     {
273     /* Can't steal from ourself! */
274     return 0;
275 elmex 1.1 }
276    
277 root 1.8 m = op->map;
278     mflags = get_map_flags (m, &m, x, y, &x, &y);
279     /* Out of map - can't do it. If nothing alive on this space,
280     * don't need to look any further.
281     */
282     if ((mflags & P_OUT_OF_MAP) || !(mflags & P_IS_ALIVE))
283     return 0;
284    
285     /* If player can't move onto the space, can't steal from it. */
286     if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)))
287     return 0;
288    
289     /* Find the topmost object at this spot */
290 root 1.16 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above);
291 root 1.8
292     /* For all the stacked objects at this point, attempt a steal */
293     for (; tmp != NULL; tmp = next)
294     {
295     next = tmp->below;
296     /* Minor hack--for multi square beings - make sure we get
297     * the 'head' coz 'tail' objects have no inventory! - b.t.
298     */
299     if (tmp->head)
300     tmp = tmp->head;
301    
302     if (tmp->type != PLAYER && !QUERY_FLAG (tmp, FLAG_MONSTER))
303     continue;
304    
305     /* do not reveal hidden DMs */
306     if (tmp->type == PLAYER && QUERY_FLAG (tmp, FLAG_WIZ) && tmp->contr->hidden)
307     continue;
308     if (attempt_steal (tmp, op, skill))
309     {
310     if (tmp->type == PLAYER) /* no xp for stealing from another player */
311     return 0;
312 elmex 1.1
313 root 1.8 /* no xp for stealing from pets (of players) */
314     if (QUERY_FLAG (tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE)
315     {
316 root 1.15 object *owner = tmp->owner;
317 elmex 1.1
318 root 1.8 if (owner != NULL && owner->type == PLAYER)
319 root 1.6 return 0;
320 root 1.8 }
321 root 1.6
322 root 1.8 // reduce monster experience by experience we gained, as to
323     // limit the amount of exp that can be gained by stealing from monsters
324     // (jessies gave ~20,000,000 exp otherwise.
325     int exp = calc_skill_exp (op, tmp, skill);
326 elmex 1.1
327 root 1.8 exp = MIN (tmp->stats.exp, exp);
328     tmp->stats.exp -= exp;
329     return exp;
330 root 1.6 }
331 elmex 1.1 }
332 root 1.8 return 0;
333 elmex 1.1 }
334    
335 root 1.8 static int
336     attempt_pick_lock (object *door, object *pl, object *skill)
337 elmex 1.1 {
338 root 1.8 int difficulty = pl->map->difficulty ? pl->map->difficulty : 0;
339     int success = 0, number; /* did we get anything? */
340 elmex 1.1
341    
342 root 1.8 /* Try to pick the lock on this item (doors only for now).
343     * Dependent on dexterity/skill SK_level of the player and
344     * the map level difficulty.
345     */
346     number = (die_roll (2, 40, pl, PREFER_LOW) - 2) / 2;
347     if (number < (pl->stats.Dex + skill->level - difficulty))
348     {
349     remove_door (door);
350     success = 1;
351     }
352     else if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP))
353     { /* set off any traps? */
354     spring_trap (door->inv, pl);
355     }
356     return success;
357 elmex 1.1 }
358    
359    
360     /* Implementation by bt. (thomas@astro.psu.edu)
361     * monster implementation 7-7-95 by bt.
362     */
363    
364 root 1.8 int
365     pick_lock (object *pl, int dir, object *skill)
366 elmex 1.1 {
367 root 1.8 object *tmp;
368     int x = pl->x + freearr_x[dir];
369     int y = pl->y + freearr_y[dir];
370 elmex 1.1
371 root 1.8 if (!dir)
372     dir = pl->facing;
373 elmex 1.1
374 root 1.8 /* For all the stacked objects at this point find a door */
375     if (out_of_map (pl->map, x, y))
376     {
377     new_draw_info (NDI_UNIQUE, 0, pl, "There is no lock there.");
378     return 0;
379 elmex 1.1 }
380    
381 root 1.16 for (tmp = GET_MAP_OB (pl->map, x, y); tmp; tmp = tmp->above)
382 root 1.8 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
383     break;
384 elmex 1.1
385 root 1.8 if (!tmp)
386     {
387     new_draw_info (NDI_UNIQUE, 0, pl, "There is no lock there.");
388     return 0;
389 elmex 1.1 }
390 root 1.8 if (tmp->type == LOCKED_DOOR)
391     {
392     new_draw_info (NDI_UNIQUE, 0, pl, "You can't pick that lock!");
393     return 0;
394 elmex 1.1 }
395    
396 root 1.8 if (!tmp->move_block)
397     {
398     new_draw_info (NDI_UNIQUE, 0, pl, "The door has no lock!");
399     return 0;
400 elmex 1.1 }
401    
402 root 1.8 if (attempt_pick_lock (tmp, pl, skill))
403     {
404     new_draw_info (NDI_UNIQUE, 0, pl, "You pick the lock.");
405     return calc_skill_exp (pl, NULL, skill);
406     }
407     else
408     {
409     new_draw_info (NDI_UNIQUE, 0, pl, "You fail to pick the lock.");
410     return 0;
411 elmex 1.1 }
412 root 1.8 }
413 elmex 1.1
414     /* HIDE CODE. The user becomes undetectable (not just 'invisible') for
415     * a short while (success and duration dependant on player SK_level,
416     * dexterity, charisma, and map difficulty).
417     * Players have a good chance of becoming 'unhidden' if they move
418     * and like invisiblity will be come visible if they attack
419     * Implemented by b.t. (thomas@astro.psu.edu)
420     * July 7, 1995 - made hiding possible for monsters. -b.t.
421 root 1.8 */
422     static int
423     attempt_hide (object *op, object *skill)
424     {
425     int number, difficulty = op->map->difficulty;
426     int terrain = hideability (op);
427    
428     if (terrain < -10) /* not enough cover here */
429     return 0;
430 elmex 1.1
431 root 1.8 /* Hiding success and duration dependant on skill level,
432     * op->stats.Dex, map difficulty and terrain.
433     */
434 root 1.35 number = (die_roll (2, 25, op, PREFER_LOW) - 2) / 2;
435 elmex 1.1
436 root 1.8 if (!stand_near_hostile (op) && (number < (op->stats.Dex + skill->level + terrain - difficulty)))
437     {
438     op->invisible += 100; /* set the level of 'hiddeness' */
439 root 1.35
440 root 1.8 if (op->type == PLAYER)
441     op->contr->tmp_invis = 1;
442 root 1.35
443 root 1.8 op->hide = 1;
444     return 1;
445 elmex 1.1 }
446 root 1.35
447 root 1.8 return 0;
448 elmex 1.1 }
449    
450     /* patched this to take terrain into consideration */
451 root 1.8 int
452     hide (object *op, object *skill)
453     {
454     /* the preliminaries -- Can we really hide now? */
455     /* this keeps monsters from using invisibilty spells and hiding */
456 elmex 1.1
457 root 1.8 if (QUERY_FLAG (op, FLAG_MAKE_INVIS))
458     {
459     new_draw_info (NDI_UNIQUE, 0, op, "You don't need to hide while invisible!");
460     return 0;
461     }
462     else if (!op->hide && op->invisible > 0 && op->type == PLAYER)
463     {
464     new_draw_info (NDI_UNIQUE, 0, op, "Your attempt to hide breaks the invisibility spell!");
465     make_visible (op);
466     }
467 elmex 1.1
468 root 1.35 if (op->invisible > 50 * skill->level)
469 root 1.8 {
470     new_draw_info (NDI_UNIQUE, 0, op, "You are as hidden as you can get.");
471     return 0;
472 elmex 1.1 }
473 root 1.8
474     if (attempt_hide (op, skill))
475     {
476     new_draw_info (NDI_UNIQUE, 0, op, "You hide in the shadows.");
477     update_object (op, UP_OBJ_FACE);
478     return calc_skill_exp (op, NULL, skill);
479 elmex 1.1 }
480 root 1.35
481 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, "You fail to conceal yourself.");
482     return 0;
483 elmex 1.1 }
484    
485     /* stop_jump() - End of jump. Clear flags, restore the map, and
486     * freeze the jumper a while to simulate the exhaustion
487     * of jumping.
488     */
489 root 1.8 static void
490     stop_jump (object *pl, int dist, int spaces)
491     {
492 root 1.18 pl->update_stats ();
493 root 1.20 pl->map->insert (pl, pl->x, pl->y, pl);
494 elmex 1.1 }
495    
496 root 1.8 static int
497     attempt_jump (object *pl, int dir, int spaces, object *skill)
498     {
499     object *tmp;
500     int i, exp = 0, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
501     sint16 x, y;
502 root 1.11 maptile *m;
503 root 1.8
504     /* Jump loop. Go through spaces opject wants to jump. Halt the
505     * jump if a wall or creature is in the way. We set FLAG_FLYING
506     * temporarily to allow player to aviod exits/archs that are not
507     * fly_on, fly_off. This will also prevent pickup of objects
508     * while jumping over them.
509     */
510    
511 root 1.12 pl->remove ();
512 root 1.8
513     /*
514     * I don't think this is actually needed - all the movement
515     * code is handled in this function, and I don't see anyplace
516     * that cares about the move_type being flying.
517     */
518     pl->move_type |= MOVE_FLY_LOW;
519 root 1.6
520 root 1.8 for (i = 0; i <= spaces; i++)
521     {
522     x = pl->x + dx;
523     y = pl->y + dy;
524     m = pl->map;
525    
526     mflags = get_map_flags (m, &m, x, y, &x, &y);
527    
528     if (mflags & P_OUT_OF_MAP)
529     {
530     (void) stop_jump (pl, i, spaces);
531     return 0;
532     }
533     if (OB_TYPE_MOVE_BLOCK (pl, GET_MAP_MOVE_BLOCK (m, x, y)))
534     {
535     new_draw_info (NDI_UNIQUE, 0, pl, "Your jump is blocked.");
536     stop_jump (pl, i, spaces);
537     return 0;
538     }
539    
540 root 1.16 for (tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above)
541 root 1.8 {
542     /* Jump into creature */
543     if (QUERY_FLAG (tmp, FLAG_MONSTER) || (tmp->type == PLAYER && (!QUERY_FLAG (tmp, FLAG_WIZ) || !tmp->contr->hidden)))
544     {
545     new_draw_info_format (NDI_UNIQUE, 0, pl, "You jump into %s%s.", tmp->type == PLAYER ? "" : "the ", &tmp->name);
546     if (tmp->type != PLAYER ||
547     (pl->type == PLAYER && pl->contr->party == NULL) ||
548     (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
549     exp = skill_attack (tmp, pl, pl->facing, "kicked", skill); /* pl makes an attack */
550     stop_jump (pl, i, spaces);
551     return exp; /* note that calc_skill_exp() is already called by skill_attack() */
552 root 1.6 }
553 root 1.8 /* If the space has fly on set (no matter what the space is),
554     * we should get the effects - after all, the player is
555     * effectively flying.
556     */
557     if (tmp->move_on & MOVE_FLY_LOW)
558     {
559     pl->x = x;
560     pl->y = y;
561     pl->map = m;
562     stop_jump (pl, i, spaces);
563     return calc_skill_exp (pl, NULL, skill);
564 root 1.6 }
565     }
566 root 1.8 pl->x = x;
567     pl->y = y;
568     pl->map = m;
569 elmex 1.1 }
570 root 1.8 stop_jump (pl, i, spaces);
571     return calc_skill_exp (pl, NULL, skill);
572 elmex 1.1 }
573    
574     /* jump() - this is both a new type of movement for player/monsters and
575     * an attack as well.
576     * Perhaps we should allow more spaces based on level, eg, level 50
577     * jumper can jump several spaces?
578 root 1.8 */
579 elmex 1.1
580 root 1.8 int
581     jump (object *pl, int dir, object *skill)
582 elmex 1.1 {
583 root 1.8 int spaces = 0, stats;
584     int str = pl->stats.Str;
585     int dex = pl->stats.Dex;
586    
587     dex = dex ? dex : 15;
588     str = str ? str : 10;
589    
590     stats = str * str * str * dex * skill->level;
591    
592     if (pl->carrying != 0) /* don't want div by zero !! */
593     spaces = (int) (stats / pl->carrying);
594     else
595     spaces = 2; /* pl has no objects - gets the far jump */
596    
597     if (spaces > 2)
598     spaces = 2;
599     else if (spaces == 0)
600     {
601     new_draw_info (NDI_UNIQUE, 0, pl, "You are carrying too much weight to jump.");
602     return 0;
603 elmex 1.1 }
604 root 1.8 return attempt_jump (pl, dir, spaces, skill);
605 elmex 1.1 }
606    
607    
608     /* skill_ident() - this code is supposed to allow players to identify
609     * classes of objects with the various "auto-ident" skills. Player must
610     * have unidentified objects of the right type in order for the skill
611     * to work. While multiple classes of objects may be identified,
612     * this code is kind of yucky -- it would be nice to make it a bit
613     * more generalized. Right now, skill indices are embedded in this routine.
614     * Returns amount of experience gained (on successful ident).
615     * - b.t. (thomas@astro.psu.edu)
616     */
617    
618 root 1.8 static int
619     do_skill_detect_curse (object *pl, object *skill)
620     {
621     object *tmp;
622     int success = 0;
623    
624     for (tmp = pl->inv; tmp; tmp = tmp->below)
625     if (!tmp->invisible
626     && !QUERY_FLAG (tmp, FLAG_IDENTIFIED) && !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED)
627     && (QUERY_FLAG (tmp, FLAG_CURSED) || QUERY_FLAG (tmp, FLAG_DAMNED)) && tmp->item_power < skill->level)
628     {
629     SET_FLAG (tmp, FLAG_KNOWN_CURSED);
630     esrv_update_item (UPD_FLAGS, pl, tmp);
631     success += calc_skill_exp (pl, tmp, skill);
632     }
633    
634     /* Check ground, too, but only objects the player could pick up */
635 root 1.16 for (tmp = GET_MAP_OB (pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
636 root 1.8 if (can_pick (pl, tmp) &&
637     !QUERY_FLAG (tmp, FLAG_IDENTIFIED) &&
638     !QUERY_FLAG (tmp, FLAG_KNOWN_CURSED)
639     && (QUERY_FLAG (tmp, FLAG_CURSED) || QUERY_FLAG (tmp, FLAG_DAMNED)) && tmp->item_power < skill->level)
640     {
641     SET_FLAG (tmp, FLAG_KNOWN_CURSED);
642     esrv_update_item (UPD_FLAGS, pl, tmp);
643     success += calc_skill_exp (pl, tmp, skill);
644     }
645    
646     return success;
647     }
648    
649     static int
650     do_skill_detect_magic (object *pl, object *skill)
651     {
652     object *tmp;
653     int success = 0;
654    
655     for (tmp = pl->inv; tmp; tmp = tmp->below)
656     if (!tmp->invisible
657     && !QUERY_FLAG (tmp, FLAG_IDENTIFIED) && !QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL)
658     && (is_magical (tmp)) && tmp->item_power < skill->level)
659     {
660     SET_FLAG (tmp, FLAG_KNOWN_MAGICAL);
661     esrv_update_item (UPD_FLAGS, pl, tmp);
662     success += calc_skill_exp (pl, tmp, skill);
663     }
664    
665     /* Check ground, too, but like above, only if the object can be picked up */
666 root 1.16 for (tmp = GET_MAP_OB (pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
667 root 1.8 if (can_pick (pl, tmp) &&
668     !QUERY_FLAG (tmp, FLAG_IDENTIFIED) && !QUERY_FLAG (tmp, FLAG_KNOWN_MAGICAL) && (is_magical (tmp)) && tmp->item_power < skill->level)
669     {
670     SET_FLAG (tmp, FLAG_KNOWN_MAGICAL);
671     esrv_update_item (UPD_FLAGS, pl, tmp);
672     success += calc_skill_exp (pl, tmp, skill);
673     }
674 elmex 1.1
675 root 1.8 return success;
676 elmex 1.1 }
677    
678     /* Helper function for do_skill_ident, so that we can loop
679     * over inventory AND objects on the ground conveniently.
680     */
681 root 1.8 int
682     do_skill_ident2 (object *tmp, object *pl, int obj_class, object *skill)
683 elmex 1.1 {
684 root 1.8 int success = 0, chance;
685     int skill_value = skill->level * pl->stats.Int ? pl->stats.Int : 10;
686 elmex 1.1
687 root 1.8 if (!QUERY_FLAG (tmp, FLAG_IDENTIFIED) && !QUERY_FLAG (tmp, FLAG_NO_SKILL_IDENT)
688     && need_identify (tmp) && !tmp->invisible && tmp->type == obj_class)
689     {
690     chance = die_roll (3, 10, pl, PREFER_LOW) - 3 + rndm (0, (tmp->magic ? tmp->magic * 5 : 1) - 1);
691 elmex 1.1
692 root 1.8 if (skill_value >= chance)
693     {
694     identify (tmp);
695    
696     if (pl->type == PLAYER)
697     {
698     new_draw_info_format (NDI_UNIQUE, 0, pl, "You identify %s.", long_desc (tmp, pl));
699    
700     if (tmp->msg)
701     {
702     new_draw_info (NDI_UNIQUE, 0, pl, "The item has a story:");
703     new_draw_info (NDI_UNIQUE, 0, pl, tmp->msg);
704     }
705 elmex 1.1
706 root 1.8 /* identify will take care of updating the item if it is in the players inventory. IF on map, do it here */
707     if (tmp->map)
708     esrv_send_item (pl, tmp);
709     }
710     success += calc_skill_exp (pl, tmp, skill);
711     }
712     else
713     SET_FLAG (tmp, FLAG_NO_SKILL_IDENT);
714     }
715 elmex 1.1
716 root 1.8 return success;
717 elmex 1.1 }
718    
719     /* do_skill_ident() - workhorse for skill_ident() -b.t.
720     */
721 root 1.8 static int
722     do_skill_ident (object *pl, int obj_class, object *skill)
723     {
724     object *tmp;
725     int success = 0;
726    
727     for (tmp = pl->inv; tmp; tmp = tmp->below)
728     success += do_skill_ident2 (tmp, pl, obj_class, skill);
729     /* check the ground */
730 elmex 1.1
731 root 1.16 for (tmp = GET_MAP_OB (pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
732 root 1.8 success += do_skill_ident2 (tmp, pl, obj_class, skill);
733 elmex 1.1
734 root 1.8 return success;
735     }
736 elmex 1.1
737 root 1.8 int
738     skill_ident (object *pl, object *skill)
739     {
740     int success = 0;
741 elmex 1.1
742 root 1.8 if (pl->type != PLAYER)
743     return 0; /* only players will skill-identify */
744 elmex 1.1
745 root 1.8 new_draw_info (NDI_UNIQUE, 0, pl, "You look at the objects nearby...");
746 elmex 1.1
747 root 1.8 switch (skill->subtype)
748     {
749 root 1.6 case SK_SMITHERY:
750 root 1.8 success += do_skill_ident (pl, WEAPON, skill) + do_skill_ident (pl, ARMOUR, skill)
751     + do_skill_ident (pl, BRACERS, skill) + do_skill_ident (pl, CLOAK, skill)
752     + do_skill_ident (pl, BOOTS, skill) + do_skill_ident (pl, SHIELD, skill)
753     + do_skill_ident (pl, GIRDLE, skill) + do_skill_ident (pl, HELMET, skill) + do_skill_ident (pl, GLOVES, skill);
754     break;
755 root 1.6
756     case SK_BOWYER:
757 root 1.8 success += do_skill_ident (pl, BOW, skill) + do_skill_ident (pl, ARROW, skill);
758     break;
759 root 1.6
760     case SK_ALCHEMY:
761 root 1.8 success += do_skill_ident (pl, POTION, skill) + do_skill_ident (pl, POISON, skill)
762     + do_skill_ident (pl, CONTAINER, skill) + do_skill_ident (pl, DRINK, skill) + do_skill_ident (pl, INORGANIC, skill);
763     break;
764    
765     case SK_WOODSMAN:
766     success += do_skill_ident (pl, FOOD, skill) + do_skill_ident (pl, DRINK, skill) + do_skill_ident (pl, FLESH, skill);
767     break;
768 root 1.6
769     case SK_JEWELER:
770 root 1.8 success += do_skill_ident (pl, GEM, skill) + do_skill_ident (pl, RING, skill) + do_skill_ident (pl, AMULET, skill);
771     break;
772 root 1.6
773     case SK_LITERACY:
774 root 1.8 success += do_skill_ident (pl, SPELLBOOK, skill) + do_skill_ident (pl, SCROLL, skill) + do_skill_ident (pl, BOOK, skill);
775     break;
776 root 1.6
777     case SK_THAUMATURGY:
778 root 1.8 success += do_skill_ident (pl, WAND, skill) + do_skill_ident (pl, ROD, skill) + do_skill_ident (pl, HORN, skill);
779     break;
780 root 1.6
781     case SK_DET_CURSE:
782 root 1.8 success = do_skill_detect_curse (pl, skill);
783     if (success)
784     new_draw_info (NDI_UNIQUE, 0, pl, "...and discover cursed items!");
785     break;
786 root 1.6
787     case SK_DET_MAGIC:
788 root 1.8 success = do_skill_detect_magic (pl, skill);
789     if (success)
790     new_draw_info (NDI_UNIQUE, 0, pl, "...and discover items imbued with mystic forces!");
791     break;
792 root 1.6
793     default:
794 root 1.8 LOG (llevError, "Error: bad call to skill_ident()\n");
795     return 0;
796     break;
797 elmex 1.1 }
798 root 1.8 if (!success)
799     {
800     new_draw_info (NDI_UNIQUE, 0, pl, "...and learn nothing more.");
801 elmex 1.1 }
802 root 1.8 return success;
803 elmex 1.1 }
804 root 1.8
805 elmex 1.1 /* players using this skill can 'charm' a monster --
806     * into working for them. It can only be used on
807     * non-special (see below) 'neutral' creatures.
808     * -b.t. (thomas@astro.psu.edu)
809     */
810 root 1.8 int
811     use_oratory (object *pl, int dir, object *skill)
812     {
813     if (pl->type != PLAYER)
814     return 0; /* only players use this skill */
815 root 1.33
816 root 1.34 sint16 x = pl->x + freearr_x[dir],
817     y = pl->y + freearr_y[dir];
818     maptile *m = pl->map;
819    
820     int mflags = get_map_flags (m, &m, x, y, &x, &y);
821 root 1.8 if (mflags & P_OUT_OF_MAP)
822     return 0;
823    
824     /* Save some processing - we have the flag already anyways
825     */
826     if (!(mflags & P_IS_ALIVE))
827     {
828     new_draw_info (NDI_UNIQUE, 0, pl, "There is nothing to orate to.");
829     return 0;
830 elmex 1.1 }
831    
832 root 1.34 object *tmp;
833 root 1.16 for (tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above)
834 root 1.8 {
835     /* can't persuade players - return because there is nothing else
836     * on that space to charm. Same for multi space monsters and
837     * special monsters - we don't allow them to be charmed, and there
838     * is no reason to do further processing since they should be the
839     * only monster on the space.
840     */
841 root 1.33 if (tmp->type == PLAYER
842     || tmp->more || tmp->head_ () != tmp
843     || tmp->msg)
844 root 1.8 return 0;
845 elmex 1.1
846 root 1.8 if (QUERY_FLAG (tmp, FLAG_MONSTER))
847     break;
848 elmex 1.1 }
849    
850 root 1.8 if (!tmp)
851     {
852     new_draw_info (NDI_UNIQUE, 0, pl, "There is nothing to orate to.");
853     return 0;
854 elmex 1.1 }
855    
856 root 1.8 new_draw_info_format (NDI_UNIQUE, 0, pl, "You orate to the %s.", query_name (tmp));
857    
858     /* the following conditions limit who may be 'charmed' */
859 elmex 1.1
860 root 1.8 /* it's hostile! */
861     if (!QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (tmp, FLAG_FRIENDLY))
862     {
863     new_draw_info_format (NDI_UNIQUE, 0, pl, "Too bad the %s isn't listening!\n", query_name (tmp));
864     return 0;
865     }
866 elmex 1.1
867 root 1.8 /* it's already allied! */
868 root 1.34 if (QUERY_FLAG (tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE)
869 root 1.8 {
870 root 1.15 if (tmp->owner == pl)
871 root 1.8 {
872     new_draw_info (NDI_UNIQUE, 0, pl, "Your follower loves your speech.\n");
873     return 0;
874     }
875     else if (skill->level > tmp->level)
876     {
877     /* you steal the follower. Perhaps we should really look at the
878     * level of the owner above?
879     */
880 root 1.15 tmp->set_owner (pl);
881 root 1.33 tmp->skill = skill->skill;
882    
883 root 1.8 new_draw_info_format (NDI_UNIQUE, 0, pl, "You convince the %s to follow you instead!\n", query_name (tmp));
884     /* Abuse fix - don't give exp since this can otherwise
885     * be used by a couple players to gets lots of exp.
886     */
887     return 0;
888     }
889     else
890     {
891     /* In this case, you can't steal it from the other player */
892     return 0;
893 root 1.6 }
894 root 1.8 } /* Creature was already a pet of someone */
895 elmex 1.1
896 root 1.34 int level = skill->level + (pl->stats.Cha - tmp->stats.Int) / 2;
897 elmex 1.1
898 root 1.8 /* Ok, got a 'sucker' lets try to make them a follower */
899 root 1.34 if (level > 0 && tmp->level < (random_roll (0, level - 1, pl, PREFER_HIGH) - 1))
900 root 1.8 {
901     new_draw_info_format (NDI_UNIQUE, 0, pl, "You convince the %s to become your follower.\n", query_name (tmp));
902    
903 root 1.15 tmp->set_owner (pl);
904 root 1.33 tmp->skill = skill->skill;
905 root 1.8 tmp->stats.exp = 0;
906     add_friendly_object (tmp);
907     tmp->attack_movement = PETMOVE;
908     return calc_skill_exp (pl, tmp, skill);
909     }
910     /* Charm failed. Creature may be angry now */
911     else if ((skill->level + ((pl->stats.Cha - 10) / 2)) < random_roll (1, 2 * tmp->level, pl, PREFER_LOW))
912     {
913     new_draw_info_format (NDI_UNIQUE, 0, pl, "Your speech angers the %s!\n", query_name (tmp));
914     if (QUERY_FLAG (tmp, FLAG_FRIENDLY))
915     {
916     CLEAR_FLAG (tmp, FLAG_FRIENDLY);
917     remove_friendly_object (tmp);
918     tmp->attack_movement = 0; /* needed? */
919 root 1.6 }
920 root 1.27
921 root 1.8 CLEAR_FLAG (tmp, FLAG_UNAGGRESSIVE);
922 elmex 1.1 }
923 root 1.25
924 root 1.8 return 0; /* Fall through - if we get here, we didn't charm anything */
925 elmex 1.1 }
926    
927     /* Singing() -this skill allows the player to pacify nearby creatures.
928     * There are few limitations on who/what kind of
929     * non-player creatures that may be pacified. Right now, a player
930     * may pacify creatures which have Int == 0. In this routine, once
931     * successfully pacified the creature gets Int=1. Thus, a player
932     * may only pacify a creature once.
933     * BTW, I appologize for the naming of the skill, I couldnt think
934     * of anything better! -b.t.
935     */
936 root 1.8 int
937     singing (object *pl, int dir, object *skill)
938     {
939 root 1.34 int i, exp = 0;
940 root 1.8 object *tmp;
941 root 1.11 maptile *m;
942 root 1.8 sint16 x, y;
943    
944     if (pl->type != PLAYER)
945     return 0; /* only players use this skill */
946    
947     new_draw_info_format (NDI_UNIQUE, 0, pl, "You sing.");
948     for (i = 0; i < MIN (skill->level, SIZEOFFREE); i++)
949     {
950     x = pl->x + freearr_x[i];
951     y = pl->y + freearr_y[i];
952     m = pl->map;
953    
954 root 1.34 int mflags = get_map_flags (m, &m, x, y, &x, &y);
955 root 1.8 if (mflags & P_OUT_OF_MAP)
956     continue;
957     if (!(mflags & P_IS_ALIVE))
958     continue;
959    
960 root 1.16 for (tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above)
961 root 1.8 {
962     if (QUERY_FLAG (tmp, FLAG_MONSTER))
963     break;
964     /* can't affect players */
965     if (tmp->type == PLAYER)
966     break;
967     }
968    
969     /* Whole bunch of checks to see if this is a type of monster that would
970     * listen to singing.
971     */
972     if (tmp && QUERY_FLAG (tmp, FLAG_MONSTER) && !QUERY_FLAG (tmp, FLAG_NO_STEAL) && /* Been charmed or abused before */
973     !QUERY_FLAG (tmp, FLAG_SPLITTING) && /* no ears */
974     !QUERY_FLAG (tmp, FLAG_HITBACK) && /* was here before */
975     (tmp->level <= skill->level) && (!tmp->head) && !QUERY_FLAG (tmp, FLAG_UNDEAD) && !QUERY_FLAG (tmp, FLAG_UNAGGRESSIVE) && /* already calm */
976     !QUERY_FLAG (tmp, FLAG_FRIENDLY))
977     { /* already calm */
978    
979     /* stealing isn't really related (although, maybe it should
980     * be). This is mainly to prevent singing to the same monster
981     * over and over again and getting exp for it.
982     */
983 root 1.34 int level = skill->level + (pl->stats.Cha - 5 - tmp->stats.Int) / 2;
984    
985     if (level && tmp->level < random_roll (0, level - 1, pl, PREFER_HIGH))
986 root 1.8 {
987     SET_FLAG (tmp, FLAG_UNAGGRESSIVE);
988     new_draw_info_format (NDI_UNIQUE, 0, pl, "You calm down the %s\n", query_name (tmp));
989     /* Give exp only if they are not aware */
990 root 1.34
991 root 1.8 if (!QUERY_FLAG (tmp, FLAG_NO_STEAL))
992     exp += calc_skill_exp (pl, tmp, skill);
993 root 1.34
994 root 1.8 SET_FLAG (tmp, FLAG_NO_STEAL);
995     }
996     else
997     {
998     new_draw_info_format (NDI_UNIQUE, 0, pl, "Too bad the %s isn't listening!\n", query_name (tmp));
999     SET_FLAG (tmp, FLAG_NO_STEAL);
1000 root 1.6 }
1001     }
1002 elmex 1.1 }
1003 root 1.8 return exp;
1004 elmex 1.1 }
1005    
1006     /* The find_traps skill (aka, search). Checks for traps
1007     * on the spaces or in certain objects
1008     */
1009    
1010 root 1.8 int
1011     find_traps (object *pl, object *skill)
1012     {
1013     object *tmp, *tmp2;
1014     int i, expsum = 0, mflags;
1015     sint16 x, y;
1016 root 1.11 maptile *m;
1017 root 1.8
1018     /* First we search all around us for runes and traps, which are
1019     * all type RUNE
1020     */
1021 root 1.6
1022 root 1.8 for (i = 0; i < 9; i++)
1023     {
1024     x = pl->x + freearr_x[i];
1025     y = pl->y + freearr_y[i];
1026     m = pl->map;
1027    
1028     mflags = get_map_flags (m, &m, x, y, &x, &y);
1029     if (mflags & P_OUT_OF_MAP)
1030     continue;
1031    
1032     /* Check everything in the square for trapness */
1033 root 1.16 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above)
1034 root 1.8 {
1035    
1036     /* And now we'd better do an inventory traversal of each
1037     * of these objects' inventory
1038     * We can narrow this down a bit - no reason to search through
1039     * the players inventory or monsters for that matter.
1040     */
1041     if (tmp->type != PLAYER && !QUERY_FLAG (tmp, FLAG_MONSTER))
1042     {
1043     for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
1044     if (tmp2->type == RUNE || tmp2->type == TRAP)
1045     if (trap_see (pl, tmp2))
1046     {
1047     trap_show (tmp2, tmp);
1048     if (tmp2->stats.Cha > 1)
1049     {
1050     if (!tmp2->owner || tmp2->owner->type != PLAYER)
1051     expsum += calc_skill_exp (pl, tmp2, skill);
1052    
1053     tmp2->stats.Cha = 1; /* unhide the trap */
1054 root 1.6 }
1055 root 1.8 }
1056 root 1.6 }
1057 root 1.8 if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see (pl, tmp))
1058     {
1059     trap_show (tmp, tmp);
1060     if (tmp->stats.Cha > 1)
1061     {
1062     if (!tmp->owner || tmp->owner->type != PLAYER)
1063     expsum += calc_skill_exp (pl, tmp, skill);
1064     tmp->stats.Cha = 1; /* unhide the trap */
1065 root 1.6 }
1066     }
1067     }
1068 elmex 1.1 }
1069 root 1.8 new_draw_info (NDI_BLACK, 0, pl, "You search the area.");
1070     return expsum;
1071     }
1072 elmex 1.1
1073     /* remove_trap() - This skill will disarm any previously discovered trap
1074     * the algorithm is based (almost totally) on the old command_disarm() - b.t.
1075 root 1.8 */
1076    
1077     int
1078     remove_trap (object *op, int dir, object *skill)
1079     {
1080     object *tmp, *tmp2;
1081     int i, success = 0, mflags;
1082 root 1.11 maptile *m;
1083 root 1.8 sint16 x, y;
1084 elmex 1.1
1085 root 1.8 for (i = 0; i < 9; i++)
1086     {
1087     x = op->x + freearr_x[i];
1088     y = op->y + freearr_y[i];
1089     m = op->map;
1090    
1091     mflags = get_map_flags (m, &m, x, y, &x, &y);
1092     if (mflags & P_OUT_OF_MAP)
1093     continue;
1094    
1095     /* Check everything in the square for trapness */
1096 root 1.16 for (tmp = GET_MAP_OB (m, x, y); tmp != NULL; tmp = tmp->above)
1097 root 1.8 {
1098     /* And now we'd better do an inventory traversal of each
1099     * of these objects inventory. Like above, only
1100     * do this for interesting objects.
1101     */
1102    
1103     if (tmp->type != PLAYER && !QUERY_FLAG (tmp, FLAG_MONSTER))
1104     {
1105     for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
1106     if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1)
1107     {
1108     trap_show (tmp2, tmp);
1109     if (trap_disarm (op, tmp2, 1, skill) && (!tmp2->owner || tmp2->owner->type != PLAYER))
1110     {
1111     tmp->stats.exp = tmp->stats.Cha * tmp->level;
1112     success += calc_skill_exp (op, tmp2, skill);
1113     }
1114     }
1115 root 1.6 }
1116 root 1.8 if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1)
1117     {
1118     trap_show (tmp, tmp);
1119     if (trap_disarm (op, tmp, 1, skill) && (!tmp->owner || tmp->owner->type != PLAYER))
1120     {
1121     tmp->stats.exp = tmp->stats.Cha * tmp->level;
1122     success += calc_skill_exp (op, tmp, skill);
1123 root 1.6 }
1124     }
1125     }
1126 elmex 1.1 }
1127 root 1.8 return success;
1128 elmex 1.1 }
1129    
1130    
1131     /* pray() - when this skill is called from do_skill(), it allows
1132     * the player to regain lost grace points at a faster rate. -b.t.
1133     * This always returns 0 - return value is used by calling function
1134     * such that if it returns true, player gets exp in that skill. This
1135     * the effect here can be done on demand, we probably don't want to
1136     * give infinite exp by returning true in any cases.
1137     */
1138    
1139 root 1.8 int
1140     pray (object *pl, object *skill)
1141     {
1142     char buf[MAX_BUF];
1143     object *tmp;
1144    
1145     if (pl->type != PLAYER)
1146     return 0;
1147    
1148     strcpy (buf, "You pray.");
1149    
1150     /* Check all objects - we could stop at floor objects,
1151     * but if someone buries an altar, I don't see a problem with
1152     * going through all the objects, and it shouldn't be much slower
1153     * than extra checks on object attributes.
1154     */
1155     for (tmp = pl->below; tmp != NULL; tmp = tmp->below)
1156     {
1157     /* Only if the altar actually belongs to someone do you get special benefits */
1158     if (tmp && tmp->type == HOLY_ALTAR && tmp->other_arch)
1159     {
1160     sprintf (buf, "You pray over the %s.", &tmp->name);
1161     pray_at_altar (pl, tmp, skill);
1162     break; /* Only pray at one altar */
1163 root 1.6 }
1164 elmex 1.1 }
1165    
1166 root 1.8 new_draw_info (NDI_BLACK, 0, pl, buf);
1167    
1168     if (pl->stats.grace < pl->stats.maxgrace)
1169     {
1170     pl->stats.grace++;
1171     pl->last_grace = -1;
1172 elmex 1.1 }
1173 root 1.8 return 0;
1174 elmex 1.1 }
1175    
1176     /* This skill allows the player to regain a few sp or hp for a
1177     * brief period of concentration. No armour or weapons may be
1178     * wielded/applied for this to work. The amount of time needed
1179     * to concentrate and the # of points regained is dependant on
1180     * the level of the user. - b.t. thomas@astro.psu.edu
1181 root 1.8 */
1182 elmex 1.1
1183 root 1.8 void
1184     meditate (object *pl, object *skill)
1185     {
1186     object *tmp;
1187 elmex 1.1
1188 root 1.8 if (pl->type != PLAYER)
1189     return; /* players only */
1190 elmex 1.1
1191 root 1.8 /* check if pl has removed encumbering armour and weapons */
1192     if (QUERY_FLAG (pl, FLAG_READY_WEAPON) && (skill->level < 6))
1193     {
1194     new_draw_info (NDI_UNIQUE, 0, pl, "You can't concentrate while wielding a weapon!\n");
1195     return;
1196     }
1197     else
1198     {
1199     for (tmp = pl->inv; tmp; tmp = tmp->below)
1200 root 1.35 if (((tmp->type == ARMOUR && skill->level < 12)
1201 root 1.8 || (tmp->type == HELMET && skill->level < 10)
1202 root 1.35 || (tmp->type == SHIELD && skill->level < 6)
1203     || (tmp->type == BOOTS && skill->level < 4)
1204     || (tmp->type == GLOVES && skill->level < 2))
1205     && QUERY_FLAG (tmp, FLAG_APPLIED))
1206 root 1.8 {
1207     new_draw_info (NDI_UNIQUE, 0, pl, "You can't concentrate while wearing so much armour!\n");
1208     return;
1209 root 1.6 }
1210 elmex 1.1 }
1211    
1212 root 1.8 /* ok let's meditate! Spell points are regained first, then once
1213     * they are maxed we get back hp. Actual incrementing of values
1214     * is handled by the do_some_living() (in player.c). This way magical
1215     * bonuses for healing/sp regeneration are included properly
1216     * No matter what, we will eat up some playing time trying to
1217     * meditate. (see 'factor' variable for what sets the amount of time)
1218     */
1219    
1220     new_draw_info (NDI_BLACK, 0, pl, "You meditate.");
1221    
1222     if (pl->stats.sp < pl->stats.maxsp)
1223     {
1224     pl->stats.sp++;
1225     pl->last_sp = -1;
1226     }
1227     else if (pl->stats.hp < pl->stats.maxhp)
1228     {
1229     pl->stats.hp++;
1230     pl->last_heal = -1;
1231 elmex 1.1 }
1232     }
1233    
1234     /* write_note() - this routine allows players to inscribe messages in
1235     * ordinary 'books' (anything that is type BOOK). b.t.
1236     */
1237 root 1.8 static int
1238     write_note (object *pl, object *item, const char *msg, object *skill)
1239     {
1240     char buf[1024];
1241     object *newBook = NULL;
1242    
1243     /* a pair of sanity checks */
1244     if (!item || item->type != BOOK)
1245     return 0;
1246    
1247     if (!msg)
1248     {
1249     new_draw_info (NDI_UNIQUE, 0, pl, "No message to write!");
1250     new_draw_info_format (NDI_UNIQUE, 0, pl, "Usage: use_skill %s <message>", &skill->skill);
1251     return 0;
1252 elmex 1.1 }
1253 root 1.24
1254 root 1.8 if (strcasestr_local (msg, "endmsg"))
1255     {
1256     new_draw_info (NDI_UNIQUE, 0, pl, "Trying to cheat now are we?");
1257     return 0;
1258 elmex 1.1 }
1259    
1260 root 1.8 if (INVOKE_OBJECT (INSCRIBE_NOTE, item, ARG_PLAYER (pl->contr), ARG_STRING (msg), ARG_OBJECT (skill)))
1261     return strlen (msg);
1262    
1263     buf[0] = 0;
1264     if (!book_overflow (item->msg, msg, sizeof (buf)))
1265     { /* add msg string to book */
1266     if (item->msg)
1267     strcpy (buf, item->msg);
1268    
1269     strcat (buf, msg);
1270     strcat (buf, "\n"); /* new msg needs a LF */
1271     if (item->nrof > 1)
1272     {
1273 root 1.14 newBook = item->clone ();
1274 root 1.8 decrease_ob (item);
1275     esrv_send_item (pl, item);
1276     newBook->nrof = 1;
1277     newBook->msg = buf;
1278     newBook = insert_ob_in_ob (newBook, pl);
1279     esrv_send_item (pl, newBook);
1280     }
1281     else
1282     {
1283     item->msg = buf;
1284     /* This shouldn't be necessary - the object hasn't changed in any
1285     * visible way
1286     */
1287     /* esrv_send_item(pl, item); */
1288     }
1289 root 1.24
1290 root 1.8 new_draw_info_format (NDI_UNIQUE, 0, pl, "You write in the %s.", query_short_name (item));
1291 root 1.4 return strlen (msg);
1292 root 1.8 }
1293     else
1294     new_draw_info_format (NDI_UNIQUE, 0, pl, "Your message won't fit in the %s!", query_short_name (item));
1295 elmex 1.1
1296 root 1.8 return 0;
1297 elmex 1.1 }
1298    
1299     /* write_scroll() - this routine allows players to inscribe spell scrolls
1300     * of spells which they know. Backfire effects are possible with the
1301     * severity of the backlash correlated with the difficulty of the scroll
1302     * that is attempted. -b.t. thomas@astro.psu.edu
1303     */
1304    
1305 root 1.8 static int
1306     write_scroll (object *pl, object *scroll, object *skill)
1307     {
1308     int success = 0, confused = 0;
1309     object *newscroll, *chosen_spell, *tmp;
1310    
1311     /* this is a sanity check */
1312     if (scroll->type != SCROLL)
1313     {
1314     new_draw_info (NDI_UNIQUE, 0, pl, "A spell can only be inscribed into a scroll!");
1315     return 0;
1316     }
1317    
1318     /* Check if we are ready to attempt inscription */
1319 root 1.28 chosen_spell = pl->contr->ranged_ob;
1320     if (!chosen_spell || chosen_spell->type != SPELL)
1321 root 1.8 {
1322     new_draw_info (NDI_UNIQUE, 0, pl, "You need a spell readied in order to inscribe!");
1323     return 0;
1324     }
1325 root 1.24
1326 root 1.8 if (SP_level_spellpoint_cost (pl, chosen_spell, SPELL_GRACE) > pl->stats.grace)
1327     {
1328     new_draw_info_format (NDI_UNIQUE, 0, pl, "You don't have enough grace to write a scroll of %s.", &chosen_spell->name);
1329     return 0;
1330     }
1331 root 1.24
1332 root 1.8 if (SP_level_spellpoint_cost (pl, chosen_spell, SPELL_MANA) > pl->stats.sp)
1333     {
1334     new_draw_info_format (NDI_UNIQUE, 0, pl, "You don't have enough mana to write a scroll of %s.", &chosen_spell->name);
1335     return 0;
1336 elmex 1.1 }
1337 root 1.8
1338     /* if there is a spell already on the scroll then player could easily
1339     * accidently read it while trying to write the new one. give player
1340     * a 50% chance to overwrite spell at their own level
1341     */
1342     if ((scroll->stats.sp || scroll->inv) && random_roll (0, scroll->level * 2, pl, PREFER_LOW) > skill->level)
1343     {
1344     new_draw_info_format (NDI_UNIQUE, 0, pl, "Oops! You accidently read it while trying to write on it.");
1345     manual_apply (pl, scroll, 0);
1346     return 0;
1347 elmex 1.1 }
1348    
1349 root 1.8 /* ok, we are ready to try inscription */
1350     if (QUERY_FLAG (pl, FLAG_CONFUSED))
1351     confused = 1;
1352    
1353     /* Lost mana/grace no matter what */
1354     pl->stats.grace -= SP_level_spellpoint_cost (pl, chosen_spell, SPELL_GRACE);
1355     pl->stats.sp -= SP_level_spellpoint_cost (pl, chosen_spell, SPELL_MANA);
1356    
1357     if (random_roll (0, chosen_spell->level * 4 - 1, pl, PREFER_LOW) < skill->level)
1358     {
1359     if (scroll->nrof > 1)
1360     {
1361 root 1.14 newscroll = scroll->clone ();
1362 root 1.8 decrease_ob (scroll);
1363     newscroll->nrof = 1;
1364     }
1365     else
1366 root 1.24 newscroll = scroll;
1367 root 1.8
1368     if (!confused)
1369     {
1370     newscroll->level = MAX (skill->level, chosen_spell->level);
1371     new_draw_info (NDI_UNIQUE, 0, pl, "You succeed in writing a new scroll.");
1372     }
1373     else
1374     {
1375     chosen_spell = find_random_spell_in_ob (pl, NULL);
1376     if (!chosen_spell)
1377 root 1.6 return 0;
1378 elmex 1.1
1379 root 1.8 newscroll->level = MAX (skill->level, chosen_spell->level);
1380     new_draw_info (NDI_UNIQUE, 0, pl, "In your confused state, you write down some odd spell.");
1381     }
1382 elmex 1.1
1383 root 1.8 if (newscroll->inv)
1384 root 1.13 newscroll->inv->destroy ();
1385 root 1.8
1386 root 1.14 tmp = chosen_spell->clone ();
1387 root 1.8 insert_ob_in_ob (tmp, newscroll);
1388    
1389     /* Same code as from treasure.c - so they can better merge.
1390     * if players want to sell them, so be it.
1391     */
1392     newscroll->value = newscroll->arch->clone.value * newscroll->inv->value * (newscroll->level + 50) / (newscroll->inv->level + 50);
1393     newscroll->stats.exp = newscroll->value / 5;
1394    
1395     /* wait until finished manipulating the scroll before inserting it */
1396     if (newscroll == scroll)
1397     {
1398     /* Remove to correctly merge with other items which may exist in inventory */
1399 root 1.12 newscroll->remove ();
1400 root 1.8 esrv_del_item (pl->contr, newscroll->count);
1401     }
1402 root 1.24
1403 root 1.8 newscroll = insert_ob_in_ob (newscroll, pl);
1404     esrv_send_item (pl, newscroll);
1405     success = calc_skill_exp (pl, newscroll, skill);
1406     if (!confused)
1407     success *= 2;
1408     success = success * skill->level;
1409     return success;
1410    
1411     }
1412     else
1413     { /* Inscription has failed */
1414    
1415     if (chosen_spell->level > skill->level || confused)
1416     { /*backfire! */
1417     new_draw_info (NDI_UNIQUE, 0, pl, "Ouch! Your attempt to write a new scroll strains your mind!");
1418     if (random_roll (0, 1, pl, PREFER_LOW) == 1)
1419 root 1.18 pl->drain_specific_stat (4);
1420 root 1.8 else
1421     {
1422     confuse_player (pl, pl, 99);
1423     return (-30 * chosen_spell->level);
1424 root 1.6 }
1425 root 1.8 }
1426     else if (random_roll (0, pl->stats.Int - 1, pl, PREFER_HIGH) < 15)
1427     {
1428     new_draw_info (NDI_UNIQUE, 0, pl, "Your attempt to write a new scroll rattles your mind!");
1429     confuse_player (pl, pl, 99);
1430     }
1431     else
1432     new_draw_info (NDI_UNIQUE, 0, pl, "You fail to write a new scroll.");
1433 elmex 1.1 }
1434 root 1.18
1435 root 1.8 return 0;
1436 elmex 1.1 }
1437    
1438     /* write_on_item() - wrapper for write_note and write_scroll */
1439 root 1.8 int
1440     write_on_item (object *pl, const char *params, object *skill)
1441     {
1442     object *item;
1443     const char *string = params;
1444     int msgtype;
1445     archetype *skat;
1446    
1447     if (pl->type != PLAYER)
1448     return 0;
1449    
1450     if (!params)
1451     {
1452     params = "";
1453     string = params;
1454     }
1455 root 1.24
1456 root 1.8 skat = get_archetype_by_type_subtype (SKILL, SK_LITERACY);
1457    
1458     /* Need to be able to read before we can write! */
1459     if (!find_skill_by_name (pl, skat->clone.skill))
1460     {
1461     new_draw_info (NDI_UNIQUE, 0, pl, "You must learn to read before you can write!");
1462     return 0;
1463 elmex 1.1 }
1464    
1465 root 1.8 /* if there is a message then it goes in a book and no message means
1466     * write active spell into the scroll
1467     */
1468     msgtype = (string[0] != '\0') ? BOOK : SCROLL;
1469    
1470     /* find an item of correct type to write on */
1471     if (!(item = find_marked_object (pl)))
1472     {
1473     new_draw_info (NDI_UNIQUE, 0, pl, "You don't have any marked item to write on.");
1474     return 0;
1475 elmex 1.1 }
1476    
1477 root 1.8 if (QUERY_FLAG (item, FLAG_UNPAID))
1478     {
1479     new_draw_info (NDI_UNIQUE, 0, pl, "You had better pay for that before you write on it.");
1480     return 0;
1481 elmex 1.1 }
1482 root 1.8 if (msgtype != item->type)
1483     {
1484     new_draw_info_format (NDI_UNIQUE, 0, pl, "You have no %s to write on", msgtype == BOOK ? "book" : "scroll");
1485     return 0;
1486 elmex 1.1 }
1487    
1488 root 1.8 if (msgtype == SCROLL)
1489 root 1.24 return write_scroll (pl, item, skill);
1490 root 1.8 else if (msgtype == BOOK)
1491 root 1.24 return write_note (pl, item, string, skill);
1492    
1493 root 1.8 return 0;
1494 elmex 1.1 }
1495    
1496     /* find_throw_ob() - if we request an object, then
1497     * we search for it in the inventory of the owner (you've
1498     * got to be carrying something in order to throw it!).
1499     * If we didnt request an object, then the top object in inventory
1500     * (that is "throwable", ie no throwing your skills away!)
1501     * is the object of choice. Also check to see if object is
1502     * 'throwable' (ie not applied cursed obj, worn, etc).
1503     */
1504 root 1.8 static object *
1505     find_throw_ob (object *op, const char *request)
1506     {
1507     object *tmp;
1508    
1509     if (!op)
1510     { /* safety */
1511     LOG (llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1512     return (object *) NULL;
1513     }
1514    
1515     /* prefer marked item */
1516     tmp = find_marked_object (op);
1517     if (tmp != NULL)
1518     {
1519     /* can't toss invisible or inv-locked items */
1520     if (tmp->invisible || QUERY_FLAG (tmp, FLAG_INV_LOCKED))
1521     {
1522     tmp = NULL;
1523     }
1524     }
1525    
1526     /* look through the inventory */
1527     if (tmp == NULL)
1528     {
1529     for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
1530     {
1531     /* can't toss invisible or inv-locked items */
1532     if (tmp->invisible || QUERY_FLAG (tmp, FLAG_INV_LOCKED))
1533     continue;
1534     if (!request || !strcmp (query_name (tmp), request) || !strcmp (tmp->name, request))
1535     break;
1536 elmex 1.1 }
1537     }
1538    
1539 root 1.8 /* this should prevent us from throwing away
1540     * cursed items, worn armour, etc. Only weapons
1541     * can be thrown from 'hand'.
1542     */
1543     if (!tmp)
1544     return NULL;
1545    
1546     if (QUERY_FLAG (tmp, FLAG_APPLIED))
1547     {
1548     if (tmp->type != WEAPON)
1549     {
1550     new_draw_info_format (NDI_UNIQUE, 0, op, "You can't throw %s.", query_name (tmp));
1551     tmp = NULL;
1552     }
1553     else if (QUERY_FLAG (tmp, FLAG_CURSED) || QUERY_FLAG (tmp, FLAG_DAMNED))
1554     {
1555     new_draw_info_format (NDI_UNIQUE, 0, op, "The %s sticks to your hand!", query_name (tmp));
1556     tmp = NULL;
1557     }
1558     else
1559     {
1560     if (apply_special (op, tmp, AP_UNAPPLY | AP_NO_MERGE))
1561     {
1562     LOG (llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1563     tmp = NULL;
1564 root 1.6 }
1565     }
1566 root 1.8 }
1567     else if (QUERY_FLAG (tmp, FLAG_UNPAID))
1568     {
1569     new_draw_info_format (NDI_UNIQUE, 0, op, "You should pay for the %s first.", query_name (tmp));
1570     tmp = NULL;
1571 elmex 1.1 }
1572    
1573 root 1.8 if (tmp && QUERY_FLAG (tmp, FLAG_INV_LOCKED))
1574     {
1575     LOG (llevError, "BUG: find_throw_ob(): object is locked\n");
1576     tmp = NULL;
1577 elmex 1.1 }
1578 root 1.8 return tmp;
1579 elmex 1.1 }
1580    
1581     /* make_throw_ob() We construct the 'carrier' object in
1582     * which we will insert the object that is being thrown.
1583     * This combination becomes the 'thrown object'. -b.t.
1584     */
1585 root 1.8 static object *
1586     make_throw_ob (object *orig)
1587     {
1588     if (!orig)
1589     return NULL;
1590 elmex 1.1
1591 root 1.8 if (QUERY_FLAG (orig, FLAG_APPLIED))
1592     {
1593     LOG (llevError, "BUG: make_throw_ob(): ob is applied\n");
1594     /* insufficient workaround, but better than nothing */
1595     CLEAR_FLAG (orig, FLAG_APPLIED);
1596     }
1597 root 1.14
1598     object *toss_item = orig->clone ();
1599    
1600 root 1.8 toss_item->type = THROWN_OBJ;
1601     CLEAR_FLAG (toss_item, FLAG_CHANGING);
1602     toss_item->stats.dam = 0; /* default damage */
1603     insert_ob_in_ob (orig, toss_item);
1604     return toss_item;
1605 elmex 1.1 }
1606    
1607     /* do_throw() - op throws any object toss_item. This code
1608     * was borrowed from fire_bow.
1609     * Returns 1 if skill was successfully used, 0 if not
1610     */
1611 root 1.8 static int
1612     do_throw (object *op, object *part, object *toss_item, int dir, object *skill)
1613     {
1614     object *throw_ob = toss_item, *left = NULL;
1615     int eff_str = 0, maxc, str = op->stats.Str, dam = 0;
1616     int pause_f, weight_f = 0, mflags;
1617     float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1618 root 1.11 maptile *m;
1619 root 1.8 sint16 sx, sy;
1620    
1621     if (throw_ob == NULL)
1622     {
1623     if (op->type == PLAYER)
1624 root 1.10 new_draw_info (NDI_UNIQUE, 0, op, "You have nothing to throw.");
1625    
1626 root 1.8 return 0;
1627 elmex 1.1 }
1628 root 1.8 if (QUERY_FLAG (throw_ob, FLAG_STARTEQUIP))
1629     {
1630     if (op->type == PLAYER)
1631 root 1.10 new_draw_info (NDI_UNIQUE, 0, op, "The gods won't let you throw that.");
1632    
1633 root 1.8 return 0;
1634     }
1635    
1636     /* Because throwing effectiveness must be reduced by the
1637     * encumbrance of the thrower and weight of the object. THus,
1638     * we use the concept of 'effective strength' as defined below.
1639     */
1640    
1641     /* if str exceeds MAX_STAT (30, eg giants), lets assign a str_factor > 1 */
1642     if (str > MAX_STAT)
1643     {
1644     str_factor = (float) str / (float) MAX_STAT;
1645     str = MAX_STAT;
1646 elmex 1.1 }
1647    
1648 root 1.8 /* the more we carry, the less we can throw. Limit only on players */
1649     maxc = max_carry[str] * 1000;
1650     if (op->carrying > maxc && op->type == PLAYER)
1651     load_factor = (float) maxc / (float) op->carrying;
1652    
1653     /* lighter items are thrown harder, farther, faster */
1654     if (throw_ob->weight > 0)
1655     item_factor = (float) maxc / (float) (3.0 * throw_ob->weight);
1656     else
1657     { /* 0 or negative weight?!? Odd object, can't throw it */
1658     new_draw_info_format (NDI_UNIQUE, 0, op, "You can't throw %s.\n", query_name (throw_ob));
1659     return 0;
1660 elmex 1.1 }
1661 root 1.8
1662     eff_str = (int) (str * (load_factor < 1.0 ? load_factor : 1.0));
1663     eff_str = (int) ((float) eff_str * item_factor * str_factor);
1664    
1665     /* alas, arrays limit us to a value of MAX_STAT (30). Use str_factor to
1666     * account for super-strong throwers. */
1667     if (eff_str > MAX_STAT)
1668     eff_str = MAX_STAT;
1669 elmex 1.1
1670     #ifdef DEBUG_THROW
1671 root 1.8 LOG (llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
1672     LOG (llevDebug, " max_c=%d, item_f=%f, load_f=%f, str=%d\n", maxc, item_factor, load_factor, op->stats.Str);
1673     LOG (llevDebug, " str_factor=%f\n", str_factor);
1674     LOG (llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
1675 elmex 1.1 #endif
1676    
1677 root 1.8 /* 3 things here prevent a throw, you aimed at your feet, you
1678     * have no effective throwing strength, or you threw at something
1679     * that flying objects can't get through.
1680     */
1681     mflags = get_map_flags (part->map, &m, part->x + freearr_x[dir], part->y + freearr_y[dir], &sx, &sy);
1682    
1683     if (!dir || (eff_str <= 1) || (mflags & P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK (m, sx, sy) & MOVE_FLY_LOW))
1684     {
1685 root 1.20 /* bounces off 'wall', and drops to feet */
1686     throw_ob->insert_at (part, op);
1687 root 1.8
1688     if (op->type == PLAYER)
1689     {
1690     if (eff_str <= 1)
1691 root 1.20 new_draw_info_format (NDI_UNIQUE, 0, op, "Your load is so heavy you drop %s to the ground.", query_name (throw_ob));
1692 root 1.8 else if (!dir)
1693 root 1.20 new_draw_info_format (NDI_UNIQUE, 0, op, "You throw %s at the ground.", query_name (throw_ob));
1694 root 1.8 else
1695     new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way.");
1696 root 1.6 }
1697 root 1.20
1698 root 1.8 return 0;
1699     } /* if object can't be thrown */
1700    
1701     left = throw_ob; /* these are throwing objects left to the player */
1702    
1703     /* sometimes get_split_ob can't split an object (because op->nrof==0?)
1704     * and returns NULL. We must use 'left' then
1705     */
1706    
1707     if ((throw_ob = get_split_ob (throw_ob, 1)) == NULL)
1708     {
1709     throw_ob = left;
1710 root 1.12 left->remove ();
1711 root 1.8 if (op->type == PLAYER)
1712     esrv_del_item (op->contr, left->count);
1713     }
1714     else if (op->type == PLAYER)
1715     {
1716 root 1.10 if (left->destroyed ())
1717     esrv_del_item (op->contr, left->count);
1718 root 1.8 else
1719     esrv_update_item (UPD_NROF, op, left);
1720     }
1721    
1722     /* special case: throwing powdery substances like dust, dirt */
1723     if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST)
1724     {
1725     cast_dust (op, throw_ob, dir);
1726     return 1;
1727     }
1728    
1729     /* Make a thrown object -- insert real object in a 'carrier' object.
1730     * If unsuccessfull at making the "thrown_obj", we just reinsert
1731     * the original object back into inventory and exit
1732     */
1733     if ((toss_item = make_throw_ob (throw_ob)))
1734     {
1735     throw_ob = toss_item;
1736     throw_ob->skill = skill->skill;
1737 elmex 1.1 }
1738 root 1.8 else
1739     {
1740     insert_ob_in_ob (throw_ob, op);
1741     return 0;
1742 elmex 1.1 }
1743    
1744 root 1.15 throw_ob->set_owner (op);
1745 root 1.8 /* At some point in the attack code, the actual real object (op->inv)
1746     * becomes the hitter. As such, we need to make sure that has a proper
1747     * owner value so exp goes to the right place.
1748     */
1749 root 1.15 throw_ob->inv->set_owner (op);
1750 root 1.8 throw_ob->direction = dir;
1751    
1752     /* the damage bonus from the force of the throw */
1753     dam = (int) (str_factor * dam_bonus[eff_str]);
1754    
1755     /* Now, lets adjust the properties of the thrown_ob. */
1756    
1757     /* how far to fly */
1758     throw_ob->last_sp = (eff_str * 3) / 5;
1759    
1760     /* speed */
1761 root 1.19 throw_ob->set_speed (min (1.0, speed_bonus[eff_str] + 1.0) / 1.5); /* no faster than an arrow! */
1762 root 1.8
1763     /* item damage. Eff_str and item weight influence damage done */
1764     weight_f = (throw_ob->weight / 2000) > MAX_STAT ? MAX_STAT : (throw_ob->weight / 2000);
1765     throw_ob->stats.dam += (dam / 3) + dam_bonus[weight_f] + (throw_ob->weight / 15000) - 2;
1766    
1767     /* chance of breaking. Proportional to force used and weight of item */
1768     throw_ob->stats.food = (dam / 2) + (throw_ob->weight / 60000);
1769    
1770     /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
1771     throw_ob->stats.wc = 25 - dex_bonus[op->stats.Dex] - thaco_bonus[eff_str] - skill->level;
1772 elmex 1.1
1773 root 1.8 /* the properties of objects which are meant to be thrown (ie dart,
1774     * throwing knife, etc) will differ from ordinary items. Lets tailor
1775     * this stuff in here.
1776     */
1777    
1778     if (QUERY_FLAG (throw_ob->inv, FLAG_IS_THROWN))
1779     {
1780     throw_ob->last_sp += eff_str / 3; /* fly a little further */
1781     throw_ob->stats.dam += throw_ob->inv->stats.dam + throw_ob->magic + 2;
1782     throw_ob->stats.wc -= throw_ob->magic + throw_ob->inv->stats.wc;
1783     /* only throw objects get directional faces */
1784     if (GET_ANIM_ID (throw_ob) && NUM_ANIMATIONS (throw_ob))
1785     SET_ANIMATION (throw_ob, dir);
1786     }
1787     else
1788     {
1789 root 1.26 uint16 mat = throw_ob->materials;
1790    
1791 root 1.8 /* some materials will adjust properties.. */
1792 root 1.26 if (mat & M_LEATHER)
1793 root 1.8 {
1794     throw_ob->stats.dam -= 1;
1795     throw_ob->stats.food -= 10;
1796     }
1797 root 1.19
1798 root 1.26 if (mat & M_GLASS)
1799 root 1.8 throw_ob->stats.food += 60;
1800    
1801 root 1.26 if (mat & M_ORGANIC)
1802 root 1.8 {
1803     throw_ob->stats.dam -= 3;
1804     throw_ob->stats.food += 55;
1805     }
1806 root 1.19
1807 root 1.26 if (mat & M_PAPER || mat & M_CLOTH)
1808 root 1.8 {
1809     throw_ob->stats.dam -= 5;
1810     throw_ob->speed *= 0.8;
1811     throw_ob->stats.wc += 3;
1812     throw_ob->stats.food -= 30;
1813     }
1814 root 1.19
1815 root 1.8 /* light obj have more wind resistance, fly slower */
1816     if (throw_ob->weight > 500)
1817     throw_ob->speed *= 0.8;
1818 root 1.19
1819 root 1.8 if (throw_ob->weight > 50)
1820     throw_ob->speed *= 0.5;
1821     } /* else tailor thrown object */
1822    
1823     /* some limits, and safeties (needed?) */
1824     if (throw_ob->stats.dam < 0)
1825     throw_ob->stats.dam = 0;
1826     if (throw_ob->last_sp > eff_str)
1827     throw_ob->last_sp = eff_str;
1828     if (throw_ob->stats.food < 0)
1829     throw_ob->stats.food = 0;
1830     if (throw_ob->stats.food > 100)
1831     throw_ob->stats.food = 100;
1832     if (throw_ob->stats.wc > 30)
1833     throw_ob->stats.wc = 30;
1834    
1835     /* how long to pause the thrower. Higher values mean less pause */
1836     pause_f = ((2 * eff_str) / 3) + 20 + skill->level;
1837    
1838     /* Put a lower limit on this */
1839     if (pause_f < 10)
1840     pause_f = 10;
1841     if (pause_f > 100)
1842     pause_f = 100;
1843    
1844     /* Changed in 0.94.2 - the calculation before was really goofy.
1845     * In short summary, a throw can take anywhere between speed 5 and
1846     * speed 0.5
1847     */
1848     op->speed_left -= 50 / pause_f;
1849    
1850     throw_ob->speed_left = 0;
1851     throw_ob->map = part->map;
1852    
1853     throw_ob->move_type = MOVE_FLY_LOW;
1854     throw_ob->move_on = MOVE_FLY_LOW | MOVE_WALK;
1855 elmex 1.1
1856     #if 0
1857 root 1.8 /* need to put in a good sound for this */
1858     play_sound_map (op->map, op->x, op->y, SOUND_THROW_OBJ);
1859 elmex 1.1 #endif
1860 root 1.8
1861 elmex 1.1 /* Lauwenmark - Now we can call the associated script_throw event (if any) */
1862 root 1.8 INVOKE_OBJECT (THROW, throw_ob, ARG_OBJECT (op));
1863 elmex 1.1 #ifdef DEBUG_THROW
1864 root 1.8 LOG (llevDebug, " pause_f=%d \n", pause_f);
1865     LOG (llevDebug, " %s stats: wc=%d dam=%d dist=%d spd=%f break=%d\n",
1866     throw_ob->name, throw_ob->stats.wc, throw_ob->stats.dam, throw_ob->last_sp, throw_ob->speed, throw_ob->stats.food);
1867     LOG (llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
1868 elmex 1.1 #endif
1869 root 1.20
1870     throw_ob->insert_at (part, op);
1871 root 1.10
1872     if (!throw_ob->destroyed ())
1873 root 1.8 move_arrow (throw_ob);
1874 root 1.10
1875 root 1.8 return 1;
1876     }
1877    
1878     int
1879     skill_throw (object *op, object *part, int dir, const char *params, object *skill)
1880     {
1881     object *throw_ob;
1882    
1883     if (op->type == PLAYER)
1884     throw_ob = find_throw_ob (op, params);
1885     else
1886     throw_ob = find_mon_throw_ob (op);
1887 elmex 1.1
1888 root 1.8 return do_throw (op, part, throw_ob, dir, skill);
1889 elmex 1.1 }