ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/skills.C
Revision: 1.28
Committed: Mon Apr 30 04:25:30 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.27: +2 -3 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

File Contents

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