ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/skills.C
Revision: 1.38
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.37: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

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