Crossfire Server, Trunk  R22010
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  // Spring traps even if the lock is picked.
363  if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) { /* set off any traps? */
364  spring_trap(door->inv, pl);
365  }
366  if (number < pl->stats.Dex + skill->level*2 - difficulty ) {
367  remove_door(door);
368  success = difficulty;
369  }
370  return success;
371 }
372 
373 
389 int pick_lock(object *pl, int dir, object *skill) {
390  object *tmp;
391  int x = pl->x+freearr_x[dir];
392  int y = pl->y+freearr_y[dir];
393  int difficulty=0;
394 
395  if (!dir)
396  dir = pl->facing;
397 
398  /* For all the stacked objects at this point find a door*/
399  if (OUT_OF_REAL_MAP(pl->map, x, y)) {
401  "There is no lock there.");
402  return 0;
403  }
404 
405  for (tmp = GET_MAP_OB(pl->map, x, y); tmp; tmp = tmp->above)
406  if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
407  break;
408 
409  if (!tmp) {
411  "There is no lock there.");
412  return 0;
413  }
414 
415  if (events_execute_object_event(tmp, EVENT_TRIGGER, pl, skill, NULL, SCRIPT_FIX_ALL) != 0)
416  return 0;
417 
418  if (tmp->type == LOCKED_DOOR) {
420  "You can't pick that lock!");
421  return 0;
422  }
423 
424  if (!tmp->move_block) {
426  "The door has no lock!");
427  return 0;
428  }
429 
430  difficulty = attempt_pick_lock(tmp, pl, skill);
431  /* Failure */
432  if (!difficulty) {
434  "You fail to pick the lock.");
435  return 0;
436  }
437 
439  "You pick the lock.");
440  return (calc_skill_exp(pl, NULL, skill) * isqrt(difficulty));
441 }
442 
443 
463 static int attempt_hide(object *op, object *skill) {
464  int number, difficulty = op->map->difficulty;
465  int terrain = hideability(op);
466 
467  if (terrain < -10) /* not enough cover here */
468  return 0;
469 
470  /* Hiding success and duration dependant on skill level,
471  * op->stats.Dex, map difficulty and terrain.
472  */
473 
474  number = (die_roll(2, 25, op, PREFER_LOW)-2)/2;
475  if (!stand_near_hostile(op) && number < op->stats.Dex+skill->level+terrain-difficulty) {
476  op->invisible += 100; /* set the level of 'hiddeness' */
477  if (op->type == PLAYER)
478  op->contr->tmp_invis = 1;
479  op->hide = 1;
480  return 1;
481  }
482  return 0;
483 }
484 
494 int hide(object *op, object *skill) {
495  /* the preliminaries -- Can we really hide now? */
496  /* this keeps monsters from using invisibilty spells and hiding */
497 
498  if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
500  "You don't need to hide while invisible!");
501  return 0;
502  }
503 
504  if (!op->hide && op->invisible > 0 && op->type == PLAYER) {
506  "Your attempt to hide breaks the invisibility spell!");
507  make_visible(op);
508  }
509 
510  if (op->invisible > 50*skill->level) {
512  "You are as hidden as you can get.");
513  return 0;
514  }
515 
516  if (attempt_hide(op, skill)) {
518  "You hide in the shadows.");
519  object_update(op, UP_OBJ_FACE);
520  return calc_skill_exp(op, NULL, skill);
521  }
523  "You fail to conceal yourself.");
524  return 0;
525 }
526 
527 
534 static void stop_jump(object *pl) {
535  fix_object(pl);
536  object_insert_in_map_at(pl, pl->map, pl, 0, pl->x, pl->y);
537  // Some monsters can also jump. They do not check for pickup with check_pick().
538  if (pl->contr)
539  check_pick(pl);
540 }
541 
555 static int attempt_jump(object *pl, int dir, int spaces, object *skill) {
560  if (pl->contr && pl->contr->transport){
561  char trans_name[MAX_BUF];
562  query_name(pl->contr->transport, trans_name, MAX_BUF);
564  "Your bounce off the walls of %s.", trans_name);
565  // We failed to jump. Return as a failure.
566  return 0;
567  }
568 
569  int i, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
570  int16_t x, y;
571  mapstruct *m;
572 
573  /* Jump loop. Go through spaces opject wants to jump. Halt the
574  * jump if a wall or creature is in the way. We set FLY_LOW
575  * temporarily to allow player to aviod exits/archs that are not
576  * move_on/off fly_low. This will also prevent pickup of objects
577  * while jumping over them.
578  */
579 
580  object_remove(pl);
581 
582  /*
583  * I don't think this is actually needed - all the movement
584  * code is handled in this function, and I don't see anyplace
585  * that cares about the move_type being flying.
586  */
587  pl->move_type |= MOVE_FLY_LOW;
588 
589  for (i = 0; i <= spaces; i++) {
590  x = pl->x+dx;
591  y = pl->y+dy;
592  m = pl->map;
593 
594  mflags = get_map_flags(m, &m, x, y, &x, &y);
595 
596  if (mflags&P_OUT_OF_MAP) {
597  (void)stop_jump(pl);
598  return 0;
599  }
600  if (OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, x, y))) {
602  "Your jump is blocked.");
603  stop_jump(pl);
604  return 0;
605  }
606 
607  FOR_MAP_PREPARE(m, x, y, tmp) {
608  tmp = HEAD(tmp);
609  /* Jump into creature */
610  if (QUERY_FLAG(tmp, FLAG_MONSTER)
611  || (tmp->type == PLAYER && (!QUERY_FLAG(tmp, FLAG_WIZ) || !tmp->contr->hidden))) {
613  "You jump into %s%s.",
614  tmp->type == PLAYER ? "" : "the ", tmp->name);
615 
616  stop_jump(pl);
617  if (tmp->type != PLAYER
618  || (pl->type == PLAYER && pl->contr->party == NULL)
619  || (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
620  skill_attack(tmp, pl, pl->facing, "kicked", skill); /* pl makes an attack */
621 
622  return 1;
623  }
624  /* If the space has fly on set (no matter what the space is),
625  * we should get the effects - after all, the player is
626  * effectively flying.
627  */
628  if (tmp->move_on&MOVE_FLY_LOW) {
629  pl->x = x;
630  pl->y = y;
631  pl->map = m;
632  if (pl->contr)
633  esrv_map_scroll(&pl->contr->socket, dx, dy);
634  stop_jump(pl);
635  return 1;
636  }
637  } FOR_MAP_FINISH();
638  pl->x = x;
639  pl->y = y;
640  pl->map = m;
641  if (pl->contr)
642  esrv_map_scroll(&pl->contr->socket, dx, dy);
643  }
644  stop_jump(pl);
645  return 1;
646 }
647 
664 int jump(object *pl, int dir, object *skill) {
665  int spaces = 0, stats;
666  int str = pl->stats.Str;
667  int dex = pl->stats.Dex;
668 
669  dex = dex ? dex : 15;
670  str = str ? str : 10;
671 
672  stats = str*str*str*dex*skill->level;
673 
674  if (pl->carrying != 0) /* don't want div by zero !! */
675  spaces = (int)(stats/pl->carrying);
676  else
677  spaces = 2; /* pl has no objects - gets the far jump */
678 
679  if (spaces > 2)
680  spaces = 2;
681  else if (spaces == 0) {
683  "You are carrying too much weight to jump.");
684  return 0;
685  }
686  return attempt_jump(pl, dir, spaces, skill);
687 }
688 
701 int detect_curse_on_item(object *pl, object *tmp, object *skill) {
703  && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
704  && tmp->item_power < skill->level) {
706  esrv_update_item(UPD_FLAGS, pl, tmp);
707  return NROF(tmp) * calc_skill_exp(pl, tmp, skill);
708  }
709  return 0;
710 }
721 static int do_skill_detect_curse(object *pl, object *skill) {
722  int success = 0;
723 
724  FOR_INV_PREPARE(pl, tmp)
725  if (!tmp->invisible) success += detect_curse_on_item(pl, tmp, skill);
726  FOR_INV_FINISH();
727 
728  /* Check ground, too, but only objects the player could pick up. Cauldrons are exceptions,
729  * you definitely want to know if they are cursed */
730  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
731  if (object_can_pick(pl, tmp) || QUERY_FLAG(tmp, FLAG_IS_CAULDRON))
732  success += detect_curse_on_item(pl, tmp, skill);
733  FOR_MAP_FINISH();
734 
735  return success;
736 }
750 int detect_magic_on_item(object *pl, object *tmp, object *skill) {
752  && (is_magical(tmp)) && tmp->item_power < skill->level) {
754  esrv_update_item(UPD_FLAGS, pl, tmp);
755  return NROF(tmp) * calc_skill_exp(pl, tmp, skill);
756  }
757  return 0;
758 }
759 
770 static int do_skill_detect_magic(object *pl, object *skill) {
771  int success = 0;
772 
773  FOR_INV_PREPARE(pl, tmp)
774  if (!tmp->invisible)
775  success += detect_magic_on_item(pl, tmp, skill);
776  FOR_INV_FINISH();
777 
778  /* Check ground, too, but like above, only if the object can be picked up*/
779  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
780  if (object_can_pick(pl, tmp))
781  success += detect_magic_on_item(pl, tmp, skill);
782  FOR_MAP_FINISH();
783 
784  return success;
785 }
786 
808 int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success) {
809  int success = 0, chance, ip;
810  int skill_value = (skill->level && pl->stats.Int) ? pl->stats.Int : 10;
811 
812  if (!tmp->invisible && !QUERY_FLAG(tmp, FLAG_NO_SKILL_IDENT) && !is_identified(tmp)) {
813  ip = tmp->magic;
814  if (tmp->item_power > ip)
815  ip = tmp->item_power;
816 
817  uint32_t identified = 0;
818  for (uint32_t i = 0; i < NROF(tmp); i++) {
819  chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
820  if (skill_value >= chance) {
821  identified++;
822  }
823  }
824 
825  if (identified == 0) {
827  object_merge(tmp, NULL);
828  return 0;
829  }
830 
831  if (identified < NROF(tmp)) {
832  object *left = tmp;
833  tmp = object_split(tmp, identified, NULL, 0);
834  SET_FLAG(left, FLAG_NO_SKILL_IDENT); // Will prevent tmp to be merged right back.
835  // It may happen that tmp is merged while inserting.
836  // It should not be the case because it means somewhere items were not merged correctly.
837  // In this case the player will identify all items but get exp only for "identified" items.
838  if (left->env) {
839  tmp = object_insert_in_ob(tmp, left->env);
840  } else {
841  tmp = object_insert_in_map_at(tmp, left->map, left, INS_BELOW_ORIGINATOR, left->x, left->y);
842  }
843  object_merge(left, NULL);
844  }
845 
846  tmp = identify(tmp);
847  if (pl->type == PLAYER && print_on_success) {
848  char desc[MAX_BUF];
849 
851  "You identify %s.",
852  ob_describe(tmp, pl, 1, desc, sizeof(desc)));
853  if (tmp->msg) {
855  "The item has a story:\n%s",
856  tmp->msg);
857  }
858  }
859  success += identified * calc_skill_exp(pl, tmp, skill);
860  }
861  return success;
862 }
863 
876 static int do_skill_ident(object *pl, int obj_class, object *skill) {
877  int success = 0, area, i;
878 
879  /* check the player */
880  FOR_INV_PREPARE(pl, tmp)
881  if (tmp->type == obj_class)
882  success += identify_object_with_skill(tmp, pl, skill, 1);
883  FOR_INV_FINISH();
884 
885  /* check the ground */
886  /* Altered to allow ident skills to increase in area with
887  * experience. -- Aaron Baugher
888  */
889 
890  if (skill->level > 64) { /* Adjust these levels? */
891  area = 49;
892  } else if (skill->level > 16) {
893  area = 25;
894  } else if (skill->level > 4) {
895  area = 9;
896  } else {
897  area = 1;
898  }
899 
900  for (i = 0; i < area; i++) {
901  int16_t x = pl->x+freearr_x[i];
902  int16_t y = pl->y+freearr_y[i];
903  mapstruct *m = pl->map;
904  int mflags;
905 
906  mflags = get_map_flags(m, &m, x, y, &x, &y);
907  if (mflags&P_OUT_OF_MAP)
908  continue;
909 
910  if (can_see_monsterP(m, pl->x, pl->y, i)) {
911  FOR_MAP_PREPARE(m, x, y, tmp)
912  if (tmp->type == obj_class)
913  success += identify_object_with_skill(tmp, pl, skill, 1);
914  FOR_MAP_FINISH();
915  }
916  }
917  return success;
918 }
919 
929 int skill_ident(object *pl, object *skill) {
930  int success = 0;
931  int i, identifiable_types=0;
932  const typedata *tmptype;
933 
934  if (pl->type != PLAYER)
935  return 0; /* only players will skill-identify */
936 
938  "You look at the objects nearby with your %s skill...", skill->name);
939 
940  switch (skill->subtype) {
941  case SK_DET_CURSE:
942  success = do_skill_detect_curse(pl, skill);
943  if (success)
945  "...and discover cursed items!");
946  break;
947 
948  case SK_DET_MAGIC:
949  success = do_skill_detect_magic(pl, skill);
950  if (success)
952  "...and discover items imbued with mystic forces!");
953  break;
954 
955  default:
956  /* we will try to identify items with this skill instead */
957  for (i=0; i<=OBJECT_TYPE_MAX; i++) {
958  tmptype = get_typedata(i);
959  if (tmptype) {
960  if (skill->subtype == tmptype->identifyskill || skill->subtype == tmptype->identifyskill2) {
961  success += do_skill_ident(pl, i, skill);
962  identifiable_types++;
963  }
964  }
965  }
966  if (identifiable_types == 0) {
967  LOG(llevError, "Error: skill_ident() called with skill %d which can't identify any items\n", skill->subtype);
968  return 0;
969  break;
970  }
971  break;
972  }
973  if (!success) {
975  "...and learn nothing more.");
976  }
977  return success;
978 }
979 
980 
999 int use_oratory(object *pl, int dir, object *skill) {
1000  int16_t x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
1001  int mflags, chance;
1002  object *tmp;
1003  mapstruct *m;
1004  char name[MAX_BUF];
1005 
1006  if (pl->type != PLAYER)
1007  return 0; /* only players use this skill */
1008  m = pl->map;
1009  mflags = get_map_flags(m, &m, x, y, &x, &y);
1010  if (mflags&P_OUT_OF_MAP)
1011  return 0;
1012 
1013  /* Save some processing - we have the flag already anyways
1014  */
1015  if (!(mflags&P_IS_ALIVE)) {
1017  "There is nothing to orate to.");
1018  return 0;
1019  }
1020 
1021  tmp = NULL;
1022  FOR_MAP_PREPARE(m, x, y, tmp2) {
1023  tmp2 = HEAD(tmp2);
1024  /* can't persuade players - return because there is nothing else
1025  * on that space to charm.
1026  */
1027  if (tmp2->type == PLAYER)
1028  return 0;
1029 
1030  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1031  const char *value = object_get_value(tmp2, "no_mood_change");
1032  if (value && strcmp(value, "1") == 0)
1033  return 0;
1034 
1035  tmp = tmp2;
1036  break;
1037  }
1038  } FOR_MAP_FINISH();
1039 
1040  if (!tmp) {
1042  "There is nothing to orate to.");
1043  return 0;
1044  }
1045 
1046  query_name(tmp, name, MAX_BUF);
1048  "You orate to the %s.",
1049  name);
1050 
1051  /* the following conditions limit who may be 'charmed' */
1052 
1053  /* it's hostile! */
1054  if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1055  query_name(tmp, name, MAX_BUF);
1057  "Too bad the %s isn't listening!",
1058  name);
1059  return 0;
1060  }
1061 
1062  /* it's already allied! */
1063  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
1064  if (object_get_owner(tmp) == pl) {
1066  "Your follower loves your speech.");
1067  return 0;
1068  }
1069 
1070  if (skill->level > tmp->level) {
1071  /* you steal the follower. Perhaps we should really look at the
1072  * level of the owner above?
1073  */
1074  object_set_owner(tmp, pl);
1075  query_name(tmp, name, MAX_BUF);
1077  "You convince the %s to follow you instead!",
1078  name);
1079 
1080  FREE_AND_COPY(tmp->skill, skill->skill);
1081 
1082  /* Abuse fix - don't give exp since this can otherwise
1083  * be used by a couple players to gets lots of exp.
1084  */
1085  return 0;
1086  }
1087 
1088  /* In this case, you can't steal it from the other player */
1089  return 0;
1090  } /* Creature was already a pet of someone */
1091 
1092  chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
1093 
1094  /* Ok, got a 'sucker' lets try to make them a follower */
1095  if (chance > 0 && tmp->level < random_roll(0, chance-1, pl, PREFER_HIGH)-1) {
1096  int64_t exp;
1097  query_name(tmp, name, MAX_BUF);
1099  "You convince the %s to become your follower.",
1100  name);
1101 
1102  object_set_owner(tmp, pl);
1103  /* compute exp before setting to 0, else the monster's experience is not taken into account. */
1104  tmp->stats.exp /= 5; /* why 5? because. */
1105  exp = calc_skill_exp(pl, tmp, skill);
1106  tmp->stats.exp = 0;
1107  add_friendly_object(tmp);
1108  SET_FLAG(tmp, FLAG_FRIENDLY);
1109  tmp->attack_movement = PETMOVE;
1110  /* keep oratory skill, so exp goes where it should if the pet kills something */
1111  FREE_AND_COPY(tmp->skill, skill->skill);
1112  return exp;
1113  }
1114 
1115  /* Charm failed. Creature may be angry now */
1116  if (skill->level+(pl->stats.Cha-10)/2 < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
1117  query_name(tmp, name, MAX_BUF);
1119  "Your speech angers the %s!",
1120  name);
1121 
1122  if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1123  CLEAR_FLAG(tmp, FLAG_FRIENDLY);
1125  tmp->attack_movement = 0; /* needed? */
1126  }
1128  }
1129 
1130  return 0; /* Fall through - if we get here, we didn't charm anything */
1131 }
1132 
1153 int singing(object *pl, int dir, object *skill) {
1154  int i, exp = 0, chance, mflags;
1155  object *tmp;
1156  mapstruct *m;
1157  int16_t x, y;
1158  char name[MAX_BUF];
1159  const char *value;
1160 
1161  if (pl->type != PLAYER)
1162  return 0; /* only players use this skill */
1163 
1165  "You sing");
1166  for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
1167  x = pl->x+freearr_x[i];
1168  y = pl->y+freearr_y[i];
1169  m = pl->map;
1170 
1171  mflags = get_map_flags(m, &m, x, y, &x, &y);
1172  if (mflags&P_OUT_OF_MAP)
1173  continue;
1174  if (!(mflags&P_IS_ALIVE))
1175  continue;
1176 
1177  tmp = NULL;
1178  FOR_MAP_PREPARE(m, x, y, tmp2) {
1179  tmp2 = HEAD(tmp2);
1180  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1181  tmp = tmp2;
1182  break;
1183  }
1184  /* can't affect players */
1185  if (tmp2->type == PLAYER) {
1186  tmp = tmp2;
1187  break;
1188  }
1189  } FOR_MAP_FINISH();
1190 
1191  /* Whole bunch of checks to see if this is a type of monster that would
1192  * listen to singing.
1193  */
1194  if (tmp
1195  && QUERY_FLAG(tmp, FLAG_MONSTER)
1196  && !QUERY_FLAG(tmp, FLAG_NO_STEAL) /* Been charmed or abused before */
1197  && !QUERY_FLAG(tmp, FLAG_SPLITTING) /* no ears */
1198  && !QUERY_FLAG(tmp, FLAG_HITBACK) /* was here before */
1199  && (tmp->level <= skill->level)
1200  && !QUERY_FLAG(tmp, FLAG_UNDEAD)
1201  && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) /* already calm */
1202  && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) { /* already calm */
1203  /* stealing isn't really related (although, maybe it should
1204  * be). This is mainly to prevent singing to the same monster
1205  * over and over again and getting exp for it.
1206  */
1207  chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
1208 
1209  value = object_get_value(tmp, "no_mood_change");
1210  if (value && strcmp(value, "1") == 0)
1211  chance = 0;
1212 
1213  if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
1215  query_name(tmp, name, MAX_BUF);
1217  "You calm down the %s",
1218  name);
1219 
1220  /* Give exp only if they are not aware */
1221  if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
1222  exp += calc_skill_exp(pl, tmp, skill);
1223  SET_FLAG(tmp, FLAG_NO_STEAL);
1224  } else {
1225  query_name(tmp, name, MAX_BUF);
1227  "Too bad the %s isn't listening!",
1228  name);
1229  SET_FLAG(tmp, FLAG_NO_STEAL);
1230  }
1231  }
1232  }
1233  return exp;
1234 }
1235 
1246 int find_traps(object *pl, object *skill) {
1247  int i, expsum = 0, mflags;
1248  int16_t x, y;
1249  mapstruct *m;
1250 
1251  /* First we search all around us for runes and traps, which are
1252  * all type RUNE
1253  */
1254 
1255  for (i = 0; i < 9; i++) {
1256  x = pl->x+freearr_x[i];
1257  y = pl->y+freearr_y[i];
1258  m = pl->map;
1259 
1260  mflags = get_map_flags(m, &m, x, y, &x, &y);
1261  if (mflags&P_OUT_OF_MAP)
1262  continue;
1263 
1264  /* Check everything in the square for trapness */
1265  FOR_MAP_PREPARE(m, x, y, tmp) {
1266  /* And now we'd better do an inventory traversal of each
1267  * of these objects' inventory
1268  * We can narrow this down a bit - no reason to search through
1269  * the players inventory or monsters for that matter.
1270  */
1271  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1272  FOR_INV_PREPARE(tmp, tmp2)
1273  if (tmp2->type == RUNE || tmp2->type == TRAP)
1274  if (trap_see(pl, tmp2)) {
1275  trap_show(tmp2, tmp);
1276  if (tmp2->stats.Cha > 1) {
1277  object *owner;
1278 
1279  owner = object_get_owner(tmp2);
1280  if (owner == NULL || owner->type != PLAYER)
1281  expsum += calc_skill_exp(pl, tmp2, skill);
1282 
1283  tmp2->stats.Cha = 1; /* unhide the trap */
1284  }
1285  }
1286  FOR_INV_FINISH();
1287  }
1288  if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
1289  trap_show(tmp, tmp);
1290  if (tmp->stats.Cha > 1) {
1291  object *owner;
1292 
1293  owner = object_get_owner(tmp);
1294  if (owner == NULL || owner->type != PLAYER)
1295  expsum += calc_skill_exp(pl, tmp, skill);
1296  tmp->stats.Cha = 1; /* unhide the trap */
1297  }
1298  }
1299  } FOR_MAP_FINISH();
1300  }
1302  "You search the area.");
1303  return expsum;
1304 }
1305 
1317 int remove_trap(object *op, object *skill) {
1318  int i, success = 0, mflags;
1319  mapstruct *m;
1320  int16_t x, y;
1321 
1322  for (i = 0; i < 9; i++) {
1323  x = op->x+freearr_x[i];
1324  y = op->y+freearr_y[i];
1325  m = op->map;
1326 
1327  mflags = get_map_flags(m, &m, x, y, &x, &y);
1328  if (mflags&P_OUT_OF_MAP)
1329  continue;
1330 
1331  /* Check everything in the square for trapness */
1332  FOR_MAP_PREPARE(m, x, y, tmp) {
1333  /* And now we'd better do an inventory traversal of each
1334  * of these objects inventory. Like above, only
1335  * do this for interesting objects.
1336  */
1337 
1338  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1339  FOR_INV_PREPARE(tmp, tmp2)
1340  if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
1341  object *owner;
1342 
1343  trap_show(tmp2, tmp);
1344  if (trap_disarm(op, tmp2, 1, skill) && ((owner = object_get_owner(tmp2)) == NULL || owner->type != PLAYER)) {
1345  tmp2->stats.exp = tmp2->stats.Cha*tmp2->level;
1346  success += calc_skill_exp(op, tmp2, skill);
1347  } else {
1348  /* Can't continue to disarm after failure */
1349  return success;
1350  }
1351  }
1352  FOR_INV_FINISH();
1353  }
1354  if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
1355  object *owner;
1356 
1357  trap_show(tmp, tmp);
1358  if (trap_disarm(op, tmp, 1, skill) && ((owner = object_get_owner(tmp)) == NULL || owner->type != PLAYER)) {
1359  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1360  success += calc_skill_exp(op, tmp, skill);
1361  } else {
1362  /* Can't continue to disarm after failure */
1363  return success;
1364  }
1365  }
1366  } FOR_MAP_FINISH();
1367  }
1368  return success;
1369 }
1370 
1371 
1390 int pray(object *pl, object *skill) {
1391  char buf[MAX_BUF];
1392 
1393  if (pl->type != PLAYER)
1394  return 0;
1395 
1396  snprintf(buf, sizeof(buf), "You pray.");
1397 
1398  /* Check all objects - we could stop at floor objects,
1399  * but if someone buries an altar, I don't see a problem with
1400  * going through all the objects, and it shouldn't be much slower
1401  * than extra checks on object attributes.
1402  */
1403  FOR_BELOW_PREPARE(pl, tmp)
1404  /* Only if the altar actually belongs to someone do you get special benefits */
1405  if (tmp->type == HOLY_ALTAR && tmp->other_arch) {
1406  snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
1407  pray_at_altar(pl, tmp, skill);
1408  break; /* Only pray at one altar */
1409  }
1410  FOR_BELOW_FINISH();
1411 
1413  buf);
1414 
1415  if (pl->stats.grace < pl->stats.maxgrace) {
1416  pl->stats.grace++;
1417  pl->last_grace = -1;
1418  }
1419  return 0;
1420 }
1421 
1438 void meditate(object *pl, object *skill) {
1439  if (pl->type != PLAYER)
1440  return; /* players only */
1441 
1442  /* check if pl has removed encumbering armour and weapons */
1443  if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && skill->level < 6) {
1445  "You can't concentrate while wielding a weapon!");
1446  return;
1447  }
1448 
1449  FOR_INV_PREPARE(pl, tmp)
1450  if (((tmp->type == ARMOUR && skill->level < 12)
1451  || (tmp->type == HELMET && skill->level < 10)
1452  || (tmp->type == SHIELD && skill->level < 6)
1453  || (tmp->type == BOOTS && skill->level < 4)
1454  || (tmp->type == GLOVES && skill->level < 2))
1455  && QUERY_FLAG(tmp, FLAG_APPLIED)) {
1457  "You can't concentrate while wearing so much armour!");
1458  return;
1459  }
1460  FOR_INV_FINISH();
1461 
1462  /* ok let's meditate! Spell points are regained first, then once
1463  * they are maxed we get back hp. Actual incrementing of values
1464  * is handled by the do_some_living() (in player.c). This way magical
1465  * bonuses for healing/sp regeneration are included properly
1466  * No matter what, we will eat up some playing time trying to
1467  * meditate. (see 'factor' variable for what sets the amount of time)
1468  */
1469 
1471  "You meditate.");
1472 
1473  if (pl->stats.sp < pl->stats.maxsp) {
1474  pl->stats.sp++;
1475  pl->last_sp = -1;
1476  } else if (pl->stats.hp < pl->stats.maxhp) {
1477  pl->stats.hp++;
1478  pl->last_heal = -1;
1479  }
1480 }
1481 
1495 static int write_note(object *pl, object *item, const char *msg) {
1496  char buf[BOOK_BUF];
1497  object *newBook = NULL;
1498 
1499  // The item should exist and be a book, but check it just in case.
1500  if (!item || item->type != BOOK) {
1502  "That was interesting...");
1503  // TODO: Print a scary log message.
1504  return 0;
1505  }
1506 
1507  // The message should never be NULL, but check it just in case.
1508  if (!msg) {
1510  "Hmm... what was I going to write?");
1511  // TODO: Print a scary log message.
1512  return 0;
1513  }
1514 
1515  // Don't let the player write a reserved keyword.
1516  if (strcasestr_local(msg, "endmsg")) {
1518  "Trying to cheat now are we?");
1519  return 0;
1520  }
1521 
1522  if (events_execute_object_event(item, EVENT_TRIGGER, pl, NULL, msg, SCRIPT_FIX_ALL) != 0)
1523  return strlen(msg);
1524 
1525  buf[0] = 0;
1526 
1527  // Write the message in the book if it doesn't overflow the buffer.
1528  if (!book_overflow(item->msg, msg, BOOK_BUF)) {
1529  // TODO: Garble some characters depending on intelligence/skill.
1530 
1531  // If there was already text, append the new message on the end.
1532  if (item->msg) {
1533  snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
1534  } else {
1535  snprintf(buf, sizeof(buf), "%s\n", msg);
1536  }
1537 
1538  // If there were multiple items in a stack, unstack one and write.
1539  if (item->nrof > 1) {
1540  newBook = object_new();
1541  object_copy(item, newBook);
1543  newBook->nrof = 1;
1544  object_set_msg(newBook, buf);
1545  newBook = object_insert_in_ob(newBook, pl);
1546  } else {
1547  object_set_msg(item, buf);
1548 
1549  // This shouldn't be necessary; the object hasn't changed visibly.
1550  //esrv_send_item(pl, item);
1551  }
1552 
1553  // Tell the player that he/she wrote in the object.
1554  query_short_name(item, buf, BOOK_BUF);
1557  "You write in the %s.", buf);
1558 
1559  // Give the player experience for writing.
1560  return strlen(msg);
1561  } else {
1562  query_short_name(item, buf, BOOK_BUF);
1565  "Your message won't fit in the %s!", buf);
1566  return 0;
1567  }
1568 }
1569 
1584 static int write_scroll(object *pl, object *scroll, object *skill) {
1585  int success = 0, confused = 0, grace_cost = 0;
1586  object *newscroll, *chosen_spell, *tmp;
1587 
1588  // The item should exist and be a scroll, but check it just in case.
1589  if (!scroll || scroll->type != SCROLL) {
1591  "A spell must be written on a magic scroll!");
1592  // TODO: Print a scary log message.
1593  return 0;
1594  }
1595 
1596  // The player must have a spell readied to inscribe.
1597  chosen_spell = pl->contr->ranges[range_magic];
1598  if (!chosen_spell) {
1600  "You should ready the spell you wish to inscribe.");
1601  return 0;
1602  }
1603 
1604  // Make sure the player has enough SP or grace to write the spell.
1605  grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1606  if (grace_cost > 0 && grace_cost > pl->stats.grace) {
1609  "You don't have enough grace to write a scroll of %s.",
1610  chosen_spell->name);
1611  return 0;
1612  }
1613 
1614  if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
1617  "You don't have enough mana to write a scroll of %s.",
1618  chosen_spell->name);
1619  return 0;
1620  }
1621 
1622  // Prevent players from writing spells that they are denied.
1623  if (chosen_spell->path_attuned & pl->path_denied
1625  char name[MAX_BUF];
1626 
1627  query_name(chosen_spell, name, MAX_BUF);
1630  "Just the idea of writing a scroll of %s makes you sick!",
1631  name);
1632  return 0;
1633  }
1634 
1635  // If the scroll already had a spell written on it, the player could
1636  // accidentally read it while trying to write a new one. Give the player
1637  // a 50% chance to overwrite a spell at their own level.
1638  if ((scroll->stats.sp || scroll->inv) &&
1639  random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
1641  "Oops! You accidently read it while trying to write on it.");
1642  apply_manual(pl, scroll, 0);
1643  return 0;
1644  }
1645 
1646  if (events_execute_object_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0) {
1647  return 0;
1648  }
1649 
1650  // Find out if the player is confused or not.
1651  if (QUERY_FLAG(pl, FLAG_CONFUSED)) {
1652  confused = 1;
1653  }
1654 
1655  // Mana or grace is lost no matter if the inscription is successful.
1656  pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1657  pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
1658 
1659  if (random_roll(0, chosen_spell->level * 4 - 1, pl, PREFER_LOW) <
1660  skill->level) {
1661  // If there were multiple items in a stack, unstack one.
1662  if (scroll->nrof > 1) {
1663  newscroll = object_new();
1664  object_copy(scroll, newscroll);
1666  newscroll->nrof = 1;
1667  } else {
1668  newscroll = scroll;
1669  }
1670 
1671  // Write spell if not confused; otherwise write random spell.
1672  newscroll->level = MAX(skill->level, chosen_spell->level);
1673 
1674  if (!confused) {
1675  draw_ext_info(
1677  "You succeed in writing a new scroll.");
1678  } else {
1679  chosen_spell = find_random_spell_in_ob(pl, NULL);
1680  if (!chosen_spell) {
1681  return 0;
1682  }
1683 
1684  draw_ext_info(
1686  "In your confused state, you write down some odd spell.");
1687  }
1688 
1689  if (newscroll->inv) {
1690  object *ninv;
1691  ninv = newscroll->inv;
1692  object_remove(ninv);
1693  object_free(ninv, FREE_OBJ_NO_DESTROY_CALLBACK);
1694  }
1695 
1696  tmp = object_new();
1697  object_copy(chosen_spell, tmp);
1698  object_insert_in_ob(tmp, newscroll);
1699 
1700  // This is needed so casting from the scroll works correctly with
1701  // moving_ball types, which checks attunements.
1702  newscroll->path_attuned = tmp->path_repelled;
1703 
1704  // Same code as from treasure.c - so they can better merge.
1705  // If players want to sell them, so be it.
1706  newscroll->value = newscroll->arch->clone.value *
1707  newscroll->inv->value * (newscroll->level + 50 ) /
1708  (newscroll->inv->level + 50);
1709  newscroll->stats.exp = newscroll->value / 5;
1710 
1711  // Finish manipulating the scroll before inserting it.
1712  if (newscroll == scroll) {
1713  // Remove object to stack correctly with other items.
1714  object_remove(newscroll);
1715  }
1716 
1717  newscroll = object_insert_in_ob(newscroll, pl);
1718  success = calc_skill_exp(pl, newscroll, skill);
1719 
1720  if (!confused) {
1721  success *= 2;
1722  }
1723 
1724  success = success * skill->level;
1725  return success;
1726  }
1727 
1728  // Inscription wasn't successful; do something bad to the player.
1729  if (chosen_spell->level > skill->level || confused) {
1730  draw_ext_info(
1732  "Ouch! Your attempt to write a new scroll strains your mind!");
1733 
1734  // Either drain a stat or subtract experience.
1735  if (random_roll(0, 1, pl, PREFER_LOW) == 1) {
1736  drain_specific_stat(pl, 4);
1737  } else {
1738  confuse_living(pl, pl, 99);
1739  return -30 * chosen_spell->level;
1740  }
1741  } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
1742  draw_ext_info(
1744  "Your attempt to write a new scroll rattles your mind!");
1745  confuse_living(pl, pl, 99);
1746  } else {
1747  draw_ext_info(
1749  "You fail to write a new scroll.");
1750  }
1751 
1752  return 0;
1753 }
1754 
1768 int write_on_item(object *pl, const char *params, object *skill) {
1769  object *item;
1770  const char *string = params;
1771  int msgtype;
1772  archetype *skat;
1773 
1774  // Only players can use the inscription skill.
1775  if (pl->type != PLAYER) {
1776  return 0;
1777  }
1778 
1779  // No message was given, so both strings are set to empty.
1780  if (!params) {
1781  params = "";
1782  string = params;
1783  }
1784 
1785  // You must be able to read before you can write.
1787 
1788  if (!find_skill_by_name(pl, skat->clone.skill)) {
1790  "You must learn to read before you can write!");
1791  return 0;
1792  }
1793 
1794  // You must not be blind to write, unless you're a DM.
1795  if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
1797  "You are unable to write while blind.");
1798  return 0;
1799  }
1800 
1801  // If a message was given, write a book. Otherwise, write a scroll.
1802  if (string[0] != '\0') {
1803  msgtype = BOOK;
1804  } else {
1805  msgtype = SCROLL;
1806  }
1807 
1808  // Find and attempt to write on the player's marked item.
1809  item = find_marked_object(pl);
1810  if (item == NULL) {
1812  "You haven't marked any items to write on yet.");
1813  return 0;
1814  }
1815 
1816  // Don't let the player write on an unpaid item.
1817  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1819  "You had better pay for that before you write on it!");
1820  return 0;
1821  }
1822 
1823  // Check if the marked item is the type of item we're writing.
1824  if (msgtype != item->type) {
1827  "You must mark a %s to write %s.",
1828  msgtype == BOOK ? "book" : "magic scroll",
1829  msgtype == BOOK ? "your message on" : "your spell down");
1830  return 0;
1831  }
1832 
1833  if (msgtype == BOOK) {
1834  return write_note(pl, item, string);
1835  } else if (msgtype == SCROLL) {
1836  return write_scroll(pl, item, skill);
1837  } else {
1838  // This case should never be reached.
1839  abort();
1840  }
1841 }
1842 
1855 static object *find_throw_ob(object *op, sstring race) {
1856  object *tmp;
1857  char name[MAX_BUF];
1858 
1859  if (!op) { /* safety */
1860  LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1861  return (object *)NULL;
1862  }
1863 
1864  /* prefer marked item */
1865  tmp = find_marked_object(op);
1866  if (tmp != NULL) {
1867  /* can't toss invisible or inv-locked items */
1868  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1869  tmp = NULL;
1870  }
1871  }
1872 
1873  /* look through the inventory if no marked found */
1874  if (tmp == NULL) {
1875  FOR_INV_PREPARE(op, tmp2) {
1876  /* can't toss invisible items */
1877  if (tmp2->invisible)
1878  continue;
1879 
1880  if (tmp2->type == CONTAINER && QUERY_FLAG(tmp2, FLAG_APPLIED) && tmp2->race == race) {
1881  tmp = find_throw_ob(tmp2, race);
1882  if (tmp != NULL) {
1883  break;
1884  }
1885  }
1886 
1887  /* if not a container, then don't look if locked */
1888  if (tmp2->type == CONTAINER || QUERY_FLAG(tmp2, FLAG_INV_LOCKED))
1889  continue;
1890 
1891  if (tmp2->race == race) {
1892  tmp = tmp2;
1893  break;
1894  }
1895 
1896  } FOR_INV_FINISH();
1897  }
1898 
1899  /* this should prevent us from throwing away
1900  * cursed items, worn armour, etc. Only weapons
1901  * can be thrown from 'hand'.
1902  */
1903  if (!tmp)
1904  return NULL;
1905 
1906  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1907  if (tmp->type != WEAPON) {
1908  query_name(tmp, name, MAX_BUF);
1910  "You can't throw %s.",
1911  name);
1912  tmp = NULL;
1913  } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
1914  query_name(tmp, name, MAX_BUF);
1917  "The %s sticks to your hand!",
1918  name);
1919  tmp = NULL;
1920  } else {
1921  if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) {
1922  LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1923  tmp = NULL;
1924  }
1925  }
1926  } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1927  query_name(tmp, name, MAX_BUF);
1929  "You should pay for the %s first.",
1930  name);
1931  tmp = NULL;
1932  }
1933 
1934  if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1935  LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
1936  tmp = NULL;
1937  }
1938  return tmp;
1939 }
1940 
1952 static object *make_throw_ob(object *orig) {
1953  object *toss_item;
1954 
1955  if (!orig)
1956  return NULL;
1957 
1958  toss_item = object_new();
1959  if (QUERY_FLAG(orig, FLAG_APPLIED)) {
1960  LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
1961  /* insufficient workaround, but better than nothing */
1962  CLEAR_FLAG(orig, FLAG_APPLIED);
1963  }
1964  object_copy(orig, toss_item);
1965  toss_item->type = THROWN_OBJ;
1966  CLEAR_FLAG(toss_item, FLAG_CHANGING);
1967  toss_item->stats.dam = 0; /* default damage */
1968  object_insert_in_ob(orig, toss_item);
1969  return toss_item;
1970 }
1971 
1972 
1993 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
1994  object *throw_ob = toss_item, *left = NULL;
1995  int eff_str = 0, str = op->stats.Str, dam = 0;
1996  int pause_f, weight_f = 0, mflags;
1997  float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1998  mapstruct *m;
1999  int16_t sx, sy;
2000  tag_t tag;
2001  char name[MAX_BUF];
2002 
2003  if (throw_ob == NULL) {
2004  if (op->type == PLAYER) {
2006  "You have nothing to throw.");
2007  }
2008  return 0;
2009  }
2010  if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
2011  if (op->type == PLAYER) {
2013  "The gods won't let you throw that.");
2014  }
2015  return 0;
2016  }
2017 
2018  tag = throw_ob->count;
2019  events_execute_object_event(throw_ob, EVENT_THROW, op, NULL, NULL, SCRIPT_FIX_ACTIVATOR);
2020  if (object_was_destroyed(throw_ob, tag)) {
2021  return 1;
2022  }
2023 
2024  /* Because throwing effectiveness must be reduced by the
2025  * encumbrance of the thrower and weight of the object. THus,
2026  * we use the concept of 'effective strength' as defined below.
2027  */
2028 
2029  /* if str exceeds settings.max_stat (30, eg giants), lets assign a str_factor > 1 */
2030  if (str > settings.max_stat) {
2031  str_factor = (float)str/(float)settings.max_stat;
2032  str = settings.max_stat;
2033  }
2034 
2035  /* the more we carry, the less we can throw. Limit only on players */
2036  if (op->type == PLAYER
2037  && op->carrying > (get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)
2038  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
2053  load_factor = 2.0f - (float)(op->carrying) / (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT);
2054  // Only clip to 1.0 if we get in here, since it is 1.0 if we do not get in here.
2055  load_factor = MIN(load_factor, 1.0f);
2056  }
2057 
2058  /* lighter items are thrown harder, farther, faster */
2059  if (throw_ob->weight > 0)
2060  item_factor = (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)/(float)(3.0*throw_ob->weight);
2061  else { /* 0 or negative weight?!? Odd object, can't throw it */
2062  query_name(throw_ob, name, MAX_BUF);
2064  "You can't throw %s.",
2065  name);
2066  return 0;
2067  }
2068 
2069  eff_str = str*load_factor;
2070  eff_str = (float)eff_str*item_factor*str_factor;
2071 
2072  /* alas, arrays limit us to a value of settings.max_stat (30). Use str_factor to
2073  * account for super-strong throwers. */
2074  if (eff_str > settings.max_stat)
2075  eff_str = settings.max_stat;
2076 
2077 #ifdef DEBUG_THROW
2078  LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
2079  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);
2080  LOG(llevDebug, " str_factor=%f\n", str_factor);
2081  LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
2082 #endif
2083 
2084  /* 3 things here prevent a throw, you aimed at your feet, you
2085  * have no effective throwing strength, or you threw at something
2086  * that flying objects can't get through.
2087  */
2088  mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
2089 
2090  if (!dir
2091  || (eff_str <= 1)
2092  || (mflags&P_OUT_OF_MAP)
2093  || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
2094  /* bounces off 'wall', and drops to feet */
2095  object_remove(throw_ob);
2096  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2097  if (op->type == PLAYER) {
2098  if (eff_str <= 1) {
2099  query_name(throw_ob, name, MAX_BUF);
2101  "Your load is so heavy you drop %s to the ground.",
2102  name);
2103  } else if (!dir) {
2104  query_name(throw_ob, name, MAX_BUF);
2106  "You throw %s at the ground.",
2107  name);
2108  } else
2110  "Something is in the way.");
2111  }
2112  return 0;
2113  } /* if object can't be thrown */
2114 
2115  left = throw_ob; /* these are throwing objects left to the player */
2116 
2117  /* sometimes object_split() can't split an object (because op->nrof==0?)
2118  * and returns NULL. We must use 'left' then
2119  */
2120  throw_ob = object_split(throw_ob, 1, NULL, 0);
2121  if (throw_ob == NULL) {
2122  throw_ob = left;
2123  object_remove(left);
2124  }
2125 
2126  /* special case: throwing powdery substances like dust, dirt */
2127  if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
2128  cast_dust(op, throw_ob, dir);
2129  return 1;
2130  }
2131 
2132  /* Make a thrown object -- insert real object in a 'carrier' object.
2133  * If unsuccessfull at making the "thrown_obj", we just reinsert
2134  * the original object back into inventory and exit
2135  */
2136  toss_item = make_throw_ob(throw_ob);
2137  if (toss_item) {
2138  throw_ob = toss_item;
2139  if (throw_ob->skill)
2140  free_string(throw_ob->skill);
2141  throw_ob->skill = add_string(skill->skill);
2142  } else {
2143  object_insert_in_ob(throw_ob, op);
2144  return 0;
2145  }
2146 
2147  object_set_owner(throw_ob, op);
2148  /* At some point in the attack code, the actual real object (op->inv)
2149  * becomes the hitter. As such, we need to make sure that has a proper
2150  * owner value so exp goes to the right place.
2151  */
2152  object_set_owner(throw_ob->inv, op);
2153  throw_ob->direction = dir;
2154 
2155  /* the damage bonus from the force of the throw */
2156  dam = str_factor*get_dam_bonus(eff_str);
2157 
2158  /* Now, lets adjust the properties of the thrown_ob. */
2159 
2160  /* how far to fly */
2161  throw_ob->last_sp = (eff_str*3)/5;
2162 
2163  /* speed */
2164  throw_ob->speed = (get_speed_bonus(eff_str)+1.0)/1.5;
2165  throw_ob->speed = MIN(1.0, throw_ob->speed); /* no faster than an arrow! */
2166 
2167  /* item damage. Eff_str and item weight influence damage done */
2168  weight_f = MIN(throw_ob->weight/2000, settings.max_stat);
2169  throw_ob->stats.dam += (dam/3)+get_dam_bonus(weight_f)+(throw_ob->weight/15000)-2;
2170 
2171  /* chance of breaking. Proportional to force used and weight of item */
2172  throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
2173 
2174  /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
2175  throw_ob->stats.wc = 25-get_dex_bonus(op->stats.Dex)-get_thaco_bonus(eff_str)-skill->level;
2176 
2177 
2178  /* the properties of objects which are meant to be thrown (ie dart,
2179  * throwing knife, etc) will differ from ordinary items. Lets tailor
2180  * this stuff in here.
2181  */
2182 
2183  if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
2184  throw_ob->last_sp += eff_str/3; /* fly a little further */
2185  throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
2186  throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
2187  /* only throw objects get directional faces */
2188  if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
2189  object_update_turn_face(throw_ob);
2190  } else {
2191  /* some materials will adjust properties.. */
2192  if (throw_ob->material&M_LEATHER) {
2193  throw_ob->stats.dam -= 1;
2194  throw_ob->stats.food -= 10;
2195  }
2196  if (throw_ob->material&M_GLASS)
2197  throw_ob->stats.food += 60;
2198 
2199  if (throw_ob->material&M_ORGANIC) {
2200  throw_ob->stats.dam -= 3;
2201  throw_ob->stats.food += 55;
2202  }
2203  if (throw_ob->material&M_PAPER || throw_ob->material&M_CLOTH) {
2204  throw_ob->stats.dam -= 5;
2205  throw_ob->speed *= 0.8;
2206  throw_ob->stats.wc += 3;
2207  throw_ob->stats.food -= 30;
2208  }
2209  /* light obj have more wind resistance, fly slower*/
2210  if (throw_ob->weight > 500)
2211  throw_ob->speed *= 0.8;
2212  if (throw_ob->weight > 50)
2213  throw_ob->speed *= 0.5;
2214  } /* else tailor thrown object */
2215 
2216  /* some limits, and safeties (needed?) */
2217  if (throw_ob->stats.dam < 0)
2218  throw_ob->stats.dam = 0;
2219  if (throw_ob->last_sp > eff_str)
2220  throw_ob->last_sp = eff_str;
2221  if (throw_ob->stats.food < 0)
2222  throw_ob->stats.food = 0;
2223  if (throw_ob->stats.food > 100)
2224  throw_ob->stats.food = 100;
2225  if (throw_ob->stats.wc > 30)
2226  throw_ob->stats.wc = 30;
2227 
2228  /* how long to pause the thrower. Higher values mean less pause */
2229  pause_f = ((2*eff_str)/3)+20+skill->level;
2230 
2231  /* Put a lower limit on this */
2232  if (pause_f < 10)
2233  pause_f = 10;
2234  if (pause_f > 100)
2235  pause_f = 100;
2236 
2237  /* Changed in 0.94.2 - the calculation before was really goofy.
2238  * In short summary, a throw can take anywhere between speed 5 and
2239  * speed 0.5
2240  */
2241  op->speed_left -= 50.0/pause_f;
2242 
2243  object_update_speed(throw_ob);
2244  throw_ob->speed_left = 0;
2245 
2246  throw_ob->move_type = MOVE_FLY_LOW;
2247  throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
2248 
2249 #ifdef DEBUG_THROW
2250  LOG(llevDebug, " pause_f=%d \n", pause_f);
2251  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);
2252  LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
2253 #endif
2254  tag = throw_ob->count;
2255  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2256  if (!object_was_destroyed(throw_ob, tag))
2257  ob_process(throw_ob);
2258  return 1;
2259 }
2260 
2276 int skill_throw(object *op, object *part, int dir, object *skill) {
2277  object *throw_ob;
2278 
2279  if (op->type == PLAYER)
2280  throw_ob = find_throw_ob(op, find_string("throwing"));
2281  else
2282  throw_ob = monster_find_throw_ob(op);
2283 
2284  return do_throw(op, part, throw_ob, dir, skill);
2285 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:313
#define AP_UNAPPLY
Definition: define.h:612
void spring_trap(object *trap, object *victim)
Definition: rune.c:205
Definition: player.h:92
int apply_special(object *who, object *op, int aflags)
Definition: apply.c:1156
static object * make_throw_ob(object *orig)
Definition: skills.c:1952
int apply_manual(object *op, object *tmp, int aflag)
Definition: apply.c:597
#define FLAG_DAMNED
Definition: define.h:318
#define FLAG_UNPAID
Definition: define.h:236
#define MOVE_WALK
Definition: define.h:407
#define MSG_TYPE_ITEM
Definition: newclient.h:388
void object_free(object *ob, int flags)
Definition: object.c:1348
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:658
uint8_t max_stat
Definition: global.h:324
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:1195
void remove_door(object *op)
Definition: time.c:38
#define FLAG_HITBACK
Definition: define.h:267
#define FOR_OB_AND_ABOVE_PREPARE(op_)
Definition: define.h:776
static int attempt_jump(object *pl, int dir, int spaces, object *skill)
Definition: skills.c:555
static int do_skill_ident(object *pl, int obj_class, object *skill)
Definition: skills.c:876
#define SET_FLAG(xyz, p)
Definition: define.h:223
int identifyskill
Definition: define.h:93
StringBuffer * buf
Definition: readable.c:1591
int get_dam_bonus(int stat)
Definition: living.c:2376
#define MSG_TYPE_ITEM_INFO
Definition: newclient.h:641
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.c:3881
#define FLAG_FRIENDLY
Definition: define.h:246
static int attempt_hide(object *op, object *skill)
Definition: skills.c:463
#define SPELL_GRACE
Definition: spells.h:59
static int do_skill_detect_magic(object *pl, object *skill)
Definition: skills.c:770
void free_string(sstring str)
Definition: shstr.c:280
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.c:340
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4136
void pick_up(object *op, object *alt)
Definition: c_object.c:509
int check_pick(object *op)
Definition: player.c:1695
short freearr_x[SIZEOFFREE]
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:601
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.c:275
int isqrt(int n)
Definition: utils.c:586
#define FLAG_CONFUSED
Definition: define.h:312
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:582
void meditate(object *pl, object *skill)
Definition: skills.c:1438
int pray(object *pl, object *skill)
Definition: skills.c:1390
#define M_CLOTH
Definition: material.h:21
#define MSG_TYPE_VICTIM_STEAL
Definition: newclient.h:648
int stand_near_hostile(object *who)
Definition: player.c:4035
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1153
method_ret ob_process(object *op)
Definition: ob_methods.c:67
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:583
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:217
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.c:437
void remove_friendly_object(object *op)
Definition: friend.c:56
#define MAX(x, y)
Definition: compat.h:20
void object_update(object *op, int action)
Definition: object.c:1190
#define MSG_TYPE_ITEM_REMOVE
Definition: newclient.h:638
uint32_t get_weight_limit(int stat)
Definition: living.c:2360
#define NDI_BLACK
Definition: newclient.h:221
#define PETMOVE
Definition: define.h:518
#define SCRIPT_FIX_ALL
Definition: global.h:370
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:308
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:791
#define MIN(x, y)
Definition: compat.h:17
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:389
short freearr_y[SIZEOFFREE]
Definition: object.c:71
#define FLAG_KNOWN_MAGICAL
Definition: define.h:320
const typedata * get_typedata(int itemtype)
Definition: item.c:318
#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
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1849
sstring find_string(const char *str)
Definition: shstr.c:236
void object_update_turn_face(object *op)
Definition: object.c:1069
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.c:2250
static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill)
Definition: skills.c:1993
int is_identified(const object *op)
Definition: item.c:1316
#define MSG_TYPE_VICTIM
Definition: newclient.h:392
object * object_new(void)
Definition: object.c:1011
#define FOR_OB_AND_ABOVE_FINISH()
Definition: define.h:780
int object_can_pick(const object *who, const object *item)
Definition: object.c:3657
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2602
#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:1855
int hide(object *op, object *skill)
Definition: skills.c:494
#define MOVE_FLY_LOW
Definition: define.h:408
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
void monster_npc_call_help(object *op)
Definition: monster.c:1840
#define BOOK_BUF
Definition: book.h:16
#define snprintf
Definition: win32.h:46
int remove_trap(object *op, object *skill)
Definition: skills.c:1317
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.c:729
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:380
int detect_curse_on_item(object *pl, object *tmp, object *skill)
Definition: skills.c:701
static int write_note(object *pl, object *item, const char *msg)
Definition: skills.c:1495
#define FLAG_IDENTIFIED
Definition: define.h:261
#define FOR_INV_FINISH()
Definition: define.h:714
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1768
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
#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:750
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.c:2391
#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
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:1509
#define M_ORGANIC
Definition: material.h:19
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
static void stop_jump(object *pl)
Definition: skills.c:534
#define FLAG_WIZ
Definition: define.h:231
#define MAX_BUF
Definition: define.h:35
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1272
int find_traps(object *pl, object *skill)
Definition: skills.c:1246
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1537
archetype * get_archetype_by_type_subtype(int type, int subtype)
Definition: arch.cpp:101
#define FLAG_IS_THROWN
Definition: define.h:249
static int attempt_pick_lock(object *door, object *pl, object *skill)
Definition: skills.c:353
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:767
#define FLAG_KNOWN_CURSED
Definition: define.h:321
const char * sstring
Definition: global.h:40
#define FLAG_CURSED
Definition: define.h:317
unsigned int uint32_t
Definition: win32.h:162
char * ob_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size)
Definition: ob_methods.c:91
#define AP_NO_MERGE
Definition: define.h:618
int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success)
Definition: skills.c:808
#define PREFER_HIGH
Definition: define.h:600
#define EVENT_THROW
Definition: events.h:32
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
Definition: object.c:2382
#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
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
object * find_random_spell_in_ob(object *ob, const char *skill)
Definition: spell_util.c:48
#define NUM_ANIMATIONS(ob)
Definition: global.h:169
#define POT_DUST
Definition: spells.h:133
static int write_scroll(object *pl, object *scroll, object *skill)
Definition: skills.c:1584
object * monster_find_throw_ob(object *op)
Definition: monster.c:2352
uint8_t no_player_stealing
Definition: global.h:310
struct Settings settings
Definition: init.c:39
static int do_skill_detect_curse(object *pl, object *skill)
Definition: skills.c:721
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.c:697
#define FLAG_SPLITTING
Definition: define.h:266
#define FLAG_APPLIED
Definition: define.h:235
object * object_merge(object *op, object *top)
Definition: object.c:1800
void query_short_name(const object *op, char *buf, size_t size)
Definition: item.c:508
int hideability(object *ob)
Definition: player.c:3953
#define FLAG_MAKE_INVIS
Definition: define.h:329
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:787
#define FLAG_STARTEQUIP
Definition: define.h:268
#define GET_ANIM_ID(ob)
Definition: global.h:163
sstring add_string(const char *str)
Definition: shstr.c:124
object * identify(object *op)
Definition: item.c:1386
#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:824
#define MSG_TYPE_SKILL
Definition: newclient.h:383
int events_execute_object_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: events.cpp:259
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:999
#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:2276
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
#define SCRIPT_FIX_ACTIVATOR
Definition: global.h:369
void make_visible(object *op)
Definition: player.c:3910
#define FLAG_WAS_WIZ
Definition: define.h:234
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:760
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:4570
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
Definition: object.c:3612
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:584
int jump(object *pl, int dir, object *skill)
Definition: skills.c:664
#define SPELL_MANA
Definition: spells.h:58
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:583
#define FOR_BELOW_PREPARE(op_, it_)
Definition: define.h:741
int allow_denied_spells_writing
Definition: global.h:315
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 EVENT_TRIGGER
Definition: events.h:33
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
Definition: newclient.h:569
#define FLAG_NO_SKILL_IDENT
Definition: define.h:336
int get_dex_bonus(int stat)
Definition: living.c:2352
int skill_ident(object *pl, object *skill)
Definition: skills.c:929
#define FLAG_INV_LOCKED
Definition: define.h:330
void fix_object(object *op)
Definition: living.c:1124
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:202
float get_speed_bonus(int stat)
Definition: living.c:2380
void object_update_speed(object *op)
Definition: object.c:1086
object * object_get_owner(object *op)
Definition: object.c:568
#define FOR_BELOW_FINISH()
Definition: define.h:748
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:707
void object_remove(object *op)
Definition: object.c:1588
int get_thaco_bonus(int stat)
Definition: living.c:2356