Crossfire Server, Trunk  R21524
skills.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, see the
9  * 'LICENSE' and 'COPYING' files.
10  *
11  * The authors can be reached via e-mail to crossfire-devel@real-time.com
12  *
13  * skills.c -- core skill handling
14  */
15 #define _GNU_SOURCE // strcasestr() is a GNU extension in string.h
16 
17 #include "global.h"
18 
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include "book.h"
23 #include "living.h"
24 #include "object.h"
25 #include "skills.h"
26 #include "spells.h"
27 #include "sproto.h"
28 
43 static int adj_stealchance(object *op, object *victim, int roll) {
44  if (!op || !victim || !roll)
45  return -1;
46 
47  /* Only prohibit stealing if the player does not have a free
48  * hand available and in fact does have hands.
49  */
50  if (op->type == PLAYER
51  && op->body_used[BODY_ARMS] <= 0
52  && op->body_info[BODY_ARMS]) {
54  "But you have no free hands to steal with!");
55  return -1;
56  }
57 
58  /* ADJUSTMENTS */
59 
60  /* Its harder to steal from hostile beings! */
61  if (!QUERY_FLAG(victim, FLAG_UNAGGRESSIVE))
62  roll = roll/2;
63 
64  /* Easier to steal from sleeping beings, or if the thief is
65  * unseen */
66  if (QUERY_FLAG(victim, FLAG_SLEEP))
67  roll = roll*3;
68  else if (op->invisible)
69  roll = roll*2;
70 
71  /* check stealing 'encumberance'. Having this equipment applied makes
72  * it quite a bit harder to steal.
73  */
74  FOR_INV_PREPARE(op, equip) {
75  if (equip->type == WEAPON && QUERY_FLAG(equip, FLAG_APPLIED)) {
76  roll -= equip->weight/10000;
77  }
78  if (equip->type == BOW && QUERY_FLAG(equip, FLAG_APPLIED))
79  roll -= equip->weight/5000;
80  if (equip->type == SHIELD && QUERY_FLAG(equip, FLAG_APPLIED)) {
81  roll -= equip->weight/2000;
82  }
83  if (equip->type == ARMOUR && QUERY_FLAG(equip, FLAG_APPLIED))
84  roll -= equip->weight/5000;
85  if (equip->type == GLOVES && QUERY_FLAG(equip, FLAG_APPLIED))
86  roll -= equip->weight/100;
87  } FOR_INV_FINISH();
88  if (roll < 0)
89  roll = 0;
90  return roll;
91 }
92 
111 static int attempt_steal(object *op, object *who, object *skill) {
112  object *success = NULL, *tmp = NULL;
113  int roll = 0, chance = 0, stats_value;
114  rv_vector rv;
115  char name[MAX_BUF];
116 
117  stats_value = ((who->stats.Dex+who->stats.Int)*3)/2;
118 
119  /* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them)
120  * they will try to prevent stealing if they can. Only unseen theives will
121  * have much chance of success.
122  */
123  if (op->type != PLAYER && QUERY_FLAG(op, FLAG_NO_STEAL)) {
124  if (monster_can_detect_enemy(op, who, &rv)) {
128  "Your attempt is prevented!");
129  return 0;
130  }
131 
132  /* help npc to detect thief next time by raising its wisdom
133  * This probably isn't the right approach - we shouldn't be
134  * changing the stats of the monsters - better approach
135  * might be to use force objects for this - MSW 2009/02/24
136  */
137  op->stats.Wis += (op->stats.Int/5)+1;
138  if (op->stats.Wis > settings.max_stat)
139  op->stats.Wis = settings.max_stat;
140  }
141  if (op->type == PLAYER && QUERY_FLAG(op, FLAG_WIZ)) {
143  "You can't steal from the dungeon master!");
144  return 0;
145  }
146  if (op->type == PLAYER && who->type == PLAYER && settings.no_player_stealing) {
148  "You can't steal from other players!");
149  return 0;
150  }
151 
152 
153  /* Ok then, go thru their inventory, stealing */
154  FOR_INV_PREPARE(op, inv) {
155  /* you can't steal worn items, starting items, wiz stuff,
156  * innate abilities, or items w/o a type. Generally
157  * speaking, the invisibility flag prevents experience or
158  * abilities from being stolen since these types are currently
159  * always invisible objects. I was implicit here so as to prevent
160  * future possible problems. -b.t.
161  * Flesh items generated w/ fix_flesh_item should have FLAG_NO_STEAL
162  * already -b.t.
163  */
164 
165  if (QUERY_FLAG(inv, FLAG_WAS_WIZ)
166  || QUERY_FLAG(inv, FLAG_APPLIED)
167  || !(inv->type)
168  || inv->type == SPELL
169  || QUERY_FLAG(inv, FLAG_STARTEQUIP)
170  || QUERY_FLAG(inv, FLAG_NO_STEAL)
171  || inv->invisible)
172  continue;
173 
174  /* Okay, try stealing this item. Dependent on dexterity of thief,
175  * skill level, see the adj_stealroll fctn for more detail.
176  */
177 
178  roll = die_roll(2, 100, who, PREFER_LOW)/2; /* weighted 1-100 */
179 
180  chance = adj_stealchance(who, op, stats_value+skill->level*10-op->level*3);
181  if (chance == -1)
182  return 0;
183  if (roll < chance) {
184  tag_t inv_count = inv->count;
185 
186  pick_up(who, inv);
187  /* need to see if the player actually stole this item -
188  * if it is in the players inv, assume it is. This prevents
189  * abuses where the player can not carry the item, so just
190  * keeps stealing it over and over.
191  */
192  if (object_was_destroyed(inv, inv_count) || inv->env != op) {
193  /* for players, play_sound: steals item */
194  success = inv;
196  }
197  tmp = inv;
198  break;
199  }
200  } FOR_INV_FINISH(); /* for loop looking for an item */
201 
202  if (!tmp) {
203  query_name(op, name, MAX_BUF);
205  "%s%s has nothing you can steal!",
206  op->type == PLAYER ? "" : "The ", name);
207  return 0;
208  }
209 
210  /* If you arent high enough level, you might get something BUT
211  * the victim will notice your stealing attempt. Ditto if you
212  * attempt to steal something heavy off them, they're bound to notice
213  */
214 
215  if (roll >= skill->level
216  || !chance
217  || (tmp && tmp->weight > 250*random_roll(0, stats_value+skill->level*10-1, who, PREFER_LOW))) {
218  /* victim figures out where the thief is! */
219  if (who->hide)
220  make_visible(who);
221 
222  if (op->type != PLAYER) {
223  /* The unaggressives look after themselves 8) */
224  if (who->type == PLAYER) {
226  query_name(op, name, MAX_BUF);
228  "%s notices your attempted pilfering!",
229  name);
230  }
232  /* all remaining npc items are guarded now. Set flag NO_STEAL
233  * on the victim.
234  */
235  SET_FLAG(op, FLAG_NO_STEAL);
236  } else { /* stealing from another player */
237  char buf[MAX_BUF];
238 
239  /* Notify the other player */
240  if (success && who->stats.Int > random_roll(0, 19, op, PREFER_LOW)) {
241  query_name(success, name, MAX_BUF);
242  snprintf(buf, sizeof(buf), "Your %s is missing!", name);
243  } else {
244  snprintf(buf, sizeof(buf), "Your pack feels strangely lighter.");
245  }
247  buf);
248  if (!success) {
249  if (who->invisible) {
250  snprintf(buf, sizeof(buf), "you feel itchy fingers getting at your pack.");
251  } else {
252  query_name(who, name, MAX_BUF);
253  snprintf(buf, sizeof(buf), "%s looks very shifty.", name);
254  }
256  buf);
257  }
258  } /* else stealing from another player */
259  /* play_sound("stop! thief!"); kindofthing */
260  } /* if you weren't 100% successful */
261  return success ? 1 : 0;
262 }
263 
264 
277 int steal(object *op, int dir, object *skill) {
278  object *tmp;
279  int16_t x, y;
280  mapstruct *m;
281  int mflags;
282 
283  x = op->x+freearr_x[dir];
284  y = op->y+freearr_y[dir];
285 
286  if (dir == 0) {
287  /* Can't steal from ourself! */
288  return 0;
289  }
290 
291  m = op->map;
292  mflags = get_map_flags(m, &m, x, y, &x, &y);
293  /* Out of map - can't do it. If nothing alive on this space,
294  * don't need to look any further.
295  */
296  if ((mflags&P_OUT_OF_MAP) || !(mflags&P_IS_ALIVE))
297  return 0;
298 
299  /* If player can't move onto the space, can't steal from it. */
300  if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y)))
301  return 0;
302 
303  /* Find the topmost object at this spot */
304  tmp = GET_MAP_OB(m, x, y);
306  if (tmp->above == NULL)
307  break;
309 
310  /* For all the stacked objects at this point, attempt a steal */
312  /* Minor hack--for multi square beings - make sure we get
313  * the 'head' coz 'tail' objects have no inventory! - b.t.
314  */
315  tmp = HEAD(tmp);
316  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER))
317  continue;
318 
319  /* do not reveal hidden DMs */
320  if (tmp->type == PLAYER && QUERY_FLAG(tmp, FLAG_WIZ) && tmp->contr->hidden)
321  continue;
322  if (attempt_steal(tmp, op, skill)) {
323  if (tmp->type == PLAYER) /* no xp for stealing from another player */
324  return 0;
325 
326  /* no xp for stealing from pets (of players) */
327  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
328  object *owner = object_get_owner(tmp);
329  if (owner != NULL && owner->type == PLAYER)
330  return 0;
331  }
332 
333  return (calc_skill_exp(op, tmp, skill));
334  }
336  return 0;
337 }
338 
353 static int attempt_pick_lock(object *door, object *pl, object *skill) {
354  int difficulty = pl->map->difficulty ? pl->map->difficulty : 0;
355  int success = 0, number; /* did we get anything? */
356 
357  /* Try to pick the lock on this item (doors only for now).
358  * Dependent on dexterity/skill SK_level of the player and
359  * the map level difficulty.
360  */
361  number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2;
362  if (number < pl->stats.Dex + skill->level*2 - difficulty ) {
363  remove_door(door);
364  success = difficulty;
365  } else if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) { /* set off any traps? */
366  spring_trap(door->inv, pl);
367  }
368  return success;
369 }
370 
371 
387 int pick_lock(object *pl, int dir, object *skill) {
388  object *tmp;
389  int x = pl->x+freearr_x[dir];
390  int y = pl->y+freearr_y[dir];
391  int difficulty=0;
392 
393  if (!dir)
394  dir = pl->facing;
395 
396  /* For all the stacked objects at this point find a door*/
397  if (OUT_OF_REAL_MAP(pl->map, x, y)) {
399  "There is no lock there.");
400  return 0;
401  }
402 
403  for (tmp = GET_MAP_OB(pl->map, x, y); tmp; tmp = tmp->above)
404  if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
405  break;
406 
407  if (!tmp) {
409  "There is no lock there.");
410  return 0;
411  }
412 
413  if (execute_event(tmp, EVENT_TRIGGER, pl, skill, NULL, SCRIPT_FIX_ALL) != 0)
414  return 0;
415 
416  if (tmp->type == LOCKED_DOOR) {
418  "You can't pick that lock!");
419  return 0;
420  }
421 
422  if (!tmp->move_block) {
424  "The door has no lock!");
425  return 0;
426  }
427 
428  difficulty = attempt_pick_lock(tmp, pl, skill);
429  /* Failure */
430  if (!difficulty) {
432  "You fail to pick the lock.");
433  return 0;
434  }
435 
437  "You pick the lock.");
438  return (calc_skill_exp(pl, NULL, skill) * isqrt(difficulty));
439 }
440 
441 
461 static int attempt_hide(object *op, object *skill) {
462  int number, difficulty = op->map->difficulty;
463  int terrain = hideability(op);
464 
465  if (terrain < -10) /* not enough cover here */
466  return 0;
467 
468  /* Hiding success and duration dependant on skill level,
469  * op->stats.Dex, map difficulty and terrain.
470  */
471 
472  number = (die_roll(2, 25, op, PREFER_LOW)-2)/2;
473  if (!stand_near_hostile(op) && number < op->stats.Dex+skill->level+terrain-difficulty) {
474  op->invisible += 100; /* set the level of 'hiddeness' */
475  if (op->type == PLAYER)
476  op->contr->tmp_invis = 1;
477  op->hide = 1;
478  return 1;
479  }
480  return 0;
481 }
482 
492 int hide(object *op, object *skill) {
493  /* the preliminaries -- Can we really hide now? */
494  /* this keeps monsters from using invisibilty spells and hiding */
495 
496  if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
498  "You don't need to hide while invisible!");
499  return 0;
500  }
501 
502  if (!op->hide && op->invisible > 0 && op->type == PLAYER) {
504  "Your attempt to hide breaks the invisibility spell!");
505  make_visible(op);
506  }
507 
508  if (op->invisible > 50*skill->level) {
510  "You are as hidden as you can get.");
511  return 0;
512  }
513 
514  if (attempt_hide(op, skill)) {
516  "You hide in the shadows.");
518  return calc_skill_exp(op, NULL, skill);
519  }
521  "You fail to conceal yourself.");
522  return 0;
523 }
524 
525 
532 static void stop_jump(object *pl) {
533  fix_object(pl);
534  object_insert_in_map_at(pl, pl->map, pl, 0, pl->x, pl->y);
535  // Some monsters can also jump. They do not check for pickup with check_pick().
536  if (pl->contr)
537  check_pick(pl);
538 }
539 
553 static int attempt_jump(object *pl, int dir, int spaces, object *skill) {
558  if (pl->contr && pl->contr->transport){
559  char trans_name[MAX_BUF];
560  query_name(pl->contr->transport, trans_name, MAX_BUF);
562  "Your bounce off the walls of %s.", trans_name);
563  // We failed to jump. Return as a failure.
564  return 0;
565  }
566 
567  int i, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
568  int16_t x, y;
569  mapstruct *m;
570 
571  /* Jump loop. Go through spaces opject wants to jump. Halt the
572  * jump if a wall or creature is in the way. We set FLY_LOW
573  * temporarily to allow player to aviod exits/archs that are not
574  * move_on/off fly_low. This will also prevent pickup of objects
575  * while jumping over them.
576  */
577 
578  object_remove(pl);
579 
580  /*
581  * I don't think this is actually needed - all the movement
582  * code is handled in this function, and I don't see anyplace
583  * that cares about the move_type being flying.
584  */
585  pl->move_type |= MOVE_FLY_LOW;
586 
587  for (i = 0; i <= spaces; i++) {
588  x = pl->x+dx;
589  y = pl->y+dy;
590  m = pl->map;
591 
592  mflags = get_map_flags(m, &m, x, y, &x, &y);
593 
594  if (mflags&P_OUT_OF_MAP) {
595  (void)stop_jump(pl);
596  return 0;
597  }
598  if (OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, x, y))) {
600  "Your jump is blocked.");
601  stop_jump(pl);
602  return 0;
603  }
604 
605  FOR_MAP_PREPARE(m, x, y, tmp) {
606  tmp = HEAD(tmp);
607  /* Jump into creature */
608  if (QUERY_FLAG(tmp, FLAG_MONSTER)
609  || (tmp->type == PLAYER && (!QUERY_FLAG(tmp, FLAG_WIZ) || !tmp->contr->hidden))) {
611  "You jump into %s%s.",
612  tmp->type == PLAYER ? "" : "the ", tmp->name);
613 
614  stop_jump(pl);
615  if (tmp->type != PLAYER
616  || (pl->type == PLAYER && pl->contr->party == NULL)
617  || (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
618  skill_attack(tmp, pl, pl->facing, "kicked", skill); /* pl makes an attack */
619 
620  return 1;
621  }
622  /* If the space has fly on set (no matter what the space is),
623  * we should get the effects - after all, the player is
624  * effectively flying.
625  */
626  if (tmp->move_on&MOVE_FLY_LOW) {
627  pl->x = x;
628  pl->y = y;
629  pl->map = m;
630  if (pl->contr)
631  esrv_map_scroll(&pl->contr->socket, dx, dy);
632  stop_jump(pl);
633  return 1;
634  }
635  } FOR_MAP_FINISH();
636  pl->x = x;
637  pl->y = y;
638  pl->map = m;
639  if (pl->contr)
640  esrv_map_scroll(&pl->contr->socket, dx, dy);
641  }
642  stop_jump(pl);
643  return 1;
644 }
645 
662 int jump(object *pl, int dir, object *skill) {
663  int spaces = 0, stats;
664  int str = pl->stats.Str;
665  int dex = pl->stats.Dex;
666 
667  dex = dex ? dex : 15;
668  str = str ? str : 10;
669 
670  stats = str*str*str*dex*skill->level;
671 
672  if (pl->carrying != 0) /* don't want div by zero !! */
673  spaces = (int)(stats/pl->carrying);
674  else
675  spaces = 2; /* pl has no objects - gets the far jump */
676 
677  if (spaces > 2)
678  spaces = 2;
679  else if (spaces == 0) {
681  "You are carrying too much weight to jump.");
682  return 0;
683  }
684  return attempt_jump(pl, dir, spaces, skill);
685 }
686 
699 int detect_curse_on_item(object *pl, object *tmp, object *skill) {
701  && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
702  && tmp->item_power < skill->level) {
704  esrv_update_item(UPD_FLAGS, pl, tmp);
705  return calc_skill_exp(pl, tmp, skill);
706  }
707  return 0;
708 }
719 static int do_skill_detect_curse(object *pl, object *skill) {
720  int success = 0;
721 
722  FOR_INV_PREPARE(pl, tmp)
723  if (!tmp->invisible) success += detect_curse_on_item(pl, tmp, skill);
724  FOR_INV_FINISH();
725 
726  /* Check ground, too, but only objects the player could pick up. Cauldrons are exceptions,
727  * you definitely want to know if they are cursed */
728  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
729  if (object_can_pick(pl, tmp) || QUERY_FLAG(tmp, FLAG_IS_CAULDRON))
730  success += detect_curse_on_item(pl, tmp, skill);
731  FOR_MAP_FINISH();
732 
733  return success;
734 }
748 int detect_magic_on_item(object *pl, object *tmp, object *skill) {
750  && (is_magical(tmp)) && tmp->item_power < skill->level) {
752  esrv_update_item(UPD_FLAGS, pl, tmp);
753  return calc_skill_exp(pl, tmp, skill);
754  }
755  return 0;
756 }
757 
768 static int do_skill_detect_magic(object *pl, object *skill) {
769  int success = 0;
770 
771  FOR_INV_PREPARE(pl, tmp)
772  if (!tmp->invisible)
773  success += detect_magic_on_item(pl, tmp, skill);
774  FOR_INV_FINISH();
775 
776  /* Check ground, too, but like above, only if the object can be picked up*/
777  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
778  if (object_can_pick(pl, tmp))
779  success += detect_magic_on_item(pl, tmp, skill);
780  FOR_MAP_FINISH();
781 
782  return success;
783 }
784 
800 int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success) {
801  int success = 0, chance, ip;
802  int skill_value = (skill->level && pl->stats.Int) ? pl->stats.Int : 10;
803 
804  if (!tmp->invisible && !QUERY_FLAG(tmp, FLAG_NO_SKILL_IDENT) && !is_identified(tmp)) {
805  ip = tmp->magic;
806  if (tmp->item_power > ip)
807  ip = tmp->item_power;
808 
809  chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
810  if (skill_value >= chance) {
811  tmp = identify(tmp);
812  if (pl->type == PLAYER && print_on_success) {
813  char desc[MAX_BUF];
814 
816  "You identify %s.",
817  ob_describe(tmp, pl, 1, desc, sizeof(desc)));
818  if (tmp->msg) {
820  "The item has a story:\n%s",
821  tmp->msg);
822  }
823  }
824  success += calc_skill_exp(pl, tmp, skill);
825  } else
827  }
828  return success;
829 }
830 
843 static int do_skill_ident(object *pl, int obj_class, object *skill) {
844  int success = 0, area, i;
845 
846  /* check the player */
847  FOR_INV_PREPARE(pl, tmp)
848  if (tmp->type == obj_class)
849  success += identify_object_with_skill(tmp, pl, skill, 1);
850  FOR_INV_FINISH();
851 
852  /* check the ground */
853  /* Altered to allow ident skills to increase in area with
854  * experience. -- Aaron Baugher
855  */
856 
857  if (skill->level > 64) { /* Adjust these levels? */
858  area = 49;
859  } else if (skill->level > 16) {
860  area = 25;
861  } else if (skill->level > 4) {
862  area = 9;
863  } else {
864  area = 1;
865  }
866 
867  for (i = 0; i < area; i++) {
868  int16_t x = pl->x+freearr_x[i];
869  int16_t y = pl->y+freearr_y[i];
870  mapstruct *m = pl->map;
871  int mflags;
872 
873  mflags = get_map_flags(m, &m, x, y, &x, &y);
874  if (mflags&P_OUT_OF_MAP)
875  continue;
876 
877  if (can_see_monsterP(m, pl->x, pl->y, i)) {
878  FOR_MAP_PREPARE(m, x, y, tmp)
879  if (tmp->type == obj_class)
880  success += identify_object_with_skill(tmp, pl, skill, 1);
881  FOR_MAP_FINISH();
882  }
883  }
884  return success;
885 }
886 
896 int skill_ident(object *pl, object *skill) {
897  int success = 0;
898  int i, identifiable_types=0;
899  const typedata *tmptype;
900 
901  if (pl->type != PLAYER)
902  return 0; /* only players will skill-identify */
903 
905  "You look at the objects nearby...");
906 
907  switch (skill->subtype) {
908  case SK_DET_CURSE:
909  success = do_skill_detect_curse(pl, skill);
910  if (success)
912  "...and discover cursed items!");
913  break;
914 
915  case SK_DET_MAGIC:
916  success = do_skill_detect_magic(pl, skill);
917  if (success)
919  "...and discover items imbued with mystic forces!");
920  break;
921 
922  default:
923  /* we will try to identify items with this skill instead */
924  for (i=0; i<=OBJECT_TYPE_MAX; i++) {
925  tmptype = get_typedata(i);
926  if (tmptype) {
927  if (skill->subtype == tmptype->identifyskill || skill->subtype == tmptype->identifyskill2) {
928  success += do_skill_ident(pl, i, skill);
929  identifiable_types++;
930  }
931  }
932  }
933  if (identifiable_types == 0) {
934  LOG(llevError, "Error: skill_ident() called with skill %d which can't identify any items\n", skill->subtype);
935  return 0;
936  break;
937  }
938  break;
939  }
940  if (!success) {
942  "...and learn nothing more.");
943  }
944  return success;
945 }
946 
947 
966 int use_oratory(object *pl, int dir, object *skill) {
967  int16_t x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
968  int mflags, chance;
969  object *tmp;
970  mapstruct *m;
971  char name[MAX_BUF];
972 
973  if (pl->type != PLAYER)
974  return 0; /* only players use this skill */
975  m = pl->map;
976  mflags = get_map_flags(m, &m, x, y, &x, &y);
977  if (mflags&P_OUT_OF_MAP)
978  return 0;
979 
980  /* Save some processing - we have the flag already anyways
981  */
982  if (!(mflags&P_IS_ALIVE)) {
984  "There is nothing to orate to.");
985  return 0;
986  }
987 
988  tmp = NULL;
989  FOR_MAP_PREPARE(m, x, y, tmp2) {
990  tmp2 = HEAD(tmp2);
991  /* can't persuade players - return because there is nothing else
992  * on that space to charm.
993  */
994  if (tmp2->type == PLAYER)
995  return 0;
996 
997  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
998  const char *value = object_get_value(tmp2, "no_mood_change");
999  if (value && strcmp(value, "1") == 0)
1000  return 0;
1001 
1002  tmp = tmp2;
1003  break;
1004  }
1005  } FOR_MAP_FINISH();
1006 
1007  if (!tmp) {
1009  "There is nothing to orate to.");
1010  return 0;
1011  }
1012 
1013  query_name(tmp, name, MAX_BUF);
1015  "You orate to the %s.",
1016  name);
1017 
1018  /* the following conditions limit who may be 'charmed' */
1019 
1020  /* it's hostile! */
1021  if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1022  query_name(tmp, name, MAX_BUF);
1024  "Too bad the %s isn't listening!",
1025  name);
1026  return 0;
1027  }
1028 
1029  /* it's already allied! */
1030  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
1031  if (object_get_owner(tmp) == pl) {
1033  "Your follower loves your speech.");
1034  return 0;
1035  }
1036 
1037  if (skill->level > tmp->level) {
1038  /* you steal the follower. Perhaps we should really look at the
1039  * level of the owner above?
1040  */
1041  object_set_owner(tmp, pl);
1042  query_name(tmp, name, MAX_BUF);
1044  "You convince the %s to follow you instead!",
1045  name);
1046 
1047  FREE_AND_COPY(tmp->skill, skill->skill);
1048 
1049  /* Abuse fix - don't give exp since this can otherwise
1050  * be used by a couple players to gets lots of exp.
1051  */
1052  return 0;
1053  }
1054 
1055  /* In this case, you can't steal it from the other player */
1056  return 0;
1057  } /* Creature was already a pet of someone */
1058 
1059  chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
1060 
1061  /* Ok, got a 'sucker' lets try to make them a follower */
1062  if (chance > 0 && tmp->level < random_roll(0, chance-1, pl, PREFER_HIGH)-1) {
1063  int64_t exp;
1064  query_name(tmp, name, MAX_BUF);
1066  "You convince the %s to become your follower.",
1067  name);
1068 
1069  object_set_owner(tmp, pl);
1070  /* compute exp before setting to 0, else the monster's experience is not taken into account. */
1071  tmp->stats.exp /= 5; /* why 5? because. */
1072  exp = calc_skill_exp(pl, tmp, skill);
1073  tmp->stats.exp = 0;
1074  add_friendly_object(tmp);
1075  SET_FLAG(tmp, FLAG_FRIENDLY);
1076  tmp->attack_movement = PETMOVE;
1077  /* keep oratory skill, so exp goes where it should if the pet kills something */
1078  FREE_AND_COPY(tmp->skill, skill->skill);
1079  return exp;
1080  }
1081 
1082  /* Charm failed. Creature may be angry now */
1083  if (skill->level+(pl->stats.Cha-10)/2 < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
1084  query_name(tmp, name, MAX_BUF);
1086  "Your speech angers the %s!",
1087  name);
1088 
1089  if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1090  CLEAR_FLAG(tmp, FLAG_FRIENDLY);
1092  tmp->attack_movement = 0; /* needed? */
1093  }
1095  }
1096 
1097  return 0; /* Fall through - if we get here, we didn't charm anything */
1098 }
1099 
1120 int singing(object *pl, int dir, object *skill) {
1121  int i, exp = 0, chance, mflags;
1122  object *tmp;
1123  mapstruct *m;
1124  int16_t x, y;
1125  char name[MAX_BUF];
1126  const char *value;
1127 
1128  if (pl->type != PLAYER)
1129  return 0; /* only players use this skill */
1130 
1132  "You sing");
1133  for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
1134  x = pl->x+freearr_x[i];
1135  y = pl->y+freearr_y[i];
1136  m = pl->map;
1137 
1138  mflags = get_map_flags(m, &m, x, y, &x, &y);
1139  if (mflags&P_OUT_OF_MAP)
1140  continue;
1141  if (!(mflags&P_IS_ALIVE))
1142  continue;
1143 
1144  tmp = NULL;
1145  FOR_MAP_PREPARE(m, x, y, tmp2) {
1146  tmp2 = HEAD(tmp2);
1147  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1148  tmp = tmp2;
1149  break;
1150  }
1151  /* can't affect players */
1152  if (tmp2->type == PLAYER) {
1153  tmp = tmp2;
1154  break;
1155  }
1156  } FOR_MAP_FINISH();
1157 
1158  /* Whole bunch of checks to see if this is a type of monster that would
1159  * listen to singing.
1160  */
1161  if (tmp
1162  && QUERY_FLAG(tmp, FLAG_MONSTER)
1163  && !QUERY_FLAG(tmp, FLAG_NO_STEAL) /* Been charmed or abused before */
1164  && !QUERY_FLAG(tmp, FLAG_SPLITTING) /* no ears */
1165  && !QUERY_FLAG(tmp, FLAG_HITBACK) /* was here before */
1166  && (tmp->level <= skill->level)
1167  && !QUERY_FLAG(tmp, FLAG_UNDEAD)
1168  && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) /* already calm */
1169  && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) { /* already calm */
1170  /* stealing isn't really related (although, maybe it should
1171  * be). This is mainly to prevent singing to the same monster
1172  * over and over again and getting exp for it.
1173  */
1174  chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
1175 
1176  value = object_get_value(tmp, "no_mood_change");
1177  if (value && strcmp(value, "1") == 0)
1178  chance = 0;
1179 
1180  if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
1182  query_name(tmp, name, MAX_BUF);
1184  "You calm down the %s",
1185  name);
1186 
1187  /* Give exp only if they are not aware */
1188  if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
1189  exp += calc_skill_exp(pl, tmp, skill);
1190  SET_FLAG(tmp, FLAG_NO_STEAL);
1191  } else {
1192  query_name(tmp, name, MAX_BUF);
1194  "Too bad the %s isn't listening!",
1195  name);
1196  SET_FLAG(tmp, FLAG_NO_STEAL);
1197  }
1198  }
1199  }
1200  return exp;
1201 }
1202 
1213 int find_traps(object *pl, object *skill) {
1214  int i, expsum = 0, mflags;
1215  int16_t x, y;
1216  mapstruct *m;
1217 
1218  /* First we search all around us for runes and traps, which are
1219  * all type RUNE
1220  */
1221 
1222  for (i = 0; i < 9; i++) {
1223  x = pl->x+freearr_x[i];
1224  y = pl->y+freearr_y[i];
1225  m = pl->map;
1226 
1227  mflags = get_map_flags(m, &m, x, y, &x, &y);
1228  if (mflags&P_OUT_OF_MAP)
1229  continue;
1230 
1231  /* Check everything in the square for trapness */
1232  FOR_MAP_PREPARE(m, x, y, tmp) {
1233  /* And now we'd better do an inventory traversal of each
1234  * of these objects' inventory
1235  * We can narrow this down a bit - no reason to search through
1236  * the players inventory or monsters for that matter.
1237  */
1238  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1239  FOR_INV_PREPARE(tmp, tmp2)
1240  if (tmp2->type == RUNE || tmp2->type == TRAP)
1241  if (trap_see(pl, tmp2)) {
1242  trap_show(tmp2, tmp);
1243  if (tmp2->stats.Cha > 1) {
1244  object *owner;
1245 
1246  owner = object_get_owner(tmp2);
1247  if (owner == NULL || owner->type != PLAYER)
1248  expsum += calc_skill_exp(pl, tmp2, skill);
1249 
1250  tmp2->stats.Cha = 1; /* unhide the trap */
1251  }
1252  }
1253  FOR_INV_FINISH();
1254  }
1255  if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
1256  trap_show(tmp, tmp);
1257  if (tmp->stats.Cha > 1) {
1258  object *owner;
1259 
1260  owner = object_get_owner(tmp);
1261  if (owner == NULL || owner->type != PLAYER)
1262  expsum += calc_skill_exp(pl, tmp, skill);
1263  tmp->stats.Cha = 1; /* unhide the trap */
1264  }
1265  }
1266  } FOR_MAP_FINISH();
1267  }
1269  "You search the area.");
1270  return expsum;
1271 }
1272 
1284 int remove_trap(object *op, object *skill) {
1285  int i, success = 0, mflags;
1286  mapstruct *m;
1287  int16_t x, y;
1288 
1289  for (i = 0; i < 9; i++) {
1290  x = op->x+freearr_x[i];
1291  y = op->y+freearr_y[i];
1292  m = op->map;
1293 
1294  mflags = get_map_flags(m, &m, x, y, &x, &y);
1295  if (mflags&P_OUT_OF_MAP)
1296  continue;
1297 
1298  /* Check everything in the square for trapness */
1299  FOR_MAP_PREPARE(m, x, y, tmp) {
1300  /* And now we'd better do an inventory traversal of each
1301  * of these objects inventory. Like above, only
1302  * do this for interesting objects.
1303  */
1304 
1305  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1306  FOR_INV_PREPARE(tmp, tmp2)
1307  if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
1308  object *owner;
1309 
1310  trap_show(tmp2, tmp);
1311  if (trap_disarm(op, tmp2, 1, skill) && ((owner = object_get_owner(tmp2)) == NULL || owner->type != PLAYER)) {
1312  tmp2->stats.exp = tmp2->stats.Cha*tmp2->level;
1313  success += calc_skill_exp(op, tmp2, skill);
1314  } else {
1315  /* Can't continue to disarm after failure */
1316  return success;
1317  }
1318  }
1319  FOR_INV_FINISH();
1320  }
1321  if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
1322  object *owner;
1323 
1324  trap_show(tmp, tmp);
1325  if (trap_disarm(op, tmp, 1, skill) && ((owner = object_get_owner(tmp)) == NULL || owner->type != PLAYER)) {
1326  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1327  success += calc_skill_exp(op, tmp, skill);
1328  } else {
1329  /* Can't continue to disarm after failure */
1330  return success;
1331  }
1332  }
1333  } FOR_MAP_FINISH();
1334  }
1335  return success;
1336 }
1337 
1338 
1357 int pray(object *pl, object *skill) {
1358  char buf[MAX_BUF];
1359 
1360  if (pl->type != PLAYER)
1361  return 0;
1362 
1363  snprintf(buf, sizeof(buf), "You pray.");
1364 
1365  /* Check all objects - we could stop at floor objects,
1366  * but if someone buries an altar, I don't see a problem with
1367  * going through all the objects, and it shouldn't be much slower
1368  * than extra checks on object attributes.
1369  */
1370  FOR_BELOW_PREPARE(pl, tmp)
1371  /* Only if the altar actually belongs to someone do you get special benefits */
1372  if (tmp->type == HOLY_ALTAR && tmp->other_arch) {
1373  snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
1374  pray_at_altar(pl, tmp, skill);
1375  break; /* Only pray at one altar */
1376  }
1377  FOR_BELOW_FINISH();
1378 
1380  buf);
1381 
1382  if (pl->stats.grace < pl->stats.maxgrace) {
1383  pl->stats.grace++;
1384  pl->last_grace = -1;
1385  }
1386  return 0;
1387 }
1388 
1405 void meditate(object *pl, object *skill) {
1406  if (pl->type != PLAYER)
1407  return; /* players only */
1408 
1409  /* check if pl has removed encumbering armour and weapons */
1410  if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && skill->level < 6) {
1412  "You can't concentrate while wielding a weapon!");
1413  return;
1414  }
1415 
1416  FOR_INV_PREPARE(pl, tmp)
1417  if (((tmp->type == ARMOUR && skill->level < 12)
1418  || (tmp->type == HELMET && skill->level < 10)
1419  || (tmp->type == SHIELD && skill->level < 6)
1420  || (tmp->type == BOOTS && skill->level < 4)
1421  || (tmp->type == GLOVES && skill->level < 2))
1422  && QUERY_FLAG(tmp, FLAG_APPLIED)) {
1424  "You can't concentrate while wearing so much armour!");
1425  return;
1426  }
1427  FOR_INV_FINISH();
1428 
1429  /* ok let's meditate! Spell points are regained first, then once
1430  * they are maxed we get back hp. Actual incrementing of values
1431  * is handled by the do_some_living() (in player.c). This way magical
1432  * bonuses for healing/sp regeneration are included properly
1433  * No matter what, we will eat up some playing time trying to
1434  * meditate. (see 'factor' variable for what sets the amount of time)
1435  */
1436 
1438  "You meditate.");
1439 
1440  if (pl->stats.sp < pl->stats.maxsp) {
1441  pl->stats.sp++;
1442  pl->last_sp = -1;
1443  } else if (pl->stats.hp < pl->stats.maxhp) {
1444  pl->stats.hp++;
1445  pl->last_heal = -1;
1446  }
1447 }
1448 
1462 static int write_note(object *pl, object *item, const char *msg) {
1463  char buf[BOOK_BUF];
1464  object *newBook = NULL;
1465 
1466  // The item should exist and be a book, but check it just in case.
1467  if (!item || item->type != BOOK) {
1469  "That was interesting...");
1470  // TODO: Print a scary log message.
1471  return 0;
1472  }
1473 
1474  // The message should never be NULL, but check it just in case.
1475  if (!msg) {
1477  "Hmm... what was I going to write?");
1478  // TODO: Print a scary log message.
1479  return 0;
1480  }
1481 
1482  // Don't let the player write a reserved keyword.
1483  if (strcasestr_local(msg, "endmsg")) {
1485  "Trying to cheat now are we?");
1486  return 0;
1487  }
1488 
1489  /* Lauwenmark: Handle for plugin book writing (trigger) event */
1490  if (execute_event(item, EVENT_TRIGGER, pl, NULL, msg, SCRIPT_FIX_ALL) != 0)
1491  return strlen(msg);
1492 
1493  buf[0] = 0;
1494 
1495  // Write the message in the book if it doesn't overflow the buffer.
1496  if (!book_overflow(item->msg, msg, BOOK_BUF)) {
1497  // TODO: Garble some characters depending on intelligence/skill.
1498 
1499  // If there was already text, append the new message on the end.
1500  if (item->msg) {
1501  snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
1502  } else {
1503  snprintf(buf, sizeof(buf), "%s\n", msg);
1504  }
1505 
1506  // If there were multiple items in a stack, unstack one and write.
1507  if (item->nrof > 1) {
1508  newBook = object_new();
1509  object_copy(item, newBook);
1511  newBook->nrof = 1;
1512  object_set_msg(newBook, buf);
1513  newBook = object_insert_in_ob(newBook, pl);
1514  } else {
1515  object_set_msg(item, buf);
1516 
1517  // This shouldn't be necessary; the object hasn't changed visibly.
1518  //esrv_send_item(pl, item);
1519  }
1520 
1521  // Tell the player that he/she wrote in the object.
1522  query_short_name(item, buf, BOOK_BUF);
1525  "You write in the %s.", buf);
1526 
1527  // Give the player experience for writing.
1528  return strlen(msg);
1529  } else {
1530  query_short_name(item, buf, BOOK_BUF);
1533  "Your message won't fit in the %s!", buf);
1534  return 0;
1535  }
1536 }
1537 
1552 static int write_scroll(object *pl, object *scroll, object *skill) {
1553  int success = 0, confused = 0, grace_cost = 0;
1554  object *newscroll, *chosen_spell, *tmp;
1555 
1556  // The item should exist and be a scroll, but check it just in case.
1557  if (!scroll || scroll->type != SCROLL) {
1559  "A spell must be written on a magic scroll!");
1560  // TODO: Print a scary log message.
1561  return 0;
1562  }
1563 
1564  // The player must have a spell readied to inscribe.
1565  chosen_spell = pl->contr->ranges[range_magic];
1566  if (!chosen_spell) {
1568  "You should ready the spell you wish to inscribe.");
1569  return 0;
1570  }
1571 
1572  // Make sure the player has enough SP or grace to write the spell.
1573  grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1574  if (grace_cost > 0 && grace_cost > pl->stats.grace) {
1577  "You don't have enough grace to write a scroll of %s.",
1578  chosen_spell->name);
1579  return 0;
1580  }
1581 
1582  if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
1585  "You don't have enough mana to write a scroll of %s.",
1586  chosen_spell->name);
1587  return 0;
1588  }
1589 
1590  // Prevent players from writing spells that they are denied.
1591  if (chosen_spell->path_attuned & pl->path_denied
1593  char name[MAX_BUF];
1594 
1595  query_name(chosen_spell, name, MAX_BUF);
1598  "Just the idea of writing a scroll of %s makes you sick!",
1599  name);
1600  return 0;
1601  }
1602 
1603  // If the scroll already had a spell written on it, the player could
1604  // accidentally read it while trying to write a new one. Give the player
1605  // a 50% chance to overwrite a spell at their own level.
1606  if ((scroll->stats.sp || scroll->inv) &&
1607  random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
1609  "Oops! You accidently read it while trying to write on it.");
1610  apply_manual(pl, scroll, 0);
1611  return 0;
1612  }
1613 
1614  if (execute_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0) {
1615  return 0;
1616  }
1617 
1618  // Find out if the player is confused or not.
1619  if (QUERY_FLAG(pl, FLAG_CONFUSED)) {
1620  confused = 1;
1621  }
1622 
1623  // Mana or grace is lost no matter if the inscription is successful.
1624  pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1625  pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
1626 
1627  if (random_roll(0, chosen_spell->level * 4 - 1, pl, PREFER_LOW) <
1628  skill->level) {
1629  // If there were multiple items in a stack, unstack one.
1630  if (scroll->nrof > 1) {
1631  newscroll = object_new();
1632  object_copy(scroll, newscroll);
1634  newscroll->nrof = 1;
1635  } else {
1636  newscroll = scroll;
1637  }
1638 
1639  // Write spell if not confused; otherwise write random spell.
1640  newscroll->level = MAX(skill->level, chosen_spell->level);
1641 
1642  if (!confused) {
1643  draw_ext_info(
1645  "You succeed in writing a new scroll.");
1646  } else {
1647  chosen_spell = find_random_spell_in_ob(pl, NULL);
1648  if (!chosen_spell) {
1649  return 0;
1650  }
1651 
1652  draw_ext_info(
1654  "In your confused state, you write down some odd spell.");
1655  }
1656 
1657  if (newscroll->inv) {
1658  object *ninv;
1659  ninv = newscroll->inv;
1660  object_remove(ninv);
1662  }
1663 
1664  tmp = object_new();
1665  object_copy(chosen_spell, tmp);
1666  object_insert_in_ob(tmp, newscroll);
1667 
1668  // This is needed so casting from the scroll works correctly with
1669  // moving_ball types, which checks attunements.
1670  newscroll->path_attuned = tmp->path_repelled;
1671 
1672  // Same code as from treasure.c - so they can better merge.
1673  // If players want to sell them, so be it.
1674  newscroll->value = newscroll->arch->clone.value *
1675  newscroll->inv->value * (newscroll->level + 50 ) /
1676  (newscroll->inv->level + 50);
1677  newscroll->stats.exp = newscroll->value / 5;
1678 
1679  // Finish manipulating the scroll before inserting it.
1680  if (newscroll == scroll) {
1681  // Remove object to stack correctly with other items.
1682  object_remove(newscroll);
1683  }
1684 
1685  newscroll = object_insert_in_ob(newscroll, pl);
1686  success = calc_skill_exp(pl, newscroll, skill);
1687 
1688  if (!confused) {
1689  success *= 2;
1690  }
1691 
1692  success = success * skill->level;
1693  return success;
1694  }
1695 
1696  // Inscription wasn't successful; do something bad to the player.
1697  if (chosen_spell->level > skill->level || confused) {
1698  draw_ext_info(
1700  "Ouch! Your attempt to write a new scroll strains your mind!");
1701 
1702  // Either drain a stat or subtract experience.
1703  if (random_roll(0, 1, pl, PREFER_LOW) == 1) {
1704  drain_specific_stat(pl, 4);
1705  } else {
1706  confuse_living(pl, pl, 99);
1707  return -30 * chosen_spell->level;
1708  }
1709  } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
1710  draw_ext_info(
1712  "Your attempt to write a new scroll rattles your mind!");
1713  confuse_living(pl, pl, 99);
1714  } else {
1715  draw_ext_info(
1717  "You fail to write a new scroll.");
1718  }
1719 
1720  return 0;
1721 }
1722 
1736 int write_on_item(object *pl, const char *params, object *skill) {
1737  object *item;
1738  const char *string = params;
1739  int msgtype;
1740  archetype *skat;
1741 
1742  // Only players can use the inscription skill.
1743  if (pl->type != PLAYER) {
1744  return 0;
1745  }
1746 
1747  // No message was given, so both strings are set to empty.
1748  if (!params) {
1749  params = "";
1750  string = params;
1751  }
1752 
1753  // You must be able to read before you can write.
1755 
1756  if (!find_skill_by_name(pl, skat->clone.skill)) {
1758  "You must learn to read before you can write!");
1759  return 0;
1760  }
1761 
1762  // You must not be blind to write, unless you're a DM.
1763  if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
1765  "You are unable to write while blind.");
1766  return 0;
1767  }
1768 
1769  // If a message was given, write a book. Otherwise, write a scroll.
1770  if (string[0] != '\0') {
1771  msgtype = BOOK;
1772  } else {
1773  msgtype = SCROLL;
1774  }
1775 
1776  // Find and attempt to write on the player's marked item.
1777  item = find_marked_object(pl);
1778  if (item == NULL) {
1780  "You haven't marked any items to write on yet.");
1781  return 0;
1782  }
1783 
1784  // Don't let the player write on an unpaid item.
1785  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1787  "You had better pay for that before you write on it!");
1788  return 0;
1789  }
1790 
1791  // Check if the marked item is the type of item we're writing.
1792  if (msgtype != item->type) {
1795  "You must mark a %s to write %s.",
1796  msgtype == BOOK ? "book" : "magic scroll",
1797  msgtype == BOOK ? "your message on" : "your spell down");
1798  return 0;
1799  }
1800 
1801  if (msgtype == BOOK) {
1802  return write_note(pl, item, string);
1803  } else if (msgtype == SCROLL) {
1804  return write_scroll(pl, item, skill);
1805  } else {
1806  // This case should never be reached.
1807  abort();
1808  }
1809 }
1810 
1823 static object *find_throw_ob(object *op, sstring race) {
1824  object *tmp;
1825  char name[MAX_BUF];
1826 
1827  if (!op) { /* safety */
1828  LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1829  return (object *)NULL;
1830  }
1831 
1832  /* prefer marked item */
1833  tmp = find_marked_object(op);
1834  if (tmp != NULL) {
1835  /* can't toss invisible or inv-locked items */
1836  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1837  tmp = NULL;
1838  }
1839  }
1840 
1841  /* look through the inventory if no marked found */
1842  if (tmp == NULL) {
1843  FOR_INV_PREPARE(op, tmp2) {
1844  /* can't toss invisible items */
1845  if (tmp2->invisible)
1846  continue;
1847 
1848  if (tmp2->type == CONTAINER && QUERY_FLAG(tmp2, FLAG_APPLIED) && tmp2->race == race) {
1849  tmp = find_throw_ob(tmp2, race);
1850  if (tmp != NULL) {
1851  break;
1852  }
1853  }
1854 
1855  /* if not a container, then don't look if locked */
1856  if (tmp2->type == CONTAINER || QUERY_FLAG(tmp2, FLAG_INV_LOCKED))
1857  continue;
1858 
1859  if (tmp2->race == race) {
1860  tmp = tmp2;
1861  break;
1862  }
1863 
1864  } FOR_INV_FINISH();
1865  }
1866 
1867  /* this should prevent us from throwing away
1868  * cursed items, worn armour, etc. Only weapons
1869  * can be thrown from 'hand'.
1870  */
1871  if (!tmp)
1872  return NULL;
1873 
1874  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1875  if (tmp->type != WEAPON) {
1876  query_name(tmp, name, MAX_BUF);
1878  "You can't throw %s.",
1879  name);
1880  tmp = NULL;
1881  } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
1882  query_name(tmp, name, MAX_BUF);
1885  "The %s sticks to your hand!",
1886  name);
1887  tmp = NULL;
1888  } else {
1889  if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) {
1890  LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1891  tmp = NULL;
1892  }
1893  }
1894  } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1895  query_name(tmp, name, MAX_BUF);
1897  "You should pay for the %s first.",
1898  name);
1899  tmp = NULL;
1900  }
1901 
1902  if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1903  LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
1904  tmp = NULL;
1905  }
1906  return tmp;
1907 }
1908 
1920 static object *make_throw_ob(object *orig) {
1921  object *toss_item;
1922 
1923  if (!orig)
1924  return NULL;
1925 
1926  toss_item = object_new();
1927  if (QUERY_FLAG(orig, FLAG_APPLIED)) {
1928  LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
1929  /* insufficient workaround, but better than nothing */
1930  CLEAR_FLAG(orig, FLAG_APPLIED);
1931  }
1932  object_copy(orig, toss_item);
1933  toss_item->type = THROWN_OBJ;
1934  CLEAR_FLAG(toss_item, FLAG_CHANGING);
1935  toss_item->stats.dam = 0; /* default damage */
1936  object_insert_in_ob(orig, toss_item);
1937  return toss_item;
1938 }
1939 
1940 
1961 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
1962  object *throw_ob = toss_item, *left = NULL;
1963  int eff_str = 0, str = op->stats.Str, dam = 0;
1964  int pause_f, weight_f = 0, mflags;
1965  float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1966  mapstruct *m;
1967  int16_t sx, sy;
1968  tag_t tag;
1969  char name[MAX_BUF];
1970 
1971  if (throw_ob == NULL) {
1972  if (op->type == PLAYER) {
1974  "You have nothing to throw.");
1975  }
1976  return 0;
1977  }
1978  if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
1979  if (op->type == PLAYER) {
1981  "The gods won't let you throw that.");
1982  }
1983  return 0;
1984  }
1985 
1986  /* Lauwenmark - Now we can call the associated script_throw event (if any) */
1987  tag = throw_ob->count;
1988  execute_event(throw_ob, EVENT_THROW, op, NULL, NULL, SCRIPT_FIX_ACTIVATOR);
1989  if (object_was_destroyed(throw_ob, tag)) {
1990  return 1;
1991  }
1992 
1993  /* Because throwing effectiveness must be reduced by the
1994  * encumbrance of the thrower and weight of the object. THus,
1995  * we use the concept of 'effective strength' as defined below.
1996  */
1997 
1998  /* if str exceeds settings.max_stat (30, eg giants), lets assign a str_factor > 1 */
1999  if (str > settings.max_stat) {
2000  str_factor = (float)str/(float)settings.max_stat;
2001  str = settings.max_stat;
2002  }
2003 
2004  /* the more we carry, the less we can throw. Limit only on players */
2005  if (op->type == PLAYER
2007  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
2022  load_factor = 2.0f - (float)(op->carrying) / (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT);
2023  // Only clip to 1.0 if we get in here, since it is 1.0 if we do not get in here.
2024  load_factor = MIN(load_factor, 1.0f);
2025  }
2026 
2027  /* lighter items are thrown harder, farther, faster */
2028  if (throw_ob->weight > 0)
2029  item_factor = (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)/(float)(3.0*throw_ob->weight);
2030  else { /* 0 or negative weight?!? Odd object, can't throw it */
2031  query_name(throw_ob, name, MAX_BUF);
2033  "You can't throw %s.",
2034  name);
2035  return 0;
2036  }
2037 
2038  eff_str = str*load_factor;
2039  eff_str = (float)eff_str*item_factor*str_factor;
2040 
2041  /* alas, arrays limit us to a value of settings.max_stat (30). Use str_factor to
2042  * account for super-strong throwers. */
2043  if (eff_str > settings.max_stat)
2044  eff_str = settings.max_stat;
2045 
2046 #ifdef DEBUG_THROW
2047  LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
2048  LOG(llevDebug, " max_c=%d, item_f=%f, load_f=%f, str=%d\n", (get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT), item_factor, load_factor, op->stats.Str);
2049  LOG(llevDebug, " str_factor=%f\n", str_factor);
2050  LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
2051 #endif
2052 
2053  /* 3 things here prevent a throw, you aimed at your feet, you
2054  * have no effective throwing strength, or you threw at something
2055  * that flying objects can't get through.
2056  */
2057  mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
2058 
2059  if (!dir
2060  || (eff_str <= 1)
2061  || (mflags&P_OUT_OF_MAP)
2062  || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
2063  /* bounces off 'wall', and drops to feet */
2064  object_remove(throw_ob);
2065  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2066  if (op->type == PLAYER) {
2067  if (eff_str <= 1) {
2068  query_name(throw_ob, name, MAX_BUF);
2070  "Your load is so heavy you drop %s to the ground.",
2071  name);
2072  } else if (!dir) {
2073  query_name(throw_ob, name, MAX_BUF);
2075  "You throw %s at the ground.",
2076  name);
2077  } else
2079  "Something is in the way.");
2080  }
2081  return 0;
2082  } /* if object can't be thrown */
2083 
2084  left = throw_ob; /* these are throwing objects left to the player */
2085 
2086  /* sometimes object_split() can't split an object (because op->nrof==0?)
2087  * and returns NULL. We must use 'left' then
2088  */
2089  throw_ob = object_split(throw_ob, 1, NULL, 0);
2090  if (throw_ob == NULL) {
2091  throw_ob = left;
2092  object_remove(left);
2093  }
2094 
2095  /* special case: throwing powdery substances like dust, dirt */
2096  if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
2097  cast_dust(op, throw_ob, dir);
2098  return 1;
2099  }
2100 
2101  /* Make a thrown object -- insert real object in a 'carrier' object.
2102  * If unsuccessfull at making the "thrown_obj", we just reinsert
2103  * the original object back into inventory and exit
2104  */
2105  toss_item = make_throw_ob(throw_ob);
2106  if (toss_item) {
2107  throw_ob = toss_item;
2108  if (throw_ob->skill)
2109  free_string(throw_ob->skill);
2110  throw_ob->skill = add_string(skill->skill);
2111  } else {
2112  object_insert_in_ob(throw_ob, op);
2113  return 0;
2114  }
2115 
2116  object_set_owner(throw_ob, op);
2117  /* At some point in the attack code, the actual real object (op->inv)
2118  * becomes the hitter. As such, we need to make sure that has a proper
2119  * owner value so exp goes to the right place.
2120  */
2121  object_set_owner(throw_ob->inv, op);
2122  throw_ob->direction = dir;
2123 
2124  /* the damage bonus from the force of the throw */
2125  dam = str_factor*get_dam_bonus(eff_str);
2126 
2127  /* Now, lets adjust the properties of the thrown_ob. */
2128 
2129  /* how far to fly */
2130  throw_ob->last_sp = (eff_str*3)/5;
2131 
2132  /* speed */
2133  throw_ob->speed = (get_speed_bonus(eff_str)+1.0)/1.5;
2134  throw_ob->speed = MIN(1.0, throw_ob->speed); /* no faster than an arrow! */
2135 
2136  /* item damage. Eff_str and item weight influence damage done */
2137  weight_f = MIN(throw_ob->weight/2000, settings.max_stat);
2138  throw_ob->stats.dam += (dam/3)+get_dam_bonus(weight_f)+(throw_ob->weight/15000)-2;
2139 
2140  /* chance of breaking. Proportional to force used and weight of item */
2141  throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
2142 
2143  /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
2144  throw_ob->stats.wc = 25-get_dex_bonus(op->stats.Dex)-get_thaco_bonus(eff_str)-skill->level;
2145 
2146 
2147  /* the properties of objects which are meant to be thrown (ie dart,
2148  * throwing knife, etc) will differ from ordinary items. Lets tailor
2149  * this stuff in here.
2150  */
2151 
2152  if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
2153  throw_ob->last_sp += eff_str/3; /* fly a little further */
2154  throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
2155  throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
2156  /* only throw objects get directional faces */
2157  if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
2158  object_update_turn_face(throw_ob);
2159  } else {
2160  /* some materials will adjust properties.. */
2161  if (throw_ob->material&M_LEATHER) {
2162  throw_ob->stats.dam -= 1;
2163  throw_ob->stats.food -= 10;
2164  }
2165  if (throw_ob->material&M_GLASS)
2166  throw_ob->stats.food += 60;
2167 
2168  if (throw_ob->material&M_ORGANIC) {
2169  throw_ob->stats.dam -= 3;
2170  throw_ob->stats.food += 55;
2171  }
2172  if (throw_ob->material&M_PAPER || throw_ob->material&M_CLOTH) {
2173  throw_ob->stats.dam -= 5;
2174  throw_ob->speed *= 0.8;
2175  throw_ob->stats.wc += 3;
2176  throw_ob->stats.food -= 30;
2177  }
2178  /* light obj have more wind resistance, fly slower*/
2179  if (throw_ob->weight > 500)
2180  throw_ob->speed *= 0.8;
2181  if (throw_ob->weight > 50)
2182  throw_ob->speed *= 0.5;
2183  } /* else tailor thrown object */
2184 
2185  /* some limits, and safeties (needed?) */
2186  if (throw_ob->stats.dam < 0)
2187  throw_ob->stats.dam = 0;
2188  if (throw_ob->last_sp > eff_str)
2189  throw_ob->last_sp = eff_str;
2190  if (throw_ob->stats.food < 0)
2191  throw_ob->stats.food = 0;
2192  if (throw_ob->stats.food > 100)
2193  throw_ob->stats.food = 100;
2194  if (throw_ob->stats.wc > 30)
2195  throw_ob->stats.wc = 30;
2196 
2197  /* how long to pause the thrower. Higher values mean less pause */
2198  pause_f = ((2*eff_str)/3)+20+skill->level;
2199 
2200  /* Put a lower limit on this */
2201  if (pause_f < 10)
2202  pause_f = 10;
2203  if (pause_f > 100)
2204  pause_f = 100;
2205 
2206  /* Changed in 0.94.2 - the calculation before was really goofy.
2207  * In short summary, a throw can take anywhere between speed 5 and
2208  * speed 0.5
2209  */
2210  op->speed_left -= 50.0/pause_f;
2211 
2212  object_update_speed(throw_ob);
2213  throw_ob->speed_left = 0;
2214 
2215  throw_ob->move_type = MOVE_FLY_LOW;
2216  throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
2217 
2218 #ifdef DEBUG_THROW
2219  LOG(llevDebug, " pause_f=%d \n", pause_f);
2220  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);
2221  LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
2222 #endif
2223  tag = throw_ob->count;
2224  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2225  if (!object_was_destroyed(throw_ob, tag))
2226  ob_process(throw_ob);
2227  return 1;
2228 }
2229 
2245 int skill_throw(object *op, object *part, int dir, object *skill) {
2246  object *throw_ob;
2247 
2248  if (op->type == PLAYER)
2249  throw_ob = find_throw_ob(op, find_string("throwing"));
2250  else
2251  throw_ob = monster_find_throw_ob(op);
2252 
2253  return do_throw(op, part, throw_ob, dir, skill);
2254 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:316
#define AP_UNAPPLY
Definition: define.h:613
void spring_trap(object *trap, object *victim)
Definition: rune.c:205
int8_t Int
Definition: living.h:36
Definition: player.h:92
int apply_special(object *who, object *op, int aflags)
Definition: apply.c:1155
static object * make_throw_ob(object *orig)
Definition: skills.c:1920
int apply_manual(object *op, object *tmp, int aflag)
Definition: apply.c:596
#define FLAG_DAMNED
Definition: define.h:318
#define FLAG_UNPAID
Definition: define.h:236
#define MOVE_WALK
Definition: define.h:407
#define UP_OBJ_FACE
Definition: object.h:519
MoveType move_type
Definition: object.h:426
#define MSG_TYPE_ITEM
Definition: newclient.h:388
void object_free(object *ob, int flags)
Definition: object.c:1345
MoveType move_on
Definition: object.h:429
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:646
uint8_t max_stat
Definition: global.h:326
Definition: map.h:380
static int adj_stealchance(object *op, object *victim, int roll)
Definition: skills.c:43
#define FLAG_SLEEP
Definition: define.h:308
int is_magical(const object *op)
Definition: item.c:1192
void remove_door(object *op)
Definition: time.c:37
#define FLAG_HITBACK
Definition: define.h:267
#define FOR_OB_AND_ABOVE_PREPARE(op_)
Definition: define.h:777
uint16_t attack_movement
Definition: object.h:393
static int attempt_jump(object *pl, int dir, int spaces, object *skill)
Definition: skills.c:553
static int do_skill_ident(object *pl, int obj_class, object *skill)
Definition: skills.c:843
#define SET_FLAG(xyz, p)
Definition: define.h:223
Definition: object.h:221
int identifyskill
Definition: define.h:93
int get_dam_bonus(int stat)
Definition: living.c:2372
#define MSG_TYPE_ITEM_INFO
Definition: newclient.h:641
uint16_t material
Definition: object.h:348
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.c:3978
#define FLAG_FRIENDLY
Definition: define.h:246
static int attempt_hide(object *op, object *skill)
Definition: skills.c:461
#define SPELL_GRACE
Definition: spells.h:59
int32_t last_heal
Definition: object.h:358
static int do_skill_detect_magic(object *pl, object *skill)
Definition: skills.c:768
int16_t maxgrace
Definition: living.h:45
void free_string(sstring str)
Definition: shstr.c:280
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.c:343
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4135
void pick_up(object *op, object *alt)
Definition: c_object.c:460
object clone
Definition: object.h:472
socket_struct socket
Definition: player.h:94
int16_t invisible
Definition: object.h:361
int check_pick(object *op)
Definition: player.c:1689
short freearr_x[SIZEOFFREE]
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:602
Definition: object.h:240
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.c:275
Definition: object.h:119
int isqrt(int n)
Definition: utils.c:586
Definition: object.h:136
#define FLAG_CONFUSED
Definition: define.h:312
int32_t last_sp
Definition: object.h:359
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:582
object * ranges[range_size]
Definition: player.h:103
void meditate(object *pl, object *skill)
Definition: skills.c:1405
int pray(object *pl, object *skill)
Definition: skills.c:1357
uint8_t subtype
Definition: object.h:340
#define M_CLOTH
Definition: material.h:21
#define MSG_TYPE_VICTIM_STEAL
Definition: newclient.h:648
uint8_t hide
Definition: object.h:389
int64_t exp
Definition: living.h:47
#define FREE_OBJ_NO_DESTROY_CALLBACK
Definition: object.h:533
struct obj * above
Definition: object.h:288
int stand_near_hostile(object *who)
Definition: player.c:4132
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1120
method_ret ob_process(object *op)
Definition: ob_methods.c:68
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:583
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:217
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.c:432
void remove_friendly_object(object *op)
Definition: friend.c:56
uint32_t path_attuned
Definition: object.h:344
#define MAX(x, y)
Definition: compat.h:20
void object_update(object *op, int action)
Definition: object.c:1199
#define MSG_TYPE_ITEM_REMOVE
Definition: newclient.h:638
int16_t sp
Definition: living.h:42
uint32_t get_weight_limit(int stat)
Definition: living.c:2356
#define NDI_BLACK
Definition: newclient.h:221
#define PETMOVE
Definition: define.h:519
uint32_t path_repelled
Definition: object.h:345
Definition: object.h:212
#define SCRIPT_FIX_ALL
Definition: global.h:367
uint32_t hidden
Definition: player.h:132
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:311
Definition: object.h:467
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:792
int16_t maxsp
Definition: living.h:43
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Definition: living.h:40
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:387
short freearr_y[SIZEOFFREE]
Definition: object.c:71
partylist * party
Definition: player.h:186
#define FLAG_KNOWN_MAGICAL
Definition: define.h:320
const typedata * get_typedata(int itemtype)
Definition: item.c:315
#define strcasestr_local
Definition: compat.h:24
int rndm(int min, int max)
Definition: utils.c:162
#define FREE_PLAYER_LOAD_PERCENT
Definition: config.h:98
#define FLAG_UNDEAD
Definition: define.h:270
void object_set_owner(object *op, object *owner)
Definition: object.c:604
int trap_show(object *trap, object *where)
Definition: rune.c:407
Definition: object.h:241
int16_t y
Definition: object.h:326
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1848
int16_t maxhp
Definition: living.h:41
sstring find_string(const char *str)
Definition: shstr.c:236
void object_update_turn_face(object *op)
Definition: object.c:1078
Definition: object.h:118
uint32_t path_denied
Definition: object.h:346
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.c:2266
static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill)
Definition: skills.c:1961
Definition: object.h:114
int is_identified(const object *op)
Definition: item.c:1313
#define MSG_TYPE_VICTIM
Definition: newclient.h:392
object * object_new(void)
Definition: object.c:1019
#define FOR_OB_AND_ABOVE_FINISH()
Definition: define.h:781
int object_can_pick(const object *who, const object *item)
Definition: object.c:3656
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2601
float speed_left
Definition: object.h:329
#define FLAG_NO_STEAL
Definition: define.h:343
signed short int16_t
Definition: win32.h:160
static object * find_throw_ob(object *op, sstring race)
Definition: skills.c:1823
int32_t weight
Definition: object.h:366
int hide(object *op, object *skill)
Definition: skills.c:492
#define MOVE_FLY_LOW
Definition: define.h:408
uint32_t tmp_invis
Definition: player.h:125
int8_t Wis
Definition: living.h:36
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
void monster_npc_call_help(object *op)
Definition: monster.c:1837
struct mapdef * map
Definition: object.h:297
#define BOOK_BUF
Definition: book.h:16
#define snprintf
Definition: win32.h:46
int remove_trap(object *op, object *skill)
Definition: skills.c:1284
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.c:725
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:380
int detect_curse_on_item(object *pl, object *tmp, object *skill)
Definition: skills.c:699
static int write_note(object *pl, object *item, const char *msg)
Definition: skills.c:1462
object * transport
Definition: player.h:195
#define FLAG_IDENTIFIED
Definition: define.h:261
#define FOR_INV_FINISH()
Definition: define.h:715
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1736
int16_t dam
Definition: living.h:46
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.c:122
void add_friendly_object(object *op)
Definition: friend.c:30
int32_t carrying
Definition: object.h:368
const char * name
Definition: object.h:311
#define FLAG_CHANGING
Definition: define.h:263
int steal(object *op, int dir, object *skill)
Definition: skills.c:277
#define M_GLASS
Definition: material.h:16
int detect_magic_on_item(object *pl, object *tmp, object *skill)
Definition: skills.c:748
int16_t last_grace
Definition: object.h:360
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.c:2389
int8_t direction
Definition: object.h:335
uint32_t nrof
Definition: object.h:333
int8_t Cha
Definition: living.h:36
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:447
#define UPD_FLAGS
Definition: newclient.h:290
#define FLAG_IS_CAULDRON
Definition: define.h:339
#define SIZEOFFREE
Definition: define.h:154
#define P_OUT_OF_MAP
Definition: map.h:251
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:192
Definition: object.h:111
struct pl * contr
Definition: object.h:276
int8_t item_power
Definition: object.h:363
int trap_see(object *op, object *trap)
Definition: rune.c:380
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:585
object * find_marked_object(object *op)
Definition: c_object.c:1270
#define M_ORGANIC
Definition: material.h:19
uint32_t tag_t
Definition: object.h:12
float speed
Definition: object.h:328
Definition: object.h:214
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
#define HEAD(op)
Definition: object.h:594
static void stop_jump(object *pl)
Definition: skills.c:532
#define FLAG_WIZ
Definition: define.h:231
#define EVENT_THROW
Definition: plugin.h:77
#define MAX_BUF
Definition: define.h:35
Definition: object.h:126
int16_t x
Definition: object.h:326
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1259
int find_traps(object *pl, object *skill)
Definition: skills.c:1213
const char * skill
Definition: object.h:321
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1527
int8_t wc
Definition: living.h:37
#define FLAG_IS_THROWN
Definition: define.h:249
static int attempt_pick_lock(object *door, object *pl, object *skill)
Definition: skills.c:353
uint16_t difficulty
Definition: map.h:343
signed __int64 int64_t
Definition: win32.h:168
#define FLAG_READY_WEAPON
Definition: define.h:335
int identifyskill2
Definition: define.h:94
#define FOR_MAP_FINISH()
Definition: define.h:768
int8_t Str
Definition: living.h:36
#define FLAG_KNOWN_CURSED
Definition: define.h:321
const char * sstring
Definition: global.h:40
#define FLAG_CURSED
Definition: define.h:317
Definition: object.h:107
char * ob_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size)
Definition: ob_methods.c:92
#define AP_NO_MERGE
Definition: define.h:619
Definition: object.h:135
int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success)
Definition: skills.c:800
int8_t body_info[NUM_BODY_LOCATIONS]
Definition: object.h:374
#define PREFER_HIGH
Definition: define.h:601
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
Definition: object.c:2381
#define FLAG_BLIND
Definition: define.h:337
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:28
void pray_at_altar(object *pl, object *altar, object *skill)
Definition: gods.c:323
int16_t grace
Definition: living.h:44
#define FREE_AND_COPY(sv, nv)
Definition: global.h:210
object * find_random_spell_in_ob(object *ob, const char *skill)
Definition: spell_util.c:48
#define NUM_ANIMATIONS(ob)
Definition: global.h:176
Definition: object.h:143
#define POT_DUST
Definition: spells.h:133
archetype * get_archetype_by_type_subtype(int type, int subtype)
Definition: arch.c:137
static int write_scroll(object *pl, object *scroll, object *skill)
Definition: skills.c:1552
object * monster_find_throw_ob(object *op)
Definition: monster.c:2350
tag_t count
Definition: object.h:299
living stats
Definition: object.h:369
int8_t Dex
Definition: living.h:36
struct archt * arch
Definition: object.h:414
uint8_t no_player_stealing
Definition: global.h:312
uint8_t type
Definition: object.h:339
struct Settings settings
Definition: init.c:39
static int do_skill_detect_curse(object *pl, object *skill)
Definition: skills.c:719
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.c:722
#define FLAG_SPLITTING
Definition: define.h:266
#define FLAG_APPLIED
Definition: define.h:235
void query_short_name(const object *op, char *buf, size_t size)
Definition: item.c:505
int execute_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: main.c:365
const char * msg
Definition: object.h:322
int hideability(object *ob)
Definition: player.c:4050
#define FLAG_MAKE_INVIS
Definition: define.h:329
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:788
#define FLAG_STARTEQUIP
Definition: define.h:268
#define GET_ANIM_ID(ob)
Definition: global.h:170
sstring add_string(const char *str)
Definition: shstr.c:124
object * identify(object *op)
Definition: item.c:1383
#define M_PAPER
Definition: material.h:14
#define GET_MAP_OB(M, X, Y)
Definition: map.h:172
#define FLAG_MONSTER
Definition: define.h:245
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.c:826
#define MSG_TYPE_SKILL
Definition: newclient.h:383
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:310
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.c:966
struct obj * inv
Definition: object.h:290
#define NDI_UNIQUE
Definition: newclient.h:245
#define M_LEATHER
Definition: material.h:17
int skill_throw(object *op, object *part, int dir, object *skill)
Definition: skills.c:2245
Definition: object.h:213
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
#define SCRIPT_FIX_ACTIVATOR
Definition: global.h:366
void make_visible(object *op)
Definition: player.c:4007
#define FLAG_WAS_WIZ
Definition: define.h:234
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:761
MoveType move_block
Definition: object.h:427
static int attempt_steal(object *op, object *who, object *skill)
Definition: skills.c:111
void object_set_msg(object *op, const char *msg)
Definition: object.c:4569
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
Definition: object.c:3611
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:584
int jump(object *pl, int dir, object *skill)
Definition: skills.c:662
#define SPELL_MANA
Definition: spells.h:58
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:580
#define FOR_BELOW_PREPARE(op_, it_)
Definition: define.h:742
int allow_denied_spells_writing
Definition: global.h:317
Definition: map.h:325
#define P_IS_ALIVE
Definition: map.h:237
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
Definition: newclient.h:569
#define FLAG_NO_SKILL_IDENT
Definition: define.h:336
Definition: object.h:120
int get_dex_bonus(int stat)
Definition: living.c:2348
int16_t level
Definition: object.h:352
int8_t facing
Definition: object.h:336
int skill_ident(object *pl, object *skill)
Definition: skills.c:896
#define FLAG_INV_LOCKED
Definition: define.h:330
void fix_object(object *op)
Definition: living.c:1120
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:192
float get_speed_bonus(int stat)
Definition: living.c:2376
void object_update_speed(object *op)
Definition: object.c:1095
int32_t value
Definition: object.h:351
object * object_get_owner(object *op)
Definition: object.c:568
int8_t magic
Definition: object.h:349
#define EVENT_TRIGGER
Definition: plugin.h:78
#define FOR_BELOW_FINISH()
Definition: define.h:749
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:708
void object_remove(object *op)
Definition: object.c:1587
int8_t body_used[NUM_BODY_LOCATIONS]
Definition: object.h:375
int32_t food
Definition: living.h:48
int get_thaco_bonus(int stat)
Definition: living.c:2352
#define BODY_ARMS
Definition: object.h:14