Crossfire Server, Branches 1.12  R18729
skills.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_skills_c =
3  * "$Id: skills.c 18483 2012-10-29 19:00:44Z akirschbaum $";
4  */
5 /*
6  CrossFire, A Multiplayer game for X-windows
7 
8  Copyright (C) 2003-2006 Mark Wedel & Crossfire Development Team
9  Copyright (C) 1992 Frank Tore Johansen
10 
11  This program is free software; you can redistribute it and/or modify
12  it under the terms of the GNU General Public License as published by
13  the Free Software Foundation; either version 2 of the License, or
14  (at your option) any later version.
15 
16  This program is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  GNU General Public License for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with this program; if not, write to the Free Software
23  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 
25  The authors can be reached via e-mail to crossfire-devel@real-time.com
26 */
27 
33 #include <global.h>
34 #include <object.h>
35 #ifndef __CEXTRACT__
36 #include <sproto.h>
37 #endif
38 #include <living.h>
39 #include <skills.h>
40 #include <spells.h>
41 #include <book.h>
42 
57 static int adj_stealchance(object *op, object *victim, int roll) {
58  object *equip;
59 
60  if (!op || !victim || !roll)
61  return -1;
62 
63  /* Only prohibit stealing if the player does not have a free
64  * hand available and in fact does have hands.
65  */
66  if (op->type == PLAYER
67  && op->body_used[BODY_ARMS] <= 0
68  && op->body_info[BODY_ARMS]) {
70  "But you have no free hands to steal with!", NULL);
71  return -1;
72  }
73 
74  /* ADJUSTMENTS */
75 
76  /* Its harder to steal from hostile beings! */
77  if (!QUERY_FLAG(victim, FLAG_UNAGGRESSIVE))
78  roll = roll/2;
79 
80  /* Easier to steal from sleeping beings, or if the thief is
81  * unseen */
82  if (QUERY_FLAG(victim, FLAG_SLEEP))
83  roll = roll*3;
84  else if (op->invisible)
85  roll = roll*2;
86 
87  /* check stealing 'encumberance'. Having this equipment applied makes
88  * it quite a bit harder to steal.
89  */
90  for (equip = op->inv; equip; equip = equip->below) {
91  if (equip->type == WEAPON && QUERY_FLAG(equip, FLAG_APPLIED)) {
92  roll -= equip->weight/10000;
93  }
94  if (equip->type == BOW && QUERY_FLAG(equip, FLAG_APPLIED))
95  roll -= equip->weight/5000;
96  if (equip->type == SHIELD && QUERY_FLAG(equip, FLAG_APPLIED)) {
97  roll -= equip->weight/2000;
98  }
99  if (equip->type == ARMOUR && QUERY_FLAG(equip, FLAG_APPLIED))
100  roll -= equip->weight/5000;
101  if (equip->type == GLOVES && QUERY_FLAG(equip, FLAG_APPLIED))
102  roll -= equip->weight/100;
103  }
104  if (roll < 0)
105  roll = 0;
106  return roll;
107 }
108 
127 static int attempt_steal(object *op, object *who, object *skill) {
128  object *success = NULL, *tmp = NULL, *next;
129  int roll = 0, chance = 0, stats_value;
130  rv_vector rv;
131  char name[MAX_BUF];
132 
133  stats_value = ((who->stats.Dex+who->stats.Int)*3)/2;
134 
135  /* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them)
136  * they will try to prevent stealing if they can. Only unseen theives will
137  * have much chance of success.
138  */
139  if (op->type != PLAYER && QUERY_FLAG(op, FLAG_NO_STEAL)) {
140  if (can_detect_enemy(op, who, &rv)) {
141  npc_call_help(op);
144  "Your attempt is prevented!", NULL);
145  return 0;
146  } else /* help npc to detect thief next time by raising its wisdom */
147  op->stats.Wis += (op->stats.Int/5)+1;
148  if (op->stats.Wis > MAX_STAT)
149  op->stats.Wis = MAX_STAT;
150  }
151  if (op->type == PLAYER && QUERY_FLAG(op, FLAG_WIZ)) {
153  "You can't steal from the dungeon master!", NULL);
154  return 0;
155  }
156  if (op->type == PLAYER && who->type == PLAYER && settings.no_player_stealing) {
158  "You can't steal from other players!", NULL);
159  return 0;
160  }
161 
162 
163  /* Ok then, go thru their inventory, stealing */
164  for (tmp = op->inv; tmp != NULL; tmp = next) {
165  next = tmp->below;
166 
167  /* you can't steal worn items, starting items, wiz stuff,
168  * innate abilities, or items w/o a type. Generally
169  * speaking, the invisibility flag prevents experience or
170  * abilities from being stolen since these types are currently
171  * always invisible objects. I was implicit here so as to prevent
172  * future possible problems. -b.t.
173  * Flesh items generated w/ fix_flesh_item should have FLAG_NO_STEAL
174  * already -b.t.
175  */
176 
177  if (QUERY_FLAG(tmp, FLAG_WAS_WIZ)
178  || QUERY_FLAG(tmp, FLAG_APPLIED)
179  || !(tmp->type)
180  || tmp->type == EXPERIENCE
181  || tmp->type == SPELL
182  || QUERY_FLAG(tmp, FLAG_STARTEQUIP)
183  || QUERY_FLAG(tmp, FLAG_NO_STEAL)
184  || tmp->invisible)
185  continue;
186 
187  /* Okay, try stealing this item. Dependent on dexterity of thief,
188  * skill level, see the adj_stealroll fctn for more detail.
189  */
190 
191  roll = die_roll(2, 100, who, PREFER_LOW)/2; /* weighted 1-100 */
192 
193  if ((chance = adj_stealchance(who, op, (stats_value+skill->level*10-op->level*3))) == -1)
194  return 0;
195  else if (roll < chance) {
196  tag_t tmp_count = tmp->count;
197 
198  pick_up(who, tmp);
199  /* need to see if the player actually stole this item -
200  * if it is in the players inv, assume it is. This prevents
201  * abuses where the player can not carry the item, so just
202  * keeps stealing it over and over.
203  */
204  if (was_destroyed(tmp, tmp_count) || tmp->env != op) {
205  /* for players, play_sound: steals item */
206  success = tmp;
208  }
209  break;
210  }
211  } /* for loop looking for an item */
212 
213  if (!tmp) {
214  query_name(op, name, MAX_BUF);
216  "%s%s has nothing you can steal!",
217  "%s%s has nothing you can steal!",
218  op->type == PLAYER ? "" : "The ", name);
219  return 0;
220  }
221 
222  /* If you arent high enough level, you might get something BUT
223  * the victim will notice your stealing attempt. Ditto if you
224  * attempt to steal something heavy off them, they're bound to notice
225  */
226 
227  if ((roll >= skill->level)
228  || !chance
229  || (tmp && tmp->weight > (250*(random_roll(0, stats_value+skill->level*10-1, who, PREFER_LOW))))) {
230  /* victim figures out where the thief is! */
231  if (who->hide)
232  make_visible(who);
233 
234  if (op->type != PLAYER) {
235  /* The unaggressives look after themselves 8) */
236  if (who->type == PLAYER) {
237  npc_call_help(op);
238  query_name(op, name, MAX_BUF);
240  "%s notices your attempted pilfering!",
241  "%s notices your attempted pilfering!",
242  name);
243  }
245  /* all remaining npc items are guarded now. Set flag NO_STEAL
246  * on the victim.
247  */
248  SET_FLAG(op, FLAG_NO_STEAL);
249  } else { /* stealing from another player */
250  char buf[MAX_BUF];
251 
252  /* Notify the other player */
253  if (success && who->stats.Int > random_roll(0, 19, op, PREFER_LOW)) {
254  query_name(success, name, MAX_BUF);
255  snprintf(buf, sizeof(buf), "Your %s is missing!", name);
256  } else {
257  snprintf(buf, sizeof(buf), "Your pack feels strangely lighter.");
258  }
260  buf, buf);
261  if (!success) {
262  if (who->invisible) {
263  snprintf(buf, sizeof(buf), "you feel itchy fingers getting at your pack.");
264  } else {
265  query_name(who, name, MAX_BUF);
266  snprintf(buf, sizeof(buf), "%s looks very shifty.", name);
267  }
269  buf, buf);
270  }
271  } /* else stealing from another player */
272  /* play_sound("stop! thief!"); kindofthing */
273  } /* if you weren't 100% successful */
274  return success ? 1 : 0;
275 }
276 
277 
290 int steal(object *op, int dir, object *skill) {
291  object *tmp, *next;
292  sint16 x, y;
293  mapstruct *m;
294  int mflags;
295 
296  x = op->x+freearr_x[dir];
297  y = op->y+freearr_y[dir];
298 
299  if (dir == 0) {
300  /* Can't steal from ourself! */
301  return 0;
302  }
303 
304  m = op->map;
305  mflags = get_map_flags(m, &m, x, y, &x, &y);
306  /* Out of map - can't do it. If nothing alive on this space,
307  * don't need to look any further.
308  */
309  if ((mflags&P_OUT_OF_MAP) || !(mflags&P_IS_ALIVE))
310  return 0;
311 
312  /* If player can't move onto the space, can't steal from it. */
313  if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y)))
314  return 0;
315 
316  /* Find the topmost object at this spot */
317  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above)
318  ;
319 
320  /* For all the stacked objects at this point, attempt a steal */
321  for (; tmp != NULL; tmp = next) {
322  next = tmp->below;
323  /* Minor hack--for multi square beings - make sure we get
324  * the 'head' coz 'tail' objects have no inventory! - b.t.
325  */
326  if (tmp->head)
327  tmp = tmp->head;
328 
329  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER))
330  continue;
331 
332  /* do not reveal hidden DMs */
333  if (tmp->type == PLAYER && QUERY_FLAG(tmp, FLAG_WIZ) && tmp->contr->hidden)
334  continue;
335  if (attempt_steal(tmp, op, skill)) {
336  if (tmp->type == PLAYER) /* no xp for stealing from another player */
337  return 0;
338 
339  /* no xp for stealing from pets (of players) */
340  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
341  object *owner = get_owner(tmp);
342  if (owner != NULL && owner->type == PLAYER)
343  return 0;
344  }
345 
346  return (calc_skill_exp(op, tmp, skill));
347  }
348  }
349  return 0;
350 }
351 
366 static int attempt_pick_lock(object *door, object *pl, object *skill) {
367  int difficulty = pl->map->difficulty ? pl->map->difficulty : 0;
368  int success = 0, number; /* did we get anything? */
369 
370  /* Try to pick the lock on this item (doors only for now).
371  * Dependent on dexterity/skill SK_level of the player and
372  * the map level difficulty.
373  */
374  number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2;
375  if (number < (pl->stats.Dex+skill->level-difficulty)) {
376  remove_door(door);
377  success = 1;
378  } else if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) { /* set off any traps? */
379  spring_trap(door->inv, pl);
380  }
381  return success;
382 }
383 
384 
400 int pick_lock(object *pl, int dir, object *skill) {
401  object *tmp;
402  int x = pl->x+freearr_x[dir];
403  int y = pl->y+freearr_y[dir];
404 
405  if (!dir)
406  dir = pl->facing;
407 
408  /* For all the stacked objects at this point find a door*/
409  if (out_of_map(pl->map, x, y)) {
411  "There is no lock there.", NULL);
412  return 0;
413  }
414 
415  for (tmp = GET_MAP_OB(pl->map, x, y); tmp; tmp = tmp->above)
416  if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
417  break;
418 
419  if (!tmp) {
421  "There is no lock there.", NULL);
422  return 0;
423  }
424  if (tmp->type == LOCKED_DOOR) {
426  "You can't pick that lock!", NULL);
427  return 0;
428  }
429 
430  if (!tmp->move_block) {
432  "The door has no lock!", NULL);
433  return 0;
434  }
435 
436  if (attempt_pick_lock(tmp, pl, skill)) {
438  "You pick the lock.", NULL);
439  return calc_skill_exp(pl, NULL, skill);
440  } else {
442  "You fail to pick the lock.", NULL);
443  return 0;
444  }
445 }
446 
447 
467 static int attempt_hide(object *op, object *skill) {
468  int number, difficulty = op->map->difficulty;
469  int terrain = hideability(op);
470 
471  if (terrain < -10) /* not enough cover here */
472  return 0;
473 
474  /* Hiding success and duration dependant on skill level,
475  * op->stats.Dex, map difficulty and terrain.
476  */
477 
478  number = (die_roll(2, 25, op, PREFER_LOW)-2)/2;
479  if (!stand_near_hostile(op) && (number < (op->stats.Dex+skill->level+terrain-difficulty))) {
480  op->invisible += 100; /* set the level of 'hiddeness' */
481  if (op->type == PLAYER)
482  op->contr->tmp_invis = 1;
483  op->hide = 1;
484  return 1;
485  }
486  return 0;
487 }
488 
498 int hide(object *op, object *skill) {
499  /* the preliminaries -- Can we really hide now? */
500  /* this keeps monsters from using invisibilty spells and hiding */
501 
502  if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
504  "You don't need to hide while invisible!", NULL);
505  return 0;
506  } else if (!op->hide && op->invisible > 0 && op->type == PLAYER) {
508  "Your attempt to hide breaks the invisibility spell!", NULL);
509  make_visible(op);
510  }
511 
512  if (op->invisible > (50*skill->level)) {
514  "You are as hidden as you can get.", NULL);
515  return 0;
516  }
517 
518  if (attempt_hide(op, skill)) {
520  "You hide in the shadows.", NULL);
522  return calc_skill_exp(op, NULL, skill);
523  }
525  "You fail to conceal yourself.", NULL);
526  return 0;
527 }
528 
529 
536 static void stop_jump(object *pl) {
537  fix_object(pl);
538  insert_ob_in_map(pl, pl->map, pl, 0);
539 }
540 
554 static int attempt_jump(object *pl, int dir, int spaces, object *skill) {
555  object *tmp;
556  int i, exp = 0, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
557  sint16 x, y;
558  mapstruct *m;
559 
560  /* Jump loop. Go through spaces opject wants to jump. Halt the
561  * jump if a wall or creature is in the way. We set FLY_LOW
562  * temporarily to allow player to aviod exits/archs that are not
563  * move_on/off fly_low. This will also prevent pickup of objects
564  * while jumping over them.
565  */
566 
567  remove_ob(pl);
568 
569  /*
570  * I don't think this is actually needed - all the movement
571  * code is handled in this function, and I don't see anyplace
572  * that cares about the move_type being flying.
573  */
574  pl->move_type |= MOVE_FLY_LOW;
575 
576  for (i = 0; i <= spaces; i++) {
577  x = pl->x+dx;
578  y = pl->y+dy;
579  m = pl->map;
580 
581  mflags = get_map_flags(m, &m, x, y, &x, &y);
582 
583  if (mflags&P_OUT_OF_MAP) {
584  (void)stop_jump(pl);
585  return 0;
586  }
587  if (OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, x, y))) {
589  "Your jump is blocked.", NULL);
590  stop_jump(pl);
591  return 0;
592  }
593 
594  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
595  /* Jump into creature */
596  if (QUERY_FLAG(tmp, FLAG_MONSTER)
597  || (tmp->type == PLAYER && (!QUERY_FLAG(tmp, FLAG_WIZ) || !tmp->contr->hidden))) {
599  "You jump into %s%s.",
600  "You jump into %s%s.",
601  tmp->type == PLAYER ? "" : "the ", tmp->name);
602 
603  if (tmp->type != PLAYER
604  || (pl->type == PLAYER && pl->contr->party == NULL)
605  || (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
606  exp = skill_attack(tmp, pl, pl->facing, "kicked", skill); /* pl makes an attack */
607 
608  stop_jump(pl);
609  return exp; /* note that calc_skill_exp() is already called by skill_attack() */
610  }
611  /* If the space has fly on set (no matter what the space is),
612  * we should get the effects - after all, the player is
613  * effectively flying.
614  */
615  if (tmp->move_on&MOVE_FLY_LOW) {
616  pl->x = x;
617  pl->y = y;
618  pl->map = m;
619  if (pl->contr)
620  esrv_map_scroll(&pl->contr->socket, dx, dy);
621  stop_jump(pl);
622  return calc_skill_exp(pl, NULL, skill);
623  }
624  }
625  pl->x = x;
626  pl->y = y;
627  pl->map = m;
628  if (pl->contr)
629  esrv_map_scroll(&pl->contr->socket, dx, dy);
630  }
631  stop_jump(pl);
632  return calc_skill_exp(pl, NULL, skill);
633 }
634 
651 int jump(object *pl, int dir, object *skill) {
652  int spaces = 0, stats;
653  int str = pl->stats.Str;
654  int dex = pl->stats.Dex;
655 
656  dex = dex ? dex : 15;
657  str = str ? str : 10;
658 
659  stats = str*str*str*dex*skill->level;
660 
661  if (pl->carrying != 0) /* don't want div by zero !! */
662  spaces = (int)(stats/pl->carrying);
663  else
664  spaces = 2; /* pl has no objects - gets the far jump */
665 
666  if (spaces > 2)
667  spaces = 2;
668  else if (spaces == 0) {
670  "You are carrying too much weight to jump.", NULL);
671  return 0;
672  }
673  return attempt_jump(pl, dir, spaces, skill);
674 }
675 
686 static int do_skill_detect_curse(object *pl, object *skill) {
687  object *tmp;
688  int success = 0;
689 
690  for (tmp = pl->inv; tmp; tmp = tmp->below)
691  if (!tmp->invisible
692  && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
693  && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)
694  && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
695  && tmp->item_power < skill->level) {
697  esrv_update_item(UPD_FLAGS, pl, tmp);
698  success += calc_skill_exp(pl, tmp, skill);
699  }
700 
701  /* Check ground, too, but only objects the player could pick up. Cauldrons are exceptions,
702  * you definitely want to know if they are cursed */
703  for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
704  if ((can_pick(pl, tmp) || QUERY_FLAG(tmp, FLAG_IS_CAULDRON))
705  && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
706  && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)
707  && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
708  && tmp->item_power < skill->level) {
710  esrv_update_item(UPD_FLAGS, pl, tmp);
711  success += calc_skill_exp(pl, tmp, skill);
712  }
713 
714  return success;
715 }
716 
727 static int do_skill_detect_magic(object *pl, object *skill) {
728  object *tmp;
729  int success = 0;
730 
731  for (tmp = pl->inv; tmp; tmp = tmp->below)
732  if (!tmp->invisible
733  && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
735  && (is_magical(tmp))
736  && tmp->item_power < skill->level) {
738  esrv_update_item(UPD_FLAGS, pl, tmp);
739  success += calc_skill_exp(pl, tmp, skill);
740  }
741 
742  /* Check ground, too, but like above, only if the object can be picked up*/
743  for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
744  if (can_pick(pl, tmp)
745  && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
747  && (is_magical(tmp)) && tmp->item_power < skill->level) {
749  esrv_update_item(UPD_FLAGS, pl, tmp);
750  success += calc_skill_exp(pl, tmp, skill);
751  }
752 
753  return success;
754 }
755 
771 static int do_skill_ident2(object *tmp, object *pl, int obj_class, object *skill) {
772  int success = 0, chance, ip;
773  int skill_value = skill->level*pl->stats.Int ? pl->stats.Int : 10;
774 
775  if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)
777  && need_identify(tmp)
778  && !tmp->invisible
779  && tmp->type == obj_class) {
780  ip = tmp->magic;
781  if (tmp->item_power > ip)
782  ip = tmp->item_power;
783 
784  chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
785  if (skill_value >= chance) {
786  identify(tmp);
787  if (pl->type == PLAYER) {
788  char desc[MAX_BUF];
789 
791  "You identify %s.",
792  "You identify %s.",
793  ob_describe(tmp, pl, desc, sizeof(desc)));
794  if (tmp->msg) {
796  "The item has a story:\n%s",
797  "The item has a story:\n%s",
798  tmp->msg);
799 
800  }
801  }
802  success += calc_skill_exp(pl, tmp, skill);
803  } else
805  }
806  return success;
807 }
808 
821 static int do_skill_ident(object *pl, int obj_class, object *skill) {
822  object *tmp;
823  int success = 0, area, i;
824 
825  /* check the player */
826  for (tmp = pl->inv; tmp; tmp = tmp->below)
827  success += do_skill_ident2(tmp, pl, obj_class, skill);
828 
829  /* check the ground */
830  /* Altered to allow ident skills to increase in area with
831  * experience. -- Aaron Baugher
832  */
833 
834  if (skill->level > 64) { /* Adjust these levels? */
835  area = 49;
836  } else if (skill->level > 16) {
837  area = 25;
838  } else if (skill->level > 4) {
839  area = 9;
840  } else {
841  area = 1;
842  }
843 
844  for (i = 0; i < area; i++) {
845  sint16 x = pl->x+freearr_x[i];
846  sint16 y = pl->y+freearr_y[i];
847  mapstruct *m = pl->map;
848  int mflags;
849 
850  mflags = get_map_flags(m, &m, x, y, &x, &y);
851  if (mflags&P_OUT_OF_MAP)
852  continue;
853 
854  if (can_see_monsterP(m, pl->x, pl->y, i)) {
855  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
856  success += do_skill_ident2(tmp, pl, obj_class, skill);
857  }
858  }
859  }
860  return success;
861 }
862 
872 int skill_ident(object *pl, object *skill) {
873  int success = 0;
874 
875  if (pl->type != PLAYER)
876  return 0; /* only players will skill-identify */
877 
879  "You look at the objects nearby...", NULL);
880 
881  switch (skill->subtype) {
882  case SK_SMITHERY:
883  success += do_skill_ident(pl, WEAPON, skill)
884  +do_skill_ident(pl, ARMOUR, skill)
885  +do_skill_ident(pl, BRACERS, skill)
886  +do_skill_ident(pl, CLOAK, skill)
887  +do_skill_ident(pl, BOOTS, skill)
888  +do_skill_ident(pl, SHIELD, skill)
889  +do_skill_ident(pl, GIRDLE, skill)
890  +do_skill_ident(pl, HELMET, skill)
891  +do_skill_ident(pl, GLOVES, skill);
892  break;
893 
894  case SK_BOWYER:
895  success += do_skill_ident(pl, BOW, skill)
896  +do_skill_ident(pl, ARROW, skill);
897  break;
898 
899  case SK_ALCHEMY:
900  success += do_skill_ident(pl, POTION, skill)
901  +do_skill_ident(pl, POISON, skill)
902  +do_skill_ident(pl, CONTAINER, skill)
903  +do_skill_ident(pl, DRINK, skill)
904  +do_skill_ident(pl, INORGANIC, skill);
905  break;
906 
907  case SK_WOODSMAN:
908  success += do_skill_ident(pl, FOOD, skill)
909  +do_skill_ident(pl, DRINK, skill)
910  +do_skill_ident(pl, FLESH, skill);
911  break;
912 
913  case SK_JEWELER:
914  success += do_skill_ident(pl, GEM, skill)
915  +do_skill_ident(pl, RING, skill)
916  +do_skill_ident(pl, AMULET, skill);
917  break;
918 
919  case SK_LITERACY:
920  success += do_skill_ident(pl, SPELLBOOK, skill)
921  +do_skill_ident(pl, SCROLL, skill)
922  +do_skill_ident(pl, BOOK, skill);
923  break;
924 
925  case SK_THAUMATURGY:
926  success += do_skill_ident(pl, WAND, skill)
927  +do_skill_ident(pl, ROD, skill)
928  +do_skill_ident(pl, HORN, skill);
929  break;
930 
931  case SK_DET_CURSE:
932  success = do_skill_detect_curse(pl, skill);
933  if (success)
935  "...and discover cursed items!", NULL);
936  break;
937 
938  case SK_DET_MAGIC:
939  success = do_skill_detect_magic(pl, skill);
940  if (success)
942  "...and discover items imbued with mystic forces!", NULL);
943  break;
944 
945  default:
946  LOG(llevError, "Error: bad call to skill_ident()\n");
947  return 0;
948  break;
949  }
950  if (!success) {
952  "...and learn nothing more.", NULL);
953  }
954  return success;
955 }
956 
957 
976 int use_oratory(object *pl, int dir, object *skill) {
977  sint16 x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
978  int mflags, chance;
979  object *tmp;
980  mapstruct *m;
981  char name[MAX_BUF];
982 
983  if (pl->type != PLAYER)
984  return 0; /* only players use this skill */
985  m = pl->map;
986  mflags = get_map_flags(m, &m, x, y, &x, &y);
987  if (mflags&P_OUT_OF_MAP)
988  return 0;
989 
990  /* Save some processing - we have the flag already anyways
991  */
992  if (!(mflags&P_IS_ALIVE)) {
994  "There is nothing to orate to.", NULL);
995  return 0;
996  }
997 
998  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
999  /* can't persuade players - return because there is nothing else
1000  * on that space to charm. Same for multi space monsters and
1001  * special monsters - we don't allow them to be charmed, and there
1002  * is no reason to do further processing since they should be the
1003  * only monster on the space.
1004  */
1005  if (tmp->type == PLAYER)
1006  return 0;
1007  if (tmp->more || tmp->head)
1008  return 0;
1009  if (tmp->msg)
1010  return 0;
1011 
1012  if (QUERY_FLAG(tmp, FLAG_MONSTER))
1013  break;
1014  }
1015 
1016  if (!tmp) {
1018  "There is nothing to orate to.", NULL);
1019  return 0;
1020  }
1021 
1022  query_name(tmp, name, MAX_BUF);
1024  "You orate to the %s.",
1025  "You orate to the %s.",
1026  name);
1027 
1028  /* the following conditions limit who may be 'charmed' */
1029 
1030  /* it's hostile! */
1031  if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1032  query_name(tmp, name, MAX_BUF);
1034  "Too bad the %s isn't listening!",
1035  "Too bad the %s isn't listening!",
1036  name);
1037  return 0;
1038  }
1039 
1040  /* it's already allied! */
1041  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && (tmp->attack_movement == PETMOVE)) {
1042  if (get_owner(tmp) == pl) {
1044  "Your follower loves your speech.", NULL);
1045  return 0;
1046  } else if (skill->level > tmp->level) {
1047  /* you steal the follower. Perhaps we should really look at the
1048  * level of the owner above?
1049  */
1050  set_owner(tmp, pl);
1051  query_name(tmp, name, MAX_BUF);
1053  "You convince the %s to follow you instead!",
1054  "You convince the %s to follow you instead!",
1055  name);
1056 
1057  /* Abuse fix - don't give exp since this can otherwise
1058  * be used by a couple players to gets lots of exp.
1059  */
1060  return 0;
1061  } else {
1062  /* In this case, you can't steal it from the other player */
1063  return 0;
1064  }
1065  } /* Creature was already a pet of someone */
1066 
1067  chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
1068 
1069  /* Ok, got a 'sucker' lets try to make them a follower */
1070  if (chance > 0 && tmp->level < (random_roll(0, chance-1, pl, PREFER_HIGH)-1)) {
1071  query_name(tmp, name, MAX_BUF);
1073  "You convince the %s to become your follower.",
1074  "You convince the %s to become your follower.",
1075  name);
1076 
1077  set_owner(tmp, pl);
1078  tmp->stats.exp = 0;
1079  add_friendly_object(tmp);
1080  SET_FLAG(tmp, FLAG_FRIENDLY);
1081  tmp->attack_movement = PETMOVE;
1082  FREE_AND_COPY(tmp->skill, skill->skill);
1083  return calc_skill_exp(pl, tmp, skill);
1084  }
1085  /* Charm failed. Creature may be angry now */
1086  else if ((skill->level+((pl->stats.Cha-10)/2)) < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
1087  query_name(tmp, name, MAX_BUF);
1089  "Your speech angers the %s!",
1090  "Your speech angers the %s!",
1091  name);
1092 
1093  if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1094  CLEAR_FLAG(tmp, FLAG_FRIENDLY);
1096  tmp->attack_movement = 0; /* needed? */
1097  }
1099  }
1100  return 0; /* Fall through - if we get here, we didn't charm anything */
1101 }
1102 
1123 int singing(object *pl, int dir, object *skill) {
1124  int i, exp = 0, chance, mflags;
1125  object *tmp;
1126  mapstruct *m;
1127  sint16 x, y;
1128  char name[MAX_BUF];
1129 
1130  if (pl->type != PLAYER)
1131  return 0; /* only players use this skill */
1132 
1134  "You sing", NULL);
1135  for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
1136  x = pl->x+freearr_x[i];
1137  y = pl->y+freearr_y[i];
1138  m = pl->map;
1139 
1140  mflags = get_map_flags(m, &m, x, y, &x, &y);
1141  if (mflags&P_OUT_OF_MAP)
1142  continue;
1143  if (!(mflags&P_IS_ALIVE))
1144  continue;
1145 
1146  for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
1147  if (QUERY_FLAG(tmp, FLAG_MONSTER))
1148  break;
1149  /* can't affect players */
1150  if (tmp->type == PLAYER)
1151  break;
1152  }
1153 
1154  /* Whole bunch of checks to see if this is a type of monster that would
1155  * listen to singing.
1156  */
1157  if (tmp
1158  && QUERY_FLAG(tmp, FLAG_MONSTER)
1159  && !QUERY_FLAG(tmp, FLAG_NO_STEAL) /* Been charmed or abused before */
1160  && !QUERY_FLAG(tmp, FLAG_SPLITTING) /* no ears */
1161  && !QUERY_FLAG(tmp, FLAG_HITBACK) /* was here before */
1162  && (tmp->level <= skill->level)
1163  && (!tmp->head)
1164  && !QUERY_FLAG(tmp, FLAG_UNDEAD)
1165  && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) /* already calm */
1166  && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) { /* already calm */
1167  /* stealing isn't really related (although, maybe it should
1168  * be). This is mainly to prevent singing to the same monster
1169  * over and over again and getting exp for it.
1170  */
1171  chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
1172  if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
1174  query_name(tmp, name, MAX_BUF);
1176  "You calm down the %s",
1177  "You calm down the %s",
1178  name);
1179 
1180  /* Give exp only if they are not aware */
1181  if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
1182  exp += calc_skill_exp(pl, tmp, skill);
1183  SET_FLAG(tmp, FLAG_NO_STEAL);
1184  } else {
1185  query_name(tmp, name, MAX_BUF);
1187  "Too bad the %s isn't listening!",
1188  "Too bad the %s isn't listening!",
1189  name);
1190  SET_FLAG(tmp, FLAG_NO_STEAL);
1191  }
1192  }
1193  }
1194  return exp;
1195 }
1196 
1207 int find_traps(object *pl, object *skill) {
1208  object *tmp, *tmp2;
1209  int i, expsum = 0, mflags;
1210  sint16 x, y;
1211  mapstruct *m;
1212 
1213  /* First we search all around us for runes and traps, which are
1214  * all type RUNE
1215  */
1216 
1217  for (i = 0; i < 9; i++) {
1218  x = pl->x+freearr_x[i];
1219  y = pl->y+freearr_y[i];
1220  m = pl->map;
1221 
1222  mflags = get_map_flags(m, &m, x, y, &x, &y);
1223  if (mflags&P_OUT_OF_MAP)
1224  continue;
1225 
1226  /* Check everything in the square for trapness */
1227  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
1228  /* And now we'd better do an inventory traversal of each
1229  * of these objects' inventory
1230  * We can narrow this down a bit - no reason to search through
1231  * the players inventory or monsters for that matter.
1232  */
1233  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1234  for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
1235  if (tmp2->type == RUNE || tmp2->type == TRAP)
1236  if (trap_see(pl, tmp2)) {
1237  trap_show(tmp2, tmp);
1238  if (tmp2->stats.Cha > 1) {
1239  if (!tmp2->owner || tmp2->owner->type != PLAYER)
1240  expsum += calc_skill_exp(pl, tmp2, skill);
1241 
1242  tmp2->stats.Cha = 1; /* unhide the trap */
1243  }
1244  }
1245  }
1246  if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
1247  trap_show(tmp, tmp);
1248  if (tmp->stats.Cha > 1) {
1249  if (!tmp->owner || tmp->owner->type != PLAYER)
1250  expsum += calc_skill_exp(pl, tmp, skill);
1251  tmp->stats.Cha = 1; /* unhide the trap */
1252  }
1253  }
1254  }
1255  }
1257  "You search the area.", NULL);
1258  return expsum;
1259 }
1260 
1272 int remove_trap(object *op, object *skill) {
1273  object *tmp, *tmp2;
1274  int i, success = 0, mflags;
1275  mapstruct *m;
1276  sint16 x, y;
1277 
1278  for (i = 0; i < 9; i++) {
1279  x = op->x+freearr_x[i];
1280  y = op->y+freearr_y[i];
1281  m = op->map;
1282 
1283  mflags = get_map_flags(m, &m, x, y, &x, &y);
1284  if (mflags&P_OUT_OF_MAP)
1285  continue;
1286 
1287  /* Check everything in the square for trapness */
1288  for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
1289  /* And now we'd better do an inventory traversal of each
1290  * of these objects inventory. Like above, only
1291  * do this for interesting objects.
1292  */
1293 
1294  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1295  for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
1296  if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
1297  trap_show(tmp2, tmp);
1298  if (trap_disarm(op, tmp2, 1, skill) && (!tmp2->owner || tmp2->owner->type != PLAYER)) {
1299  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1300  success += calc_skill_exp(op, tmp2, skill);
1301  } else {
1302  /* Can't continue to disarm after failure */
1303  return success;
1304  }
1305  }
1306  }
1307  if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
1308  trap_show(tmp, tmp);
1309  if (trap_disarm(op, tmp, 1, skill) && (!tmp->owner || tmp->owner->type != PLAYER)) {
1310  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1311  success += calc_skill_exp(op, tmp, skill);
1312  } else {
1313  /* Can't continue to disarm after failure */
1314  return success;
1315  }
1316  }
1317  }
1318  }
1319  return success;
1320 }
1321 
1322 
1341 int pray(object *pl, object *skill) {
1342  char buf[MAX_BUF];
1343  object *tmp;
1344 
1345  if (pl->type != PLAYER)
1346  return 0;
1347 
1348  snprintf(buf, sizeof(buf), "You pray.");
1349 
1350  /* Check all objects - we could stop at floor objects,
1351  * but if someone buries an altar, I don't see a problem with
1352  * going through all the objects, and it shouldn't be much slower
1353  * than extra checks on object attributes.
1354  */
1355  for (tmp = pl->below; tmp != NULL; tmp = tmp->below) {
1356  /* Only if the altar actually belongs to someone do you get special benefits */
1357  if (tmp && tmp->type == HOLY_ALTAR && tmp->other_arch) {
1358  snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
1359  pray_at_altar(pl, tmp, skill);
1360  break; /* Only pray at one altar */
1361  }
1362  }
1363 
1365  buf, buf);
1366 
1367  if (pl->stats.grace < pl->stats.maxgrace) {
1368  pl->stats.grace++;
1369  pl->last_grace = -1;
1370  }
1371  return 0;
1372 }
1373 
1390 void meditate(object *pl, object *skill) {
1391  object *tmp;
1392 
1393  if (pl->type != PLAYER)
1394  return; /* players only */
1395 
1396  /* check if pl has removed encumbering armour and weapons */
1397  if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && (skill->level < 6)) {
1399  "You can't concentrate while wielding a weapon!", NULL);
1400  return;
1401  } else {
1402  for (tmp = pl->inv; tmp; tmp = tmp->below)
1403  if (((tmp->type == ARMOUR && skill->level < 12)
1404  || (tmp->type == HELMET && skill->level < 10)
1405  || (tmp->type == SHIELD && skill->level < 6)
1406  || (tmp->type == BOOTS && skill->level < 4)
1407  || (tmp->type == GLOVES && skill->level < 2))
1408  && QUERY_FLAG(tmp, FLAG_APPLIED)) {
1410  "You can't concentrate while wearing so much armour!", NULL);
1411  return;
1412  }
1413  }
1414 
1415  /* ok let's meditate! Spell points are regained first, then once
1416  * they are maxed we get back hp. Actual incrementing of values
1417  * is handled by the do_some_living() (in player.c). This way magical
1418  * bonuses for healing/sp regeneration are included properly
1419  * No matter what, we will eat up some playing time trying to
1420  * meditate. (see 'factor' variable for what sets the amount of time)
1421  */
1422 
1424  "You meditate.", NULL);
1425 
1426  if (pl->stats.sp < pl->stats.maxsp) {
1427  pl->stats.sp++;
1428  pl->last_sp = -1;
1429  } else if (pl->stats.hp < pl->stats.maxhp) {
1430  pl->stats.hp++;
1431  pl->last_heal = -1;
1432  }
1433 }
1434 
1451 static int write_note(object *pl, object *item, const char *msg, object *skill) {
1452  char buf[BOOK_BUF];
1453  object *newBook = NULL;
1454 
1455  /* a pair of sanity checks */
1456  if (!item || item->type != BOOK)
1457  return 0;
1458 
1459  if (!msg) {
1461  "No message to write!\nUsage: use_skill %s <message>",
1462  "No message to write!\nUsage: use_skill %s <message>",
1463  skill->skill);
1464  return 0;
1465  }
1466  if (strcasestr_local(msg, "endmsg")) {
1468  "Trying to cheat now are we?", NULL);
1469  return 0;
1470  }
1471 
1472  /* Lauwenmark: Handle for plugin book writing (trigger) event */
1473  if (execute_event(item, EVENT_TRIGGER, pl, NULL, msg, SCRIPT_FIX_ALL) != 0)
1474  return strlen(msg);
1475 
1476  buf[0] = 0;
1477  if (!book_overflow(item->msg, msg, BOOK_BUF)) { /* add msg string to book */
1478  if (item->msg)
1479  snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
1480  else
1481  snprintf(buf, sizeof(buf), "%s\n", msg);
1482 
1483  if (item->nrof > 1) {
1484  newBook = get_object();
1485  copy_object(item, newBook);
1486  decrease_ob(item);
1487  newBook->nrof = 1;
1488  if (newBook->msg)
1489  free_string(newBook->msg);
1490  newBook->msg = add_string(buf);
1491  newBook = insert_ob_in_ob(newBook, pl);
1492  } else {
1493  if (item->msg)
1494  free_string(item->msg);
1495  item->msg = add_string(buf);
1496  /* This shouldn't be necessary - the object hasn't changed in any
1497  * visible way
1498  */
1499  /* esrv_send_item(pl, item);*/
1500  }
1501  query_short_name(item, buf, BOOK_BUF);
1503  "You write in the %s.",
1504  "You write in the %s.",
1505  buf);
1506  return strlen(msg);
1507  } else {
1508  query_short_name(item, buf, BOOK_BUF);
1510  "Your message won't fit in the %s!",
1511  "Your message won't fit in the %s!",
1512  buf);
1513  }
1514  return 0;
1515 }
1516 
1532 static int write_scroll(object *pl, object *scroll, object *skill) {
1533  int success = 0, confused = 0, grace_cost = 0;
1534  object *newscroll, *chosen_spell, *tmp;
1535 
1536  /* this is a sanity check */
1537  if (scroll->type != SCROLL) {
1539  "A spell can only be inscribed into a scroll!", NULL);
1540  return 0;
1541  }
1542 
1543  /* Check if we are ready to attempt inscription */
1544  chosen_spell = pl->contr->ranges[range_magic];
1545  if (!chosen_spell) {
1547  "You need a spell readied in order to inscribe!", NULL);
1548  return 0;
1549  }
1550  /* grace can become negative, we don't want a sp spell to block writing. */
1551  grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1552  if (grace_cost > 0 && grace_cost > pl->stats.grace) {
1554  "You don't have enough grace to write a scroll of %s.",
1555  "You don't have enough grace to write a scroll of %s.",
1556  chosen_spell->name);
1557  return 0;
1558  }
1559  if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
1561  "You don't have enough mana to write a scroll of %s.",
1562  "You don't have enough mana to write a scroll of %s.",
1563  chosen_spell->name);
1564  return 0;
1565  }
1566  /* Prevent an abuse: write a spell you're denied with, then cast it from the
1567  * written scroll - gros, 28th July 2006
1568  */
1569  if (chosen_spell->path_attuned&pl->path_denied && settings.allow_denied_spells_writing == 0) {
1570  char name[MAX_BUF];
1571 
1572  query_name(chosen_spell, name, MAX_BUF);
1574  "The simple idea of writing a scroll of %s makes you sick !",
1575  "The simple idea of writing a scroll of %s makes you sick !",
1576  name);
1577  return 0;
1578  }
1579 
1580  /* if there is a spell already on the scroll then player could easily
1581  * accidently read it while trying to write the new one. give player
1582  * a 50% chance to overwrite spell at their own level
1583  */
1584  if ((scroll->stats.sp || scroll->inv)
1585  && random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
1587  "Oops! You accidently read it while trying to write on it.", NULL);
1588  manual_apply(pl, scroll, 0);
1589  return 0;
1590  }
1591 
1592  if (execute_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0)
1593  return 0;
1594 
1595  /* ok, we are ready to try inscription */
1596  if (QUERY_FLAG(pl, FLAG_CONFUSED))
1597  confused = 1;
1598 
1599  /* Lost mana/grace no matter what */
1600  pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1601  pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
1602 
1603  if (random_roll(0, chosen_spell->level*4-1, pl, PREFER_LOW) < skill->level) {
1604  if (scroll->nrof > 1) {
1605  newscroll = get_object();
1606  copy_object(scroll, newscroll);
1607  decrease_ob(scroll);
1608  newscroll->nrof = 1;
1609  } else {
1610  newscroll = scroll;
1611  }
1612 
1613  if (!confused) {
1614  newscroll->level = MAX(skill->level, chosen_spell->level);
1616  "You succeed in writing a new scroll.", NULL);
1617  } else {
1618  chosen_spell = find_random_spell_in_ob(pl, NULL);
1619  if (!chosen_spell)
1620  return 0;
1621 
1622  newscroll->level = MAX(skill->level, chosen_spell->level);
1624  "In your confused state, you write down some odd spell.", NULL);
1625  }
1626 
1627  if (newscroll->inv) {
1628  object *ninv;
1629 
1630  ninv = newscroll->inv;
1631  remove_ob(ninv);
1632  free_object(ninv);
1633  }
1634  tmp = get_object();
1635  copy_object(chosen_spell, tmp);
1636  insert_ob_in_ob(tmp, newscroll);
1637  /* This is needed so casting from the scroll correctly works with moving_ball types, which
1638  check attunements. */
1639  newscroll->path_attuned = tmp->path_repelled;
1640 
1641  /* Same code as from treasure.c - so they can better merge.
1642  * if players want to sell them, so be it.
1643  */
1644  newscroll->value = newscroll->arch->clone.value*newscroll->inv->value*(newscroll->level+50)/(newscroll->inv->level+50);
1645  newscroll->stats.exp = newscroll->value/5;
1646 
1647  /* wait until finished manipulating the scroll before inserting it */
1648  if (newscroll == scroll) {
1649  /* Remove to correctly merge with other items which may exist in inventory */
1650  remove_ob(newscroll);
1651  }
1652  newscroll = insert_ob_in_ob(newscroll, pl);
1653  success = calc_skill_exp(pl, newscroll, skill);
1654  if (!confused)
1655  success *= 2;
1656  success = success*skill->level;
1657  return success;
1658 
1659  } else { /* Inscription has failed */
1660 
1661  if (chosen_spell->level > skill->level || confused) { /*backfire!*/
1663  "Ouch! Your attempt to write a new scroll strains your mind!", NULL);
1664  if (random_roll(0, 1, pl, PREFER_LOW) == 1)
1665  drain_specific_stat(pl, 4);
1666  else {
1667  confuse_living(pl, pl, 99);
1668  return (-30*chosen_spell->level);
1669  }
1670  } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
1672  "Your attempt to write a new scroll rattles your mind!", NULL);
1673  confuse_living(pl, pl, 99);
1674  } else
1676  "You fail to write a new scroll.", NULL);
1677  }
1678  return 0;
1679 }
1680 
1695 int write_on_item(object *pl, const char *params, object *skill) {
1696  object *item;
1697  const char *string = params;
1698  int msgtype;
1699  archetype *skat;
1700 
1701  if (pl->type != PLAYER)
1702  return 0;
1703 
1704  if (!params) {
1705  params = "";
1706  string = params;
1707  }
1709 
1710  /* Need to be able to read before we can write! */
1711  if (!find_skill_by_name(pl, skat->clone.skill)) {
1713  "You must learn to read before you can write!", NULL);
1714  return 0;
1715  }
1716 
1717  if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
1719  "You are unable to write while blind.", NULL);
1720  return 0;
1721  }
1722 
1723 
1724  /* if there is a message then it goes in a book and no message means
1725  * write active spell into the scroll
1726  */
1727  msgtype = (string[0] != '\0') ? BOOK : SCROLL;
1728 
1729  /* find an item of correct type to write on */
1730  if (!(item = find_marked_object(pl))) {
1732  "You don't have any marked item to write on.", NULL);
1733  return 0;
1734  }
1735 
1736  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1738  "You had better pay for that before you write on it.", NULL);
1739  return 0;
1740  }
1741  if (msgtype != item->type) {
1743  "You have no %s to write %s",
1744  "You have no %s to write %s",
1745  msgtype == BOOK ? "book" : "scroll",
1746  msgtype == BOOK ? "on" : "your spell down");
1747  return 0;
1748  }
1749 
1750  if (msgtype == SCROLL) {
1751  return write_scroll(pl, item, skill);
1752  } else if (msgtype == BOOK) {
1753  return write_note(pl, item, string, skill);
1754  }
1755  return 0;
1756 }
1757 
1758 
1759 
1777 static object *find_throw_ob(object *op, const char *request) {
1778  object *tmp;
1779  char name[MAX_BUF];
1780 
1781  if (!op) { /* safety */
1782  LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1783  return (object *)NULL;
1784  }
1785 
1786  /* prefer marked item */
1787  tmp = find_marked_object(op);
1788  if (tmp != NULL) {
1789  /* can't toss invisible or inv-locked items */
1790  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1791  tmp = NULL;
1792  }
1793  }
1794 
1795  /* look through the inventory */
1796  if (tmp == NULL) {
1797  for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
1798  /* can't toss invisible or inv-locked items */
1799  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED))
1800  continue;
1801  query_name(tmp, name, MAX_BUF);
1802  if (!request
1803  || !strcmp(name, request)
1804  || !strcmp(tmp->name, request))
1805  break;
1806  }
1807  }
1808 
1809  /* this should prevent us from throwing away
1810  * cursed items, worn armour, etc. Only weapons
1811  * can be thrown from 'hand'.
1812  */
1813  if (!tmp)
1814  return NULL;
1815 
1816  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1817  if (tmp->type != WEAPON) {
1818  query_name(tmp, name, MAX_BUF);
1820  "You can't throw %s.",
1821  "You can't throw %s.",
1822  name);
1823  tmp = NULL;
1824  } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
1825  query_name(tmp, name, MAX_BUF);
1828  "The %s sticks to your hand!",
1829  "The %s sticks to your hand!",
1830  name);
1831  tmp = NULL;
1832  } else {
1833  if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) {
1834  LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1835  tmp = NULL;
1836  }
1837  }
1838  } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1839  query_name(tmp, name, MAX_BUF);
1841  "You should pay for the %s first.",
1842  "You should pay for the %s first.",
1843  name);
1844  tmp = NULL;
1845  }
1846 
1847  if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1848  LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
1849  tmp = NULL;
1850  }
1851  return tmp;
1852 }
1853 
1865 static object *make_throw_ob(object *orig) {
1866  object *toss_item;
1867 
1868  if (!orig)
1869  return NULL;
1870 
1871  toss_item = get_object();
1872  if (QUERY_FLAG(orig, FLAG_APPLIED)) {
1873  LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
1874  /* insufficient workaround, but better than nothing */
1875  CLEAR_FLAG(orig, FLAG_APPLIED);
1876  }
1877  copy_object(orig, toss_item);
1878  toss_item->type = THROWN_OBJ;
1879  CLEAR_FLAG(toss_item, FLAG_CHANGING);
1880  toss_item->stats.dam = 0; /* default damage */
1881  insert_ob_in_ob(orig, toss_item);
1882  return toss_item;
1883 }
1884 
1885 
1906 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
1907  object *throw_ob = toss_item, *left = NULL;
1908  tag_t left_tag;
1909  int eff_str = 0, maxc, str = op->stats.Str, dam = 0;
1910  int pause_f, weight_f = 0, mflags;
1911  float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1912  mapstruct *m;
1913  sint16 sx, sy;
1914  tag_t tag;
1915  char name[MAX_BUF];
1916 
1917  if (throw_ob == NULL) {
1918  if (op->type == PLAYER) {
1920  "You have nothing to throw.", NULL);
1921  }
1922  return 0;
1923  }
1924  if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
1925  if (op->type == PLAYER) {
1927  "The gods won't let you throw that.", NULL);
1928  }
1929  return 0;
1930  }
1931 
1932  /* Because throwing effectiveness must be reduced by the
1933  * encumbrance of the thrower and weight of the object. THus,
1934  * we use the concept of 'effective strength' as defined below.
1935  */
1936 
1937  /* if str exceeds MAX_STAT (30, eg giants), lets assign a str_factor > 1 */
1938  if (str > MAX_STAT) {
1939  str_factor = (float)str/(float)MAX_STAT;
1940  str = MAX_STAT;
1941  }
1942 
1943  /* the more we carry, the less we can throw. Limit only on players */
1944  /* This logic is basically grabbed right out of fix_object() */
1945  maxc=max_carry[str] * 1000;
1946  if (op->type == PLAYER && op->carrying > maxc)
1947  load_factor = (float)maxc/(float) op->carrying;
1948 
1949  /* lighter items are thrown harder, farther, faster */
1950  if (throw_ob->weight > 0)
1951  item_factor = (float) maxc/(float) (3.0 * throw_ob->weight);
1952  else { /* 0 or negative weight?!? Odd object, can't throw it */
1953  query_name(throw_ob, name, MAX_BUF);
1955  "You can't throw %s.",
1956  "You can't throw %s.",
1957  name);
1958  return 0;
1959  }
1960 
1961  eff_str = str*MIN(load_factor, 1.0);
1962  eff_str = (float)eff_str*item_factor*str_factor;
1963 
1964  /* alas, arrays limit us to a value of MAX_STAT (30). Use str_factor to
1965  * account for super-strong throwers. */
1966  if (eff_str > MAX_STAT)
1967  eff_str = MAX_STAT;
1968 
1969 #ifdef DEBUG_THROW
1970  LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
1971  LOG(llevDebug, " max_c=%d, item_f=%f, load_f=%f, str=%d\n", (weight_limit[op->stats.Str]*FREE_PLAYER_LOAD_PERCENT), item_factor, load_factor, op->stats.Str);
1972  LOG(llevDebug, " str_factor=%f\n", str_factor);
1973  LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
1974 #endif
1975 
1976  /* 3 things here prevent a throw, you aimed at your feet, you
1977  * have no effective throwing strength, or you threw at something
1978  * that flying objects can't get through.
1979  */
1980  mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
1981 
1982  if (!dir
1983  || (eff_str <= 1)
1984  || (mflags&P_OUT_OF_MAP)
1985  || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
1986 
1987  /* bounces off 'wall', and drops to feet */
1988  remove_ob(throw_ob);
1989  throw_ob->x = part->x; throw_ob->y = part->y;
1990  insert_ob_in_map(throw_ob, part->map, op, 0);
1991  if (op->type == PLAYER) {
1992  if (eff_str <= 1) {
1993  query_name(throw_ob, name, MAX_BUF);
1995  "Your load is so heavy you drop %s to the ground.",
1996  "Your load is so heavy you drop %s to the ground.",
1997  name);
1998  } else if (!dir) {
1999  query_name(throw_ob, name, MAX_BUF);
2001  "You throw %s at the ground.",
2002  "You throw %s at the ground.",
2003  name);
2004  } else
2006  "Something is in the way.", NULL);
2007  }
2008  return 0;
2009  } /* if object can't be thrown */
2010 
2011  left = throw_ob; /* these are throwing objects left to the player */
2012  /* BUG? The value in left_tag doesn't seem to be used. */
2013  left_tag = left->count;
2014 
2015  /* sometimes get_split_ob can't split an object (because op->nrof==0?)
2016  * and returns NULL. We must use 'left' then
2017  */
2018 
2019  if ((throw_ob = get_split_ob(throw_ob, 1, NULL, 0)) == NULL) {
2020  throw_ob = left;
2021  remove_ob(left);
2022  }
2023 
2024  /* special case: throwing powdery substances like dust, dirt */
2025  if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
2026  cast_dust(op, throw_ob, dir);
2027  return 1;
2028  }
2029 
2030  /* Make a thrown object -- insert real object in a 'carrier' object.
2031  * If unsuccessfull at making the "thrown_obj", we just reinsert
2032  * the original object back into inventory and exit
2033  */
2034  if ((toss_item = make_throw_ob(throw_ob))) {
2035  throw_ob = toss_item;
2036  if (throw_ob->skill)
2037  free_string(throw_ob->skill);
2038  throw_ob->skill = add_string(skill->skill);
2039  } else {
2040  insert_ob_in_ob(throw_ob, op);
2041  return 0;
2042  }
2043 
2044  set_owner(throw_ob, op);
2045  /* At some point in the attack code, the actual real object (op->inv)
2046  * becomes the hitter. As such, we need to make sure that has a proper
2047  * owner value so exp goes to the right place.
2048  */
2049  set_owner(throw_ob->inv, op);
2050  throw_ob->direction = dir;
2051  throw_ob->x = part->x;
2052  throw_ob->y = part->y;
2053 
2054  /* the damage bonus from the force of the throw */
2055  dam = str_factor*dam_bonus[eff_str];
2056 
2057  /* Now, lets adjust the properties of the thrown_ob. */
2058 
2059  /* how far to fly */
2060  throw_ob->last_sp = (eff_str*3)/5;
2061 
2062  /* speed */
2063  throw_ob->speed = (speed_bonus[eff_str]+1.0)/1.5;
2064  throw_ob->speed = MIN(1.0, throw_ob->speed); /* no faster than an arrow! */
2065 
2066  /* item damage. Eff_str and item weight influence damage done */
2067  weight_f = MIN(throw_ob->weight/2000, MAX_STAT);
2068  throw_ob->stats.dam += (dam/3)+dam_bonus[weight_f]+(throw_ob->weight/15000)-2;
2069 
2070  /* chance of breaking. Proportional to force used and weight of item */
2071  throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
2072 
2073  /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
2074  throw_ob->stats.wc = 25-dex_bonus[op->stats.Dex]-thaco_bonus[eff_str]-skill->level;
2075 
2076 
2077  /* the properties of objects which are meant to be thrown (ie dart,
2078  * throwing knife, etc) will differ from ordinary items. Lets tailor
2079  * this stuff in here.
2080  */
2081 
2082  if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
2083  throw_ob->last_sp += eff_str/3; /* fly a little further */
2084  throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
2085  throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
2086  /* only throw objects get directional faces */
2087  if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
2088  SET_ANIMATION(throw_ob, dir);
2089  } else {
2090  /* some materials will adjust properties.. */
2091  if (throw_ob->material&M_LEATHER) {
2092  throw_ob->stats.dam -= 1;
2093  throw_ob->stats.food -= 10;
2094  }
2095  if (throw_ob->material&M_GLASS)
2096  throw_ob->stats.food += 60;
2097 
2098  if (throw_ob->material&M_ORGANIC) {
2099  throw_ob->stats.dam -= 3;
2100  throw_ob->stats.food += 55;
2101  }
2102  if (throw_ob->material&M_PAPER||throw_ob->material&M_CLOTH) {
2103  throw_ob->stats.dam -= 5;
2104  throw_ob->speed *= 0.8;
2105  throw_ob->stats.wc += 3;
2106  throw_ob->stats.food -= 30;
2107  }
2108  /* light obj have more wind resistance, fly slower*/
2109  if (throw_ob->weight > 500)
2110  throw_ob->speed *= 0.8;
2111  if (throw_ob->weight > 50)
2112  throw_ob->speed *= 0.5;
2113 
2114  } /* else tailor thrown object */
2115 
2116  /* some limits, and safeties (needed?) */
2117  if (throw_ob->stats.dam < 0)
2118  throw_ob->stats.dam = 0;
2119  if (throw_ob->last_sp > eff_str)
2120  throw_ob->last_sp = eff_str;
2121  if (throw_ob->stats.food < 0)
2122  throw_ob->stats.food = 0;
2123  if (throw_ob->stats.food > 100)
2124  throw_ob->stats.food = 100;
2125  if (throw_ob->stats.wc > 30)
2126  throw_ob->stats.wc = 30;
2127 
2128  /* how long to pause the thrower. Higher values mean less pause */
2129  pause_f = ((2*eff_str)/3)+20+skill->level;
2130 
2131  /* Put a lower limit on this */
2132  if (pause_f < 10)
2133  pause_f = 10;
2134  if (pause_f > 100)
2135  pause_f = 100;
2136 
2137  /* Changed in 0.94.2 - the calculation before was really goofy.
2138  * In short summary, a throw can take anywhere between speed 5 and
2139  * speed 0.5
2140  */
2141  op->speed_left -= 50/pause_f;
2142 
2143  update_ob_speed(throw_ob);
2144  throw_ob->speed_left = 0;
2145  throw_ob->map = part->map;
2146 
2147  throw_ob->move_type = MOVE_FLY_LOW;
2148  throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
2149 
2150  /* Lauwenmark - Now we can call the associated script_throw event (if any) */
2151  execute_event(throw_ob, EVENT_THROW, op, NULL, NULL, SCRIPT_FIX_ACTIVATOR);
2152 #ifdef DEBUG_THROW
2153  LOG(llevDebug, " pause_f=%d \n", pause_f);
2154  LOG(llevDebug, " %s stats: wc=%d dam=%d dist=%d spd=%f break=%d\n", throw_ob->name, throw_ob->stats.wc, throw_ob->stats.dam, throw_ob->last_sp, throw_ob->speed, throw_ob->stats.food);
2155  LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
2156 #endif
2157  tag = throw_ob->count;
2158  insert_ob_in_map(throw_ob, part->map, op, 0);
2159  if (!was_destroyed(throw_ob, tag))
2160  ob_process(throw_ob);
2161  return 1;
2162 }
2163 
2181 int skill_throw(object *op, object *part, int dir, const char *params, object *skill) {
2182  object *throw_ob;
2183 
2184  if (op->type == PLAYER)
2185  throw_ob = find_throw_ob(op, params);
2186  else
2187  throw_ob = find_mon_throw_ob(op);
2188 
2189  return do_throw(op, part, throw_ob, dir, skill);
2190 }
#define AP_UNAPPLY
Definition: define.h:1010
#define UPD_FLAGS
Definition: newclient.h:255
void spring_trap(object *trap, object *victim)
Definition: rune.c:227
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:501
#define FOOD
Definition: define.h:118
#define RING
Definition: define.h:232
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, sint16 x, sint16 y, sint16 *nx, sint16 *ny)
Definition: map.c:330
sint8 Int
Definition: living.h:78
Definition: player.h:146
int apply_special(object *who, object *op, int aflags)
Definition: apply.c:1139
static object * make_throw_ob(object *orig)
Definition: skills.c:1865
#define FLAG_DAMNED
Definition: define.h:614
#define FLAG_UNPAID
Definition: define.h:532
#define MOVE_WALK
Definition: define.h:700
#define UP_OBJ_FACE
Definition: object.h:356
MoveType move_type
Definition: object.h:277
MoveType move_on
Definition: object.h:280
signed short sint16
Definition: global.h:72
Definition: map.h:399
static int adj_stealchance(object *op, object *victim, int roll)
Definition: skills.c:57
#define FLAG_SLEEP
Definition: define.h:604
void set_owner(object *op, object *owner)
Definition: object.c:564
int is_magical(const object *op)
Definition: item.c:1312
const int dam_bonus[MAX_STAT+1]
Definition: living.c:119
void remove_door(object *op)
Definition: time.c:51
#define FLAG_HITBACK
Definition: define.h:563
uint16 attack_movement
Definition: object.h:242
static int attempt_jump(object *pl, int dir, int spaces, object *skill)
Definition: skills.c:554
static int do_skill_ident(object *pl, int obj_class, object *skill)
Definition: skills.c:821
#define SET_FLAG(xyz, p)
Definition: define.h:510
#define TRAP
Definition: define.h:326
#define WAND
Definition: define.h:291
uint16 material
Definition: object.h:198
static object * find_throw_ob(object *op, const char *request)
Definition: skills.c:1777
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.c:3682
#define FLAG_FRIENDLY
Definition: define.h:542
static int attempt_hide(object *op, object *skill)
Definition: skills.c:467
#define MSG_TYPE_ITEM
Definition: newclient.h:334
#define DOOR
Definition: define.h:135
#define SPELL_GRACE
Definition: spells.h:87
const int dex_bonus[MAX_STAT+1]
Definition: living.c:104
sint32 last_heal
Definition: object.h:208
static int do_skill_detect_magic(object *pl, object *skill)
Definition: skills.c:727
sint16 maxgrace
Definition: living.h:86
void free_string(sstring str)
Definition: shstr.c:272
#define SET_ANIMATION(ob, newanim)
Definition: global.h:247
void esrv_update_item(int flags, object *pl, object *op)
Definition: standalone.c:200
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:504
void pick_up(object *op, object *alt)
Definition: c_object.c:462
object clone
Definition: object.h:326
socket_struct socket
Definition: player.h:148
#define SK_BOWYER
Definition: skills.h:51
sint16 invisible
Definition: object.h:211
short freearr_x[SIZEOFFREE]
Definition: object.c:75
#define PREFER_LOW
Definition: define.h:909
#define POTION
Definition: define.h:117
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
#define SK_THAUMATURGY
Definition: skills.h:76
#define FLAG_CONFUSED
Definition: define.h:608
sint32 last_sp
Definition: object.h:209
#define SCROLL
Definition: define.h:293
object * ranges[range_size]
Definition: player.h:157
void meditate(object *pl, object *skill)
Definition: skills.c:1390
int pray(object *pl, object *skill)
Definition: skills.c:1341
void update_object(object *op, int action)
Definition: object.c:1112
uint8 subtype
Definition: object.h:190
#define EXPERIENCE
Definition: define.h:158
#define M_CLOTH
Definition: material.h:51
sint64 exp
Definition: living.h:88
#define FLAG_SPLITTING
Definition: define.h:562
#define RUNE
Definition: define.h:325
struct obj * above
Definition: object.h:146
int stand_near_hostile(object *who)
Definition: player.c:3837
#define SK_SMITHERY
Definition: skills.h:50
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1123
method_ret ob_process(object *op)
Definition: ob_methods.c:79
#define BOOTS
Definition: define.h:281
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.c:459
int manual_apply(object *op, object *tmp, int aflag)
Definition: apply.c:512
#define CLOAK
Definition: define.h:268
sint16 x
Definition: object.h:179
#define GIRDLE
Definition: define.h:295
void remove_friendly_object(object *op)
Definition: friend.c:69
uint32 path_attuned
Definition: object.h:194
sint16 sp
Definition: living.h:83
#define NDI_BLACK
Definition: newclient.h:195
uint8 no_player_stealing
Definition: global.h:399
#define PETMOVE
Definition: define.h:826
uint32 path_repelled
Definition: object.h:195
#define SCRIPT_FIX_ALL
Definition: global.h:450
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
object * find_mon_throw_ob(object *op)
Definition: monster.c:1978
struct archt * other_arch
Definition: object.h:264
#define ARMOUR
Definition: define.h:128
Definition: object.h:321
static int do_skill_ident2(object *tmp, object *pl, int obj_class, object *skill)
Definition: skills.c:771
#define PLAYER
Definition: define.h:113
sint16 maxsp
Definition: living.h:84
sint16 hp
Definition: living.h:81
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:400
short freearr_y[SIZEOFFREE]
Definition: object.c:81
partylist * party
Definition: player.h:237
#define FLAG_KNOWN_MAGICAL
Definition: define.h:616
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:502
int rndm(int min, int max)
Definition: utils.c:174
#define AMULET
Definition: define.h:153
#define FLAG_UNDEAD
Definition: define.h:566
int trap_show(object *trap, object *where)
Definition: rune.c:431
uint32 tag_t
Definition: object.h:40
void remove_ob(object *op)
Definition: object.c:1515
sint16 maxhp
Definition: living.h:82
#define POISON
Definition: define.h:119
uint32 path_denied
Definition: object.h:196
#define MSG_TYPE_ITEM_REMOVE
Definition: newclient.h:557
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.c:2237
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:503
static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill)
Definition: skills.c:1906
float speed_left
Definition: object.h:182
uint32 hidden
Definition: player.h:186
#define FLAG_NO_STEAL
Definition: define.h:639
#define MSG_TYPE_VICTIM
Definition: newclient.h:336
const int thaco_bonus[MAX_STAT+1]
Definition: living.c:124
int skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1126
int hide(object *op, object *skill)
Definition: skills.c:498
sint32 weight
Definition: object.h:216
#define MOVE_FLY_LOW
Definition: define.h:701
sint8 Wis
Definition: living.h:78
#define FLAG_UNAGGRESSIVE
Definition: define.h:568
#define MSG_TYPE_ITEM_INFO
Definition: newclient.h:560
struct mapdef * map
Definition: object.h:155
#define FLAG_CHANGING
Definition: define.h:559
#define MSG_TYPE_VICTIM_STEAL
Definition: newclient.h:568
#define BOOK_BUF
Definition: book.h:39
int remove_trap(object *op, object *skill)
Definition: skills.c:1272
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.c:760
const float speed_bonus[MAX_STAT+1]
Definition: living.c:109
#define HORN
Definition: define.h:147
#define FLAG_IDENTIFIED
Definition: define.h:557
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1695
void identify(object *op)
Definition: item.c:1447
sint16 dam
Definition: living.h:87
sint64 calc_skill_exp(object *who, object *op, object *skill)
Definition: skill_util.c:599
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.c:134
void add_friendly_object(object *op)
Definition: friend.c:43
sint32 carrying
Definition: object.h:218
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:327
const char * name
Definition: object.h:167
#define SPELL
Definition: define.h:283
#define SK_DET_MAGIC
Definition: skills.h:58
uint32 tmp_invis
Definition: player.h:179
#define SK_LITERACY
Definition: skills.h:55
#define FLESH
Definition: define.h:234
int steal(object *op, int dir, object *skill)
Definition: skills.c:290
object * get_owner(object *op)
Definition: object.c:524
struct obj * below
Definition: object.h:145
#define M_GLASS
Definition: material.h:46
uint16 difficulty
Definition: map.h:364
sint16 last_grace
Definition: object.h:210
int can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.c:2020
uint32 nrof
Definition: object.h:184
#define SK_WOODSMAN
Definition: skills.h:68
sint8 Cha
Definition: living.h:78
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:740
#define FLAG_IS_CAULDRON
Definition: define.h:635
#define SIZEOFFREE
Definition: define.h:441
#define P_OUT_OF_MAP
Definition: map.h:272
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:213
sint8 facing
Definition: object.h:186
sint16 y
Definition: object.h:179
#define LOCKED_DOOR
Definition: define.h:132
struct pl * contr
Definition: object.h:134
sint8 item_power
Definition: object.h:213
#define WEAPON
Definition: define.h:127
int can_pick(const object *who, const object *item)
Definition: object.c:3569
int trap_see(object *op, object *trap)
Definition: rune.c:403
object * find_marked_object(object *op)
Definition: c_object.c:1339
#define M_ORGANIC
Definition: material.h:49
#define MAX(x, y)
Definition: define.h:70
#define MSG_TYPE_SKILL
Definition: newclient.h:329
float speed
Definition: object.h:181
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
#define SK_ALCHEMY
Definition: skills.h:53
#define CLEAR_FLAG(xyz, p)
Definition: define.h:512
static void stop_jump(object *pl)
Definition: skills.c:536
#define FLAG_WIZ
Definition: define.h:527
#define GEM
Definition: define.h:202
#define EVENT_THROW
Definition: plugin.h:70
object * insert_ob_in_ob(object *op, object *where)
Definition: object.c:2510
#define MAX_BUF
Definition: define.h:81
#define GLOVES
Definition: define.h:282
char * ob_describe(const object *op, const object *observer, char *buf, size_t size)
Definition: ob_methods.c:102
object * get_object(void)
Definition: object.c:921
object * insert_ob_in_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1992
int find_traps(object *pl, object *skill)
Definition: skills.c:1207
#define BOOK
Definition: define.h:120
static int write_note(object *pl, object *item, const char *msg, object *skill)
Definition: skills.c:1451
#define BRACERS
Definition: define.h:286
const char * skill
Definition: object.h:174
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1432
sint8 wc
Definition: living.h:79
#define FLAG_IS_THROWN
Definition: define.h:545
static int attempt_pick_lock(object *door, object *pl, object *skill)
Definition: skills.c:366
#define FLAG_READY_WEAPON
Definition: define.h:631
#define MIN(x, y)
Definition: define.h:67
void npc_call_help(object *op)
Definition: monster.c:1632
sint8 Str
Definition: living.h:78
#define THROWN_OBJ
Definition: define.h:170
#define FLAG_KNOWN_CURSED
Definition: define.h:617
int need_identify(const object *op)
Definition: item.c:1401
#define FLAG_CURSED
Definition: define.h:613
sint8 body_info[NUM_BODY_LOCATIONS]
Definition: object.h:223
#define SHIELD
Definition: define.h:145
#define AP_NO_MERGE
Definition: define.h:1015
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define decrease_ob(xyz)
Definition: global.h:276
sint8 body_used[NUM_BODY_LOCATIONS]
Definition: object.h:224
#define INORGANIC
Definition: define.h:235
#define PREFER_HIGH
Definition: define.h:908
#define FLAG_BLIND
Definition: define.h:633
sint8 direction
Definition: object.h:185
#define CONTAINER
Definition: define.h:306
struct obj * owner
Definition: object.h:228
void pray_at_altar(object *pl, object *altar, object *skill)
Definition: gods.c:325
sint16 grace
Definition: living.h:85
#define FREE_AND_COPY(sv, nv)
Definition: global.h:288
object * find_random_spell_in_ob(object *ob, const char *skill)
Definition: spell_util.c:60
#define NUM_ANIMATIONS(ob)
Definition: global.h:255
#define POT_DUST
Definition: spells.h:160
archetype * get_archetype_by_type_subtype(int type, int subtype)
Definition: arch.c:149
static int write_scroll(object *pl, object *scroll, object *skill)
Definition: skills.c:1532
tag_t count
Definition: object.h:157
living stats
Definition: object.h:219
sint8 Dex
Definition: living.h:78
struct archt * arch
Definition: object.h:263
#define SKILL
Definition: define.h:157
struct Settings settings
Definition: init.c:48
static int do_skill_detect_curse(object *pl, object *skill)
Definition: skills.c:686
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.c:696
#define FLAG_APPLIED
Definition: define.h:531
void query_short_name(const object *op, char *buf, size_t size)
Definition: item.c:551
int out_of_map(mapstruct *m, int x, int y)
Definition: map.c:2300
int execute_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: standalone.c:225
sint16 SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.c:281
const uint32 weight_limit[MAX_STAT+1]
Definition: living.c:143
const char * msg
Definition: object.h:175
int hideability(object *ob)
Definition: player.c:3755
#define FLAG_MAKE_INVIS
Definition: define.h:625
#define FLAG_STARTEQUIP
Definition: define.h:564
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
Definition: newclient.h:487
void update_ob_speed(object *op)
Definition: object.c:1008
#define GET_ANIM_ID(ob)
Definition: global.h:249
#define BOW
Definition: define.h:126
sstring add_string(const char *str)
Definition: shstr.c:116
#define DRINK
Definition: define.h:187
#define M_PAPER
Definition: material.h:44
#define GET_MAP_OB(M, X, Y)
Definition: map.h:193
#define FLAG_MONSTER
Definition: define.h:541
int skill_throw(object *op, object *part, int dir, const char *params, object *skill)
Definition: skills.c:2181
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.c:976
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
#define HELMET
Definition: define.h:146
#define M_LEATHER
Definition: material.h:47
struct obj * head
Definition: object.h:154
#define SPELLBOOK
Definition: define.h:266
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
const char * strcasestr_local(const char *s, const char *find)
Definition: porting.c:461
object * get_split_ob(object *orig_ob, uint32 nr, char *err, size_t size)
Definition: object.c:2313
#define SCRIPT_FIX_ACTIVATOR
Definition: global.h:449
#define ARROW
Definition: define.h:125
void make_visible(object *op)
Definition: player.c:3712
#define ROD
Definition: define.h:115
#define FLAG_WAS_WIZ
Definition: define.h:530
#define was_destroyed(op, old_tag)
Definition: object.h:94
MoveType move_block
Definition: object.h:278
static int attempt_steal(object *op, object *who, object *skill)
Definition: skills.c:127
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
Definition: object.c:3524
void copy_object(object *op2, object *op)
Definition: object.c:758
int jump(object *pl, int dir, object *skill)
Definition: skills.c:651
#define SPELL_MANA
Definition: spells.h:86
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:628
int allow_denied_spells_writing
Definition: global.h:404
void free_object(object *ob)
Definition: object.c:1238
Definition: map.h:346
#define P_IS_ALIVE
Definition: map.h:258
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:51
#define HOLY_ALTAR
Definition: define.h:193
#define FLAG_NO_SKILL_IDENT
Definition: define.h:632
#define SK_DET_CURSE
Definition: skills.h:61
sint16 level
Definition: object.h:202
int skill_ident(object *pl, object *skill)
Definition: skills.c:872
#define FLAG_INV_LOCKED
Definition: define.h:626
void fix_object(object *op)
Definition: living.c:900
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:207
struct obj * more
Definition: object.h:153
sint32 value
Definition: object.h:201
sint8 magic
Definition: object.h:199
const int max_carry[MAX_STAT+1]
Definition: living.c:129
#define EVENT_TRIGGER
Definition: plugin.h:71
uint8 type
Definition: object.h:189
#define MAX_STAT
Definition: define.h:78
#define SK_JEWELER
Definition: skills.h:52
sint32 food
Definition: living.h:89
#define BODY_ARMS
Definition: object.h:42
uint32 hide
Definition: object.h:238