Crossfire Server, Trunk
skills.cpp
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 #ifndef _GNU_SOURCE
16 #define _GNU_SOURCE // strcasestr() is a GNU extension in string.h
17 #endif
18 
19 #include "global.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "book.h"
25 #include "living.h"
26 #include "object.h"
27 #include "skills.h"
28 #include "spells.h"
29 #include "sproto.h"
30 
45 static int adj_stealchance(object *op, object *victim, int roll) {
46  if (!op || !victim || !roll)
47  return -1;
48 
49  /* Only prohibit stealing if the player does not have a free
50  * hand available and in fact does have hands.
51  */
52  if (op->type == PLAYER
53  && op->body_used[BODY_ARMS] <= 0
54  && op->body_info[BODY_ARMS]) {
56  "But you have no free hands to steal with!");
57  return -1;
58  }
59 
60  /* ADJUSTMENTS */
61 
62  /* Its harder to steal from hostile beings! */
64  roll = roll/2;
65 
66  /* Easier to steal from sleeping beings, or if the thief is
67  * unseen */
69  roll = roll*3;
70  else if (op->invisible)
71  roll = roll*2;
72 
73  /* check stealing 'encumberance'. Having this equipment applied makes
74  * it quite a bit harder to steal.
75  */
76  FOR_INV_PREPARE(op, equip) {
77  if (equip->type == WEAPON && QUERY_FLAG(equip, FLAG_APPLIED)) {
78  roll -= equip->weight/10000;
79  }
80  if (equip->type == BOW && QUERY_FLAG(equip, FLAG_APPLIED))
81  roll -= equip->weight/5000;
82  if (equip->type == SHIELD && QUERY_FLAG(equip, FLAG_APPLIED)) {
83  roll -= equip->weight/2000;
84  }
85  if (equip->type == ARMOUR && QUERY_FLAG(equip, FLAG_APPLIED))
86  roll -= equip->weight/5000;
87  if (equip->type == GLOVES && QUERY_FLAG(equip, FLAG_APPLIED))
88  roll -= equip->weight/100;
89  } FOR_INV_FINISH();
90  if (roll < 0)
91  roll = 0;
92  return roll;
93 }
94 
113 static int attempt_steal(object *op, object *who, object *skill) {
114  object *success = NULL, *tmp = NULL;
115  int roll = 0, chance = 0, stats_value;
116  rv_vector rv;
117  char name[MAX_BUF];
118 
119  stats_value = ((who->stats.Dex+who->stats.Int)*3)/2;
120 
121  /* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them)
122  * they will try to prevent stealing if they can. Only unseen theives will
123  * have much chance of success.
124  */
125  if (op->type != PLAYER && QUERY_FLAG(op, FLAG_NO_STEAL)) {
126  if (monster_can_detect_enemy(op, who, &rv)) {
130  "Your attempt is prevented!");
131  return 0;
132  }
133 
134  /* help npc to detect thief next time by raising its wisdom
135  * This probably isn't the right approach - we shouldn't be
136  * changing the stats of the monsters - better approach
137  * might be to use force objects for this - MSW 2009/02/24
138  */
139  op->stats.Wis += (op->stats.Int/5)+1;
140  if (op->stats.Wis > settings.max_stat)
141  op->stats.Wis = settings.max_stat;
142  }
143  if (op->type == PLAYER && QUERY_FLAG(op, FLAG_WIZ)) {
145  "You can't steal from the dungeon master!");
146  return 0;
147  }
148  if (op->type == PLAYER && who->type == PLAYER && settings.no_player_stealing) {
150  "You can't steal from other players!");
151  return 0;
152  }
153 
154 
155  /* Ok then, go thru their inventory, stealing */
157  /* you can't steal worn items, starting items, wiz stuff,
158  * innate abilities, or items w/o a type. Generally
159  * speaking, the invisibility flag prevents experience or
160  * abilities from being stolen since these types are currently
161  * always invisible objects. I was implicit here so as to prevent
162  * future possible problems. -b.t.
163  * Flesh items generated w/ fix_flesh_item should have FLAG_NO_STEAL
164  * already -b.t.
165  */
166 
169  || !(inv->type)
170  || inv->type == SPELL
173  || inv->invisible)
174  continue;
175 
176  /* Okay, try stealing this item. Dependent on dexterity of thief,
177  * skill level, see the adj_stealroll fctn for more detail.
178  */
179 
180  roll = die_roll(2, 100, who, PREFER_LOW)/2; /* weighted 1-100 */
181 
182  chance = adj_stealchance(who, op, stats_value+skill->level*10-op->level*3);
183  if (chance == -1)
184  return 0;
185  if (roll < chance) {
186  tag_t inv_count = inv->count;
187 
188  pick_up(who, inv);
189  /* need to see if the player actually stole this item -
190  * if it is in the players inv, assume it is. This prevents
191  * abuses where the player can not carry the item, so just
192  * keeps stealing it over and over.
193  */
194  if (object_was_destroyed(inv, inv_count) || inv->env != op) {
195  /* for players, play_sound: steals item */
196  success = inv;
198  }
199  tmp = inv;
200  break;
201  }
202  } FOR_INV_FINISH(); /* for loop looking for an item */
203 
204  if (!tmp) {
207  "%s%s has nothing you can steal!",
208  op->type == PLAYER ? "" : "The ", name);
209  return 0;
210  }
211 
212  /* If you arent high enough level, you might get something BUT
213  * the victim will notice your stealing attempt. Ditto if you
214  * attempt to steal something heavy off them, they're bound to notice
215  */
216 
217  if (roll >= skill->level
218  || !chance
219  || (tmp && tmp->weight > 250*random_roll(0, stats_value+skill->level*10-1, who, PREFER_LOW))) {
220  /* victim figures out where the thief is! */
221  if (who->hide)
222  make_visible(who);
223 
224  if (op->type != PLAYER) {
225  /* The unaggressives look after themselves 8) */
226  if (who->type == PLAYER) {
230  "%s notices your attempted pilfering!",
231  name);
232  }
234  /* all remaining npc items are guarded now. Set flag NO_STEAL
235  * on the victim.
236  */
238  } else { /* stealing from another player */
239  char buf[MAX_BUF];
240 
241  /* Notify the other player */
242  if (success && who->stats.Int > random_roll(0, 19, op, PREFER_LOW)) {
243  query_name(success, name, MAX_BUF);
244  snprintf(buf, sizeof(buf), "Your %s is missing!", name);
245  } else {
246  snprintf(buf, sizeof(buf), "Your pack feels strangely lighter.");
247  }
249  buf);
250  if (!success) {
251  if (who->invisible) {
252  snprintf(buf, sizeof(buf), "you feel itchy fingers getting at your pack.");
253  } else {
255  snprintf(buf, sizeof(buf), "%s looks very shifty.", name);
256  }
258  buf);
259  }
260  } /* else stealing from another player */
261  /* play_sound("stop! thief!"); kindofthing */
262  } /* if you weren't 100% successful */
263  return success ? 1 : 0;
264 }
265 
266 
279 int steal(object *op, int dir, object *skill) {
280  object *tmp;
281  int16_t x, y;
282  mapstruct *m;
283  int mflags;
284 
285  x = op->x+freearr_x[dir];
286  y = op->y+freearr_y[dir];
287 
288  if (dir == 0) {
289  /* Can't steal from ourself! */
290  return 0;
291  }
292 
293  m = op->map;
294  mflags = get_map_flags(m, &m, x, y, &x, &y);
295  /* Out of map - can't do it. If nothing alive on this space,
296  * don't need to look any further.
297  */
298  if ((mflags&P_OUT_OF_MAP) || !(mflags&P_IS_ALIVE))
299  return 0;
300 
301  /* If player can't move onto the space, can't steal from it. */
303  return 0;
304 
305  /* Find the topmost object at this spot */
306  tmp = GET_MAP_OB(m, x, y);
308  if (tmp->above == NULL)
309  break;
311 
312  /* For all the stacked objects at this point, attempt a steal */
314  /* Minor hack--for multi square beings - make sure we get
315  * the 'head' coz 'tail' objects have no inventory! - b.t.
316  */
317  tmp = HEAD(tmp);
318  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER))
319  continue;
320 
321  /* do not reveal hidden DMs */
322  if (tmp->type == PLAYER && QUERY_FLAG(tmp, FLAG_WIZ) && tmp->contr->hidden)
323  continue;
324  if (attempt_steal(tmp, op, skill)) {
325  if (tmp->type == PLAYER) /* no xp for stealing from another player */
326  return 0;
327 
328  /* no xp for stealing from pets (of players) */
329  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
330  object *owner = object_get_owner(tmp);
331  if (owner != NULL && owner->type == PLAYER)
332  return 0;
333  }
334 
335  return (calc_skill_exp(op, tmp, skill));
336  }
338  return 0;
339 }
340 
355 static int attempt_pick_lock(object *door, object *pl, object *skill) {
356  int difficulty = pl->map->difficulty ? pl->map->difficulty : 0;
357  int success = 0, number; /* did we get anything? */
358 
359  /* Try to pick the lock on this item (doors only for now).
360  * Dependent on dexterity/skill SK_level of the player and
361  * the map level difficulty.
362  */
363  number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2;
364  // Spring traps even if the lock is picked.
365  if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) { /* set off any traps? */
366  spring_trap(door->inv, pl);
367  }
368  if (number < pl->stats.Dex + skill->level*2 - difficulty ) {
369  remove_door(door);
370  success = difficulty;
371  }
372  return success;
373 }
374 
375 
391 int pick_lock(object *pl, int dir, object *skill) {
392  object *tmp;
393  int x = pl->x+freearr_x[dir];
394  int y = pl->y+freearr_y[dir];
395  int difficulty=0;
396 
397  if (!dir)
398  dir = pl->facing;
399 
400  /* For all the stacked objects at this point find a door*/
401  if (OUT_OF_REAL_MAP(pl->map, x, y)) {
403  "There is no lock there.");
404  return 0;
405  }
406 
407  for (tmp = GET_MAP_OB(pl->map, x, y); tmp; tmp = tmp->above)
408  if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
409  break;
410 
411  if (!tmp) {
413  "There is no lock there.");
414  return 0;
415  }
416 
418  return 0;
419 
420  if (tmp->type == LOCKED_DOOR) {
422  "You can't pick that lock!");
423  return 0;
424  }
425 
426  if (!tmp->move_block) {
428  "The door has no lock!");
429  return 0;
430  }
431 
432  difficulty = attempt_pick_lock(tmp, pl, skill);
433  /* Failure */
434  if (!difficulty) {
436  "You fail to pick the lock.");
437  return 0;
438  }
439 
441  "You pick the lock.");
442  return (calc_skill_exp(pl, NULL, skill) * isqrt(difficulty));
443 }
444 
445 
465 static int attempt_hide(object *op, object *skill) {
466  int number, difficulty = op->map->difficulty;
467  int terrain = hideability(op);
468 
469  if (terrain < -10) /* not enough cover here */
470  return 0;
471 
472  /* Hiding success and duration dependant on skill level,
473  * op->stats.Dex, map difficulty and terrain.
474  */
475 
476  number = (die_roll(2, 25, op, PREFER_LOW)-2)/2;
477  if (!stand_near_hostile(op) && number < op->stats.Dex+skill->level+terrain-difficulty) {
478  op->invisible += 100; /* set the level of 'hiddeness' */
479  if (op->type == PLAYER)
480  op->contr->tmp_invis = 1;
481  op->hide = 1;
482  return 1;
483  }
484  return 0;
485 }
486 
496 int hide(object *op, object *skill) {
497  /* the preliminaries -- Can we really hide now? */
498  /* this keeps monsters from using invisibilty spells and hiding */
499 
500  if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
502  "You don't need to hide while invisible!");
503  return 0;
504  }
505 
506  if (!op->hide && op->invisible > 0 && op->type == PLAYER) {
508  "Your attempt to hide breaks the invisibility spell!");
509  make_visible(op);
510  }
511 
512  if (op->invisible > 50*skill->level) {
514  "You are as hidden as you can get.");
515  return 0;
516  }
517 
518  if (attempt_hide(op, skill)) {
520  "You hide in the shadows.");
522  return calc_skill_exp(op, NULL, skill);
523  }
525  "You fail to conceal yourself.");
526  return 0;
527 }
528 
529 
536 static void stop_jump(object *pl) {
537  fix_object(pl);
538  object_insert_in_map_at(pl, pl->map, pl, 0, pl->x, pl->y);
539  // Some monsters can also jump. They do not check for pickup with check_pick().
540  if (pl->contr)
541  check_pick(pl);
542 }
543 
557 static int attempt_jump(object *pl, int dir, int spaces, object *skill) {
562  if (pl->contr && pl->contr->transport){
563  char trans_name[MAX_BUF];
564  query_name(pl->contr->transport, trans_name, MAX_BUF);
566  "Your bounce off the walls of %s.", trans_name);
567  // We failed to jump. Return as a failure.
568  return 0;
569  }
570 
571  int i, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
572  int16_t x, y;
573  mapstruct *m;
574 
575  /* Jump loop. Go through spaces opject wants to jump. Halt the
576  * jump if a wall or creature is in the way. We set FLY_LOW
577  * temporarily to allow player to aviod exits/archs that are not
578  * move_on/off fly_low. This will also prevent pickup of objects
579  * while jumping over them.
580  */
581 
582  object_remove(pl);
583 
584  /*
585  * I don't think this is actually needed - all the movement
586  * code is handled in this function, and I don't see anyplace
587  * that cares about the move_type being flying.
588  */
589  pl->move_type |= MOVE_FLY_LOW;
590 
591  for (i = 0; i <= spaces; i++) {
592  x = pl->x+dx;
593  y = pl->y+dy;
594  m = pl->map;
595 
596  mflags = get_map_flags(m, &m, x, y, &x, &y);
597 
598  if (mflags&P_OUT_OF_MAP) {
599  (void)stop_jump(pl);
600  return 0;
601  }
604  "Your jump is blocked.");
605  stop_jump(pl);
606  return 0;
607  }
608 
609  FOR_MAP_PREPARE(m, x, y, tmp) {
610  tmp = HEAD(tmp);
611  /* Jump into creature */
613  || (tmp->type == PLAYER && (!QUERY_FLAG(tmp, FLAG_WIZ) || !tmp->contr->hidden))) {
615  "You jump into %s%s.",
616  tmp->type == PLAYER ? "" : "the ", tmp->name);
617 
618  stop_jump(pl);
619  if (tmp->type != PLAYER
620  || (pl->type == PLAYER && pl->contr->party == NULL)
621  || (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
622  skill_attack(tmp, pl, pl->facing, "kicked", skill); /* pl makes an attack */
623 
624  return 1;
625  }
626  /* If the space has fly on set (no matter what the space is),
627  * we should get the effects - after all, the player is
628  * effectively flying.
629  */
630  if (tmp->move_on&MOVE_FLY_LOW) {
631  pl->x = x;
632  pl->y = y;
633  pl->map = m;
634  if (pl->contr)
635  esrv_map_scroll(pl->contr->socket, dx, dy);
636  stop_jump(pl);
637  return 1;
638  }
639  } FOR_MAP_FINISH();
640  pl->x = x;
641  pl->y = y;
642  pl->map = m;
643  if (pl->contr)
644  esrv_map_scroll(pl->contr->socket, dx, dy);
645  }
646  stop_jump(pl);
647  return 1;
648 }
649 
666 int jump(object *pl, int dir, object *skill) {
667  int spaces = 0, stats;
668  int str = pl->stats.Str;
669  int dex = pl->stats.Dex;
670 
671  dex = dex ? dex : 15;
672  str = str ? str : 10;
673 
674  stats = str*str*str*dex*skill->level;
675 
676  if (pl->carrying != 0) /* don't want div by zero !! */
677  spaces = (int)(stats/pl->carrying);
678  else
679  spaces = 2; /* pl has no objects - gets the far jump */
680 
681  if (spaces > 2)
682  spaces = 2;
683  else if (spaces == 0) {
685  "You are carrying too much weight to jump.");
686  return 0;
687  }
688  return attempt_jump(pl, dir, spaces, skill);
689 }
690 
703 int detect_curse_on_item(object *pl, object *tmp, object *skill) {
706  && tmp->item_power < skill->level) {
709  return NROF(tmp) * calc_skill_exp(pl, tmp, skill);
710  }
711  return 0;
712 }
723 static int do_skill_detect_curse(object *pl, object *skill) {
724  int success = 0;
725 
727  if (!tmp->invisible) success += detect_curse_on_item(pl, tmp, skill);
728  FOR_INV_FINISH();
729 
730  /* Check ground, too, but only objects the player could pick up. Cauldrons are exceptions,
731  * you definitely want to know if they are cursed */
732  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
734  success += detect_curse_on_item(pl, tmp, skill);
735  FOR_MAP_FINISH();
736 
737  return success;
738 }
752 int detect_magic_on_item(object *pl, object *tmp, object *skill) {
754  && (is_magical(tmp)) && tmp->item_power < skill->level) {
757  return NROF(tmp) * calc_skill_exp(pl, tmp, skill);
758  }
759  return 0;
760 }
761 
772 static int do_skill_detect_magic(object *pl, object *skill) {
773  int success = 0;
774 
776  if (!tmp->invisible)
777  success += detect_magic_on_item(pl, tmp, skill);
778  FOR_INV_FINISH();
779 
780  /* Check ground, too, but like above, only if the object can be picked up*/
781  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
782  if (object_can_pick(pl, tmp))
783  success += detect_magic_on_item(pl, tmp, skill);
784  FOR_MAP_FINISH();
785 
786  return success;
787 }
788 
810 int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success) {
811  int success = 0, chance;
812  int skill_value = (skill->level && pl->stats.Int) ? pl->stats.Int : 10;
813 
814  if (!tmp->invisible && !QUERY_FLAG(tmp, FLAG_NO_SKILL_IDENT) && !is_identified(tmp)) {
815  uint32_t identified = 0;
816  for (uint32_t i = 0; i < NROF(tmp); i++) {
817  chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
818  if (skill_value >= chance) {
819  identified++;
820  }
821  }
822 
823  if (identified == 0) {
825  object_merge(tmp, NULL);
826  return 0;
827  }
828 
829  if (identified < NROF(tmp)) {
830  object *left = tmp;
831  tmp = object_split(tmp, identified, NULL, 0);
832  SET_FLAG(left, FLAG_NO_SKILL_IDENT); // Will prevent tmp to be merged right back.
833  // It may happen that tmp is merged while inserting.
834  // It should not be the case because it means somewhere items were not merged correctly.
835  // In this case the player will identify all items but get exp only for "identified" items.
836  if (left->env) {
837  tmp = object_insert_in_ob(tmp, left->env);
838  } else {
839  tmp = object_insert_in_map_at(tmp, left->map, left, INS_BELOW_ORIGINATOR, left->x, left->y);
840  }
841  object_merge(left, NULL);
842  }
843 
844  tmp = identify(tmp);
845  if (pl->type == PLAYER && print_on_success) {
846  char desc[MAX_BUF];
847 
849  "You identify %s.",
850  ob_describe(tmp, pl, 1, desc, sizeof(desc)));
851  if (tmp->msg) {
853  "The item has a story:\n%s",
854  tmp->msg);
855  }
856  }
857  success += identified * calc_skill_exp(pl, tmp, skill);
858  }
859  return success;
860 }
861 
874 static int do_skill_ident(object *pl, int obj_class, object *skill) {
875  int success = 0, area, i;
876 
877  /* check the player */
879  if (tmp->type == obj_class)
880  success += identify_object_with_skill(tmp, pl, skill, 1);
881  FOR_INV_FINISH();
882 
883  /* check the ground */
884  /* Altered to allow ident skills to increase in area with
885  * experience. -- Aaron Baugher
886  */
887 
888  if (skill->level > 64) { /* Adjust these levels? */
889  area = 49;
890  } else if (skill->level > 16) {
891  area = 25;
892  } else if (skill->level > 4) {
893  area = 9;
894  } else {
895  area = 1;
896  }
897 
898  for (i = 0; i < area; i++) {
899  int16_t x = pl->x+freearr_x[i];
900  int16_t y = pl->y+freearr_y[i];
901  mapstruct *m = pl->map;
902  int mflags;
903 
904  mflags = get_map_flags(m, &m, x, y, &x, &y);
905  if (mflags&P_OUT_OF_MAP)
906  continue;
907 
908  if (can_see_monsterP(m, pl->x, pl->y, i)) {
909  FOR_MAP_PREPARE(m, x, y, tmp)
910  if (tmp->type == obj_class)
911  success += identify_object_with_skill(tmp, pl, skill, 1);
912  FOR_MAP_FINISH();
913  }
914  }
915  return success;
916 }
917 
927 int skill_ident(object *pl, object *skill) {
928  int success = 0;
929  int i, identifiable_types=0;
930  const typedata *tmptype;
931 
932  if (pl->type != PLAYER)
933  return 0; /* only players will skill-identify */
934 
936  "You look at the objects nearby with your %s skill...", skill->name);
937 
938  switch (skill->subtype) {
939  case SK_DET_CURSE:
940  success = do_skill_detect_curse(pl, skill);
941  if (success)
943  "...and discover cursed items!");
944  break;
945 
946  case SK_DET_MAGIC:
947  success = do_skill_detect_magic(pl, skill);
948  if (success)
950  "...and discover items imbued with mystic forces!");
951  break;
952 
953  default:
954  /* we will try to identify items with this skill instead */
955  for (i=0; i<=OBJECT_TYPE_MAX; i++) {
956  tmptype = get_typedata(i);
957  if (tmptype) {
958  if (skill->subtype == tmptype->identifyskill || skill->subtype == tmptype->identifyskill2) {
959  success += do_skill_ident(pl, i, skill);
960  identifiable_types++;
961  }
962  }
963  }
964  if (identifiable_types == 0) {
965  LOG(llevError, "Error: skill_ident() called with skill %d which can't identify any items\n", skill->subtype);
966  return 0;
967  }
968  break;
969  }
970  if (!success) {
972  "...and learn nothing more.");
973  }
974  return success;
975 }
976 
977 
996 int use_oratory(object *pl, int dir, object *skill) {
997  int16_t x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
998  int mflags, chance;
999  object *tmp;
1000  mapstruct *m;
1001  char name[MAX_BUF];
1002 
1003  if (pl->type != PLAYER)
1004  return 0; /* only players use this skill */
1005  m = pl->map;
1006  mflags = get_map_flags(m, &m, x, y, &x, &y);
1007  if (mflags&P_OUT_OF_MAP)
1008  return 0;
1009 
1010  /* Save some processing - we have the flag already anyways
1011  */
1012  if (!(mflags&P_IS_ALIVE)) {
1014  "There is nothing to orate to.");
1015  return 0;
1016  }
1017 
1018  tmp = NULL;
1019  FOR_MAP_PREPARE(m, x, y, tmp2) {
1020  tmp2 = HEAD(tmp2);
1021  /* can't persuade players - return because there is nothing else
1022  * on that space to charm.
1023  */
1024  if (tmp2->type == PLAYER)
1025  return 0;
1026 
1027  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1028  if (object_value_set(tmp2, "no_mood_change"))
1029  return 0;
1030 
1031  tmp = tmp2;
1032  break;
1033  }
1034  } FOR_MAP_FINISH();
1035 
1036  if (!tmp) {
1038  "There is nothing to orate to.");
1039  return 0;
1040  }
1041 
1044  "You orate to the %s.",
1045  name);
1046 
1047  /* the following conditions limit who may be 'charmed' */
1048 
1049  /* it's hostile! */
1053  "Too bad the %s isn't listening!",
1054  name);
1055  return 0;
1056  }
1057 
1058  /* it's already allied! */
1059  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
1060  if (object_get_owner(tmp) == pl) {
1062  "Your follower loves your speech.");
1063  return 0;
1064  }
1065 
1066  if (skill->level > tmp->level) {
1067  /* you steal the follower. Perhaps we should really look at the
1068  * level of the owner above?
1069  */
1073  "You convince the %s to follow you instead!",
1074  name);
1075 
1076  FREE_AND_COPY(tmp->skill, skill->skill);
1077 
1078  /* Abuse fix - don't give exp since this can otherwise
1079  * be used by a couple players to gets lots of exp.
1080  */
1081  return 0;
1082  }
1083 
1084  /* In this case, you can't steal it from the other player */
1085  return 0;
1086  } /* Creature was already a pet of someone */
1087 
1088  chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
1089 
1090  /* Ok, got a 'sucker' lets try to make them a follower */
1091  if (chance > 0 && tmp->level < random_roll(0, chance-1, pl, PREFER_HIGH)-1) {
1092  int64_t exp;
1095  "You convince the %s to become your follower.",
1096  name);
1097 
1099  /* compute exp before setting to 0, else the monster's experience is not taken into account. */
1100  tmp->stats.exp /= 5; /* why 5? because. */
1101  exp = calc_skill_exp(pl, tmp, skill);
1102  tmp->stats.exp = 0;
1105  tmp->attack_movement = PETMOVE;
1106  /* keep oratory skill, so exp goes where it should if the pet kills something */
1107  FREE_AND_COPY(tmp->skill, skill->skill);
1108  return exp;
1109  }
1110 
1111  /* Charm failed. Creature may be angry now */
1112  if (skill->level+(pl->stats.Cha-10)/2 < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
1115  "Your speech angers the %s!",
1116  name);
1117 
1118  if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1121  tmp->attack_movement = 0; /* needed? */
1122  }
1124  }
1125 
1126  return 0; /* Fall through - if we get here, we didn't charm anything */
1127 }
1128 
1149 int singing(object *pl, int dir, object *skill) {
1150  int i, exp = 0, chance, mflags;
1151  object *tmp;
1152  mapstruct *m;
1153  int16_t x, y;
1154  char name[MAX_BUF];
1155 
1156  if (pl->type != PLAYER)
1157  return 0; /* only players use this skill */
1158 
1160  "You sing");
1161  for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
1162  x = pl->x+freearr_x[i];
1163  y = pl->y+freearr_y[i];
1164  m = pl->map;
1165 
1166  mflags = get_map_flags(m, &m, x, y, &x, &y);
1167  if (mflags&P_OUT_OF_MAP)
1168  continue;
1169  if (!(mflags&P_IS_ALIVE))
1170  continue;
1171 
1172  tmp = NULL;
1173  FOR_MAP_PREPARE(m, x, y, tmp2) {
1174  tmp2 = HEAD(tmp2);
1175  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1176  tmp = tmp2;
1177  break;
1178  }
1179  /* can't affect players */
1180  if (tmp2->type == PLAYER) {
1181  tmp = tmp2;
1182  break;
1183  }
1184  } FOR_MAP_FINISH();
1185 
1186  /* Whole bunch of checks to see if this is a type of monster that would
1187  * listen to singing.
1188  */
1189  if (tmp
1191  && !QUERY_FLAG(tmp, FLAG_NO_STEAL) /* Been charmed or abused before */
1192  && !QUERY_FLAG(tmp, FLAG_SPLITTING) /* no ears */
1193  && !QUERY_FLAG(tmp, FLAG_HITBACK) /* was here before */
1194  && (tmp->level <= skill->level)
1195  && !QUERY_FLAG(tmp, FLAG_UNDEAD)
1196  && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) /* already calm */
1197  && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) { /* already calm */
1198  /* stealing isn't really related (although, maybe it should
1199  * be). This is mainly to prevent singing to the same monster
1200  * over and over again and getting exp for it.
1201  */
1202  chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
1203 
1204  if (object_value_set(tmp, "no_mood_change"))
1205  chance = 0;
1206 
1207  if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
1211  "You calm down the %s",
1212  name);
1213 
1214  /* Give exp only if they are not aware */
1215  if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
1216  exp += calc_skill_exp(pl, tmp, skill);
1218  } else {
1221  "Too bad the %s isn't listening!",
1222  name);
1224  }
1225  }
1226  }
1227  return exp;
1228 }
1229 
1240 int find_traps(object *pl, object *skill) {
1241  int i, expsum = 0, mflags;
1242  int16_t x, y;
1243  mapstruct *m;
1244 
1245  /* First we search all around us for runes and traps, which are
1246  * all type RUNE
1247  */
1248 
1249  for (i = 0; i < 9; i++) {
1250  x = pl->x+freearr_x[i];
1251  y = pl->y+freearr_y[i];
1252  m = pl->map;
1253 
1254  mflags = get_map_flags(m, &m, x, y, &x, &y);
1255  if (mflags&P_OUT_OF_MAP)
1256  continue;
1257 
1258  /* Check everything in the square for trapness */
1259  FOR_MAP_PREPARE(m, x, y, tmp) {
1260  /* And now we'd better do an inventory traversal of each
1261  * of these objects' inventory
1262  * We can narrow this down a bit - no reason to search through
1263  * the players inventory or monsters for that matter.
1264  */
1265  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1266  FOR_INV_PREPARE(tmp, tmp2)
1267  if (tmp2->type == RUNE || tmp2->type == TRAP)
1268  if (trap_see(pl, tmp2)) {
1269  trap_show(tmp2, tmp);
1270  if (tmp2->stats.Cha > 1) {
1271  object *owner;
1272 
1273  owner = object_get_owner(tmp2);
1274  if (owner == NULL || owner->type != PLAYER)
1275  expsum += calc_skill_exp(pl, tmp2, skill);
1276 
1277  tmp2->stats.Cha = 1; /* unhide the trap */
1278  }
1279  }
1280  FOR_INV_FINISH();
1281  }
1282  if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
1283  trap_show(tmp, tmp);
1284  if (tmp->stats.Cha > 1) {
1285  object *owner;
1286 
1287  owner = object_get_owner(tmp);
1288  if (owner == NULL || owner->type != PLAYER)
1289  expsum += calc_skill_exp(pl, tmp, skill);
1290  tmp->stats.Cha = 1; /* unhide the trap */
1291  }
1292  }
1293  } FOR_MAP_FINISH();
1294  }
1296  "You search the area.");
1297  return expsum;
1298 }
1299 
1311 int remove_trap(object *op, object *skill) {
1312  int i, success = 0, mflags;
1313  mapstruct *m;
1314  int16_t x, y;
1315 
1316  for (i = 0; i < 9; i++) {
1317  x = op->x+freearr_x[i];
1318  y = op->y+freearr_y[i];
1319  m = op->map;
1320 
1321  mflags = get_map_flags(m, &m, x, y, &x, &y);
1322  if (mflags&P_OUT_OF_MAP)
1323  continue;
1324 
1325  /* Check everything in the square for trapness */
1326  FOR_MAP_PREPARE(m, x, y, tmp) {
1327  /* And now we'd better do an inventory traversal of each
1328  * of these objects inventory. Like above, only
1329  * do this for interesting objects.
1330  */
1331 
1332  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1333  FOR_INV_PREPARE(tmp, tmp2)
1334  if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
1335  object *owner;
1336 
1337  trap_show(tmp2, tmp);
1338  if (trap_disarm(op, tmp2, 1, skill) && ((owner = object_get_owner(tmp2)) == NULL || owner->type != PLAYER)) {
1339  tmp2->stats.exp = tmp2->stats.Cha*tmp2->level;
1340  success += calc_skill_exp(op, tmp2, skill);
1341  } else {
1342  /* Can't continue to disarm after failure */
1343  return success;
1344  }
1345  }
1346  FOR_INV_FINISH();
1347  }
1348  if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
1349  object *owner;
1350 
1351  trap_show(tmp, tmp);
1352  if (trap_disarm(op, tmp, 1, skill) && ((owner = object_get_owner(tmp)) == NULL || owner->type != PLAYER)) {
1353  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1354  success += calc_skill_exp(op, tmp, skill);
1355  } else {
1356  /* Can't continue to disarm after failure */
1357  return success;
1358  }
1359  }
1360  } FOR_MAP_FINISH();
1361  }
1362  return success;
1363 }
1364 
1365 
1384 int pray(object *pl, object *skill) {
1385  char buf[MAX_BUF];
1386 
1387  if (pl->type != PLAYER)
1388  return 0;
1389 
1390  snprintf(buf, sizeof(buf), "You pray.");
1391 
1392  /* Check all objects - we could stop at floor objects,
1393  * but if someone buries an altar, I don't see a problem with
1394  * going through all the objects, and it shouldn't be much slower
1395  * than extra checks on object attributes.
1396  */
1398  /* Only if the altar actually belongs to someone do you get special benefits */
1399  if (tmp->type == HOLY_ALTAR && tmp->other_arch) {
1400  snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
1401  pray_at_altar(pl, tmp, skill);
1402  break; /* Only pray at one altar */
1403  }
1404  FOR_BELOW_FINISH();
1405 
1407  buf);
1408 
1409  if (pl->stats.grace < pl->stats.maxgrace) {
1410  pl->stats.grace++;
1411  pl->last_grace = -1;
1412  }
1413  return 0;
1414 }
1415 
1432 void meditate(object *pl, object *skill) {
1433  if (pl->type != PLAYER)
1434  return; /* players only */
1435 
1436  /* check if pl has removed encumbering armour and weapons */
1437  if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && skill->level < 6) {
1439  "You can't concentrate while wielding a weapon!");
1440  return;
1441  }
1442 
1444  if (((tmp->type == ARMOUR && skill->level < 12)
1445  || (tmp->type == HELMET && skill->level < 10)
1446  || (tmp->type == SHIELD && skill->level < 6)
1447  || (tmp->type == BOOTS && skill->level < 4)
1448  || (tmp->type == GLOVES && skill->level < 2))
1449  && QUERY_FLAG(tmp, FLAG_APPLIED)) {
1451  "You can't concentrate while wearing so much armour!");
1452  return;
1453  }
1454  FOR_INV_FINISH();
1455 
1456  /* ok let's meditate! Spell points are regained first, then once
1457  * they are maxed we get back hp. Actual incrementing of values
1458  * is handled by the do_some_living() (in player.c). This way magical
1459  * bonuses for healing/sp regeneration are included properly
1460  * No matter what, we will eat up some playing time trying to
1461  * meditate. (see 'factor' variable for what sets the amount of time)
1462  */
1463 
1465  "You meditate.");
1466 
1467  if (pl->stats.sp < pl->stats.maxsp) {
1468  pl->stats.sp++;
1469  pl->last_sp = -1;
1470  } else if (pl->stats.hp < pl->stats.maxhp) {
1471  pl->stats.hp++;
1472  pl->last_heal = -1;
1473  }
1474 }
1475 
1489 static int write_note(object *pl, object *item, const char *msg) {
1490  char buf[BOOK_BUF];
1491  object *newBook = NULL;
1492 
1493  // The item should exist and be a book, but check it just in case.
1494  if (!item || item->type != BOOK) {
1496  "That was interesting...");
1497  // TODO: Print a scary log message.
1498  return 0;
1499  }
1500 
1501  // The message should never be NULL, but check it just in case.
1502  if (!msg) {
1504  "Hmm... what was I going to write?");
1505  // TODO: Print a scary log message.
1506  return 0;
1507  }
1508 
1509  // Don't let the player write a reserved keyword.
1510  if (strcasestr_local(msg, "endmsg")) {
1512  "Trying to cheat now are we?");
1513  return 0;
1514  }
1515 
1517  return strlen(msg);
1518 
1519  buf[0] = 0;
1520 
1521  // Write the message in the book if it doesn't overflow the buffer.
1522  if (!book_overflow(item->msg, msg, BOOK_BUF)) {
1523  // TODO: Garble some characters depending on intelligence/skill.
1524 
1525  // If there was already text, append the new message on the end.
1526  if (item->msg) {
1527  snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
1528  } else {
1529  snprintf(buf, sizeof(buf), "%s\n", msg);
1530  }
1531 
1532  // If there were multiple items in a stack, unstack one and write.
1533  if (item->nrof > 1) {
1534  newBook = object_new();
1535  object_copy(item, newBook);
1537  newBook->nrof = 1;
1538  object_set_msg(newBook, buf);
1539  newBook = object_insert_in_ob(newBook, pl);
1540  } else {
1542 
1543  // This shouldn't be necessary; the object hasn't changed visibly.
1544  //esrv_send_item(pl, item);
1545  }
1546 
1547  // Tell the player that he/she wrote in the object.
1551  "You write in the %s.", buf);
1552 
1553  // Give the player experience for writing.
1554  return strlen(msg);
1555  } else {
1559  "Your message won't fit in the %s!", buf);
1560  return 0;
1561  }
1562 }
1563 
1578 static int write_scroll(object *pl, object *scroll, object *skill) {
1579  int success = 0, confused = 0, grace_cost = 0;
1580  object *newscroll, *chosen_spell, *tmp;
1581 
1582  // The item should exist and be a scroll, but check it just in case.
1583  if (!scroll || scroll->type != SCROLL) {
1585  "A spell must be written on a magic scroll!");
1586  // TODO: Print a scary log message.
1587  return 0;
1588  }
1589 
1590  // The player must have a spell readied to inscribe.
1591  chosen_spell = pl->contr->ranges[range_magic];
1592  if (!chosen_spell) {
1594  "You should ready the spell you wish to inscribe.");
1595  return 0;
1596  }
1597 
1598  // Make sure the player has enough SP or grace to write the spell.
1599  grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1600  if (grace_cost > 0 && grace_cost > pl->stats.grace) {
1603  "You don't have enough grace to write a scroll of %s.",
1604  chosen_spell->name);
1605  return 0;
1606  }
1607 
1608  if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
1611  "You don't have enough mana to write a scroll of %s.",
1612  chosen_spell->name);
1613  return 0;
1614  }
1615 
1616  // Prevent players from writing spells that they are denied.
1617  if (chosen_spell->path_attuned & pl->path_denied
1619  char name[MAX_BUF];
1620 
1621  query_name(chosen_spell, name, MAX_BUF);
1624  "Just the idea of writing a scroll of %s makes you sick!",
1625  name);
1626  return 0;
1627  }
1628 
1629  // If the scroll already had a spell written on it, the player could
1630  // accidentally read it while trying to write a new one. Give the player
1631  // a 50% chance to overwrite a spell at their own level.
1632  if ((scroll->stats.sp || scroll->inv) &&
1633  random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
1635  "Oops! You accidently read it while trying to write on it.");
1636  apply_manual(pl, scroll, 0);
1637  return 0;
1638  }
1639 
1640  if (events_execute_object_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0) {
1641  return 0;
1642  }
1643 
1644  // Find out if the player is confused or not.
1645  if (QUERY_FLAG(pl, FLAG_CONFUSED)) {
1646  confused = 1;
1647  }
1648 
1649  // Mana or grace is lost no matter if the inscription is successful.
1650  pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1651  pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
1652 
1653  if (random_roll(0, chosen_spell->level * 4 - 1, pl, PREFER_LOW) <
1654  skill->level) {
1655  // If there were multiple items in a stack, unstack one.
1656  if (scroll->nrof > 1) {
1657  newscroll = object_new();
1658  object_copy(scroll, newscroll);
1660  newscroll->nrof = 1;
1661  } else {
1662  newscroll = scroll;
1663  }
1664 
1665  // Write spell if not confused; otherwise write random spell.
1666  newscroll->level = MAX(skill->level, chosen_spell->level);
1667 
1668  if (!confused) {
1669  draw_ext_info(
1671  "You succeed in writing a new scroll.");
1672  } else {
1673  chosen_spell = find_random_spell_in_ob(pl, NULL);
1674  if (!chosen_spell) {
1675  return 0;
1676  }
1677 
1678  draw_ext_info(
1680  "In your confused state, you write down some odd spell.");
1681  }
1682 
1683  if (newscroll->inv) {
1684  object *ninv;
1685  ninv = newscroll->inv;
1686  object_remove(ninv);
1688  }
1689 
1690  tmp = object_new();
1691  object_copy(chosen_spell, tmp);
1692  object_insert_in_ob(tmp, newscroll);
1693 
1694  // This is needed so casting from the scroll works correctly with
1695  // moving_ball types, which checks attunements.
1696  newscroll->path_attuned = tmp->path_repelled;
1697 
1698  // Same code as from treasure.c - so they can better merge.
1699  // If players want to sell them, so be it.
1700  newscroll->value = newscroll->arch->clone.value *
1701  newscroll->inv->value * (newscroll->level + 50 ) /
1702  (newscroll->inv->level + 50);
1703  newscroll->stats.exp = newscroll->value / 5;
1704 
1705  // Finish manipulating the scroll before inserting it.
1706  if (newscroll == scroll) {
1707  // Remove object to stack correctly with other items.
1708  object_remove(newscroll);
1709  }
1710 
1711  newscroll = object_insert_in_ob(newscroll, pl);
1712  success = calc_skill_exp(pl, newscroll, skill);
1713 
1714  if (!confused) {
1715  success *= 2;
1716  }
1717 
1718  success = success * skill->level;
1719  return success;
1720  }
1721 
1722  // Inscription wasn't successful; do something bad to the player.
1723  if (chosen_spell->level > skill->level || confused) {
1724  draw_ext_info(
1726  "Ouch! Your attempt to write a new scroll strains your mind!");
1727 
1728  // Either drain a stat or subtract experience.
1729  if (random_roll(0, 1, pl, PREFER_LOW) == 1) {
1730  drain_specific_stat(pl, 4);
1731  } else {
1732  confuse_living(pl, pl, 99);
1733  return -30 * chosen_spell->level;
1734  }
1735  } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
1736  draw_ext_info(
1738  "Your attempt to write a new scroll rattles your mind!");
1739  confuse_living(pl, pl, 99);
1740  } else {
1741  draw_ext_info(
1743  "You fail to write a new scroll.");
1744  }
1745 
1746  return 0;
1747 }
1748 
1762 int write_on_item(object *pl, const char *params, object *skill) {
1763  object *item;
1764  const char *string = params;
1765  int msgtype;
1766  archetype *skat;
1767 
1768  // Only players can use the inscription skill.
1769  if (pl->type != PLAYER) {
1770  return 0;
1771  }
1772 
1773  // No message was given, so both strings are set to empty.
1774  if (!params) {
1775  params = "";
1776  string = params;
1777  }
1778 
1779  // You must be able to read before you can write.
1781 
1782  if (!find_skill_by_name(pl, skat->clone.skill)) {
1784  "You must learn to read before you can write!");
1785  return 0;
1786  }
1787 
1788  // You must not be blind to write, unless you're a DM.
1789  if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
1791  "You are unable to write while blind.");
1792  return 0;
1793  }
1794 
1795  // If a message was given, write a book. Otherwise, write a scroll.
1796  if (string[0] != '\0') {
1797  msgtype = BOOK;
1798  } else {
1799  msgtype = SCROLL;
1800  }
1801 
1802  // Find and attempt to write on the player's marked item.
1804  if (item == NULL) {
1806  "You haven't marked any items to write on yet.");
1807  return 0;
1808  }
1809 
1810  // Don't let the player write on an unpaid item.
1811  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1813  "You had better pay for that before you write on it!");
1814  return 0;
1815  }
1816 
1817  // Check if the marked item is the type of item we're writing.
1818  if (msgtype != item->type) {
1821  "You must mark a %s to write %s.",
1822  msgtype == BOOK ? "book" : "magic scroll",
1823  msgtype == BOOK ? "your message on" : "your spell down");
1824  return 0;
1825  }
1826 
1827  if (msgtype == BOOK) {
1828  return write_note(pl, item, string);
1829  } else if (msgtype == SCROLL) {
1830  return write_scroll(pl, item, skill);
1831  } else {
1832  // This case should never be reached.
1833  abort();
1834  }
1835 }
1836 
1849 static object *find_throw_ob(object *op, sstring race) {
1850  object *tmp;
1851  char name[MAX_BUF];
1852 
1853  if (!op) { /* safety */
1854  LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1855  return (object *)NULL;
1856  }
1857 
1858  /* prefer marked item */
1860  if (tmp != NULL) {
1861  /* can't toss invisible or inv-locked items */
1862  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1863  tmp = NULL;
1864  }
1865  }
1866 
1867  /* look through the inventory if no marked found */
1868  if (tmp == NULL) {
1869  FOR_INV_PREPARE(op, tmp2) {
1870  /* can't toss invisible items */
1871  if (tmp2->invisible)
1872  continue;
1873 
1874  if (tmp2->type == CONTAINER && QUERY_FLAG(tmp2, FLAG_APPLIED) && tmp2->race == race) {
1875  tmp = find_throw_ob(tmp2, race);
1876  if (tmp != NULL) {
1877  break;
1878  }
1879  }
1880 
1881  /* if not a container, then don't look if locked */
1882  if (tmp2->type == CONTAINER || QUERY_FLAG(tmp2, FLAG_INV_LOCKED))
1883  continue;
1884 
1885  if (tmp2->race == race) {
1886  tmp = tmp2;
1887  break;
1888  }
1889 
1890  } FOR_INV_FINISH();
1891  }
1892 
1893  /* this should prevent us from throwing away
1894  * cursed items, worn armour, etc. Only weapons
1895  * can be thrown from 'hand'.
1896  */
1897  if (!tmp)
1898  return NULL;
1899 
1900  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1901  if (tmp->type != WEAPON) {
1904  "You can't throw %s.",
1905  name);
1906  tmp = NULL;
1907  } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
1911  "The %s sticks to your hand!",
1912  name);
1913  tmp = NULL;
1914  } else {
1916  LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1917  tmp = NULL;
1918  }
1919  }
1920  } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1923  "You should pay for the %s first.",
1924  name);
1925  tmp = NULL;
1926  }
1927 
1928  if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1929  LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
1930  tmp = NULL;
1931  }
1932  return tmp;
1933 }
1934 
1946 static object *make_throw_ob(object *orig) {
1947  object *toss_item;
1948 
1949  if (!orig)
1950  return NULL;
1951 
1952  toss_item = object_new();
1953  if (QUERY_FLAG(orig, FLAG_APPLIED)) {
1954  LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
1955  /* insufficient workaround, but better than nothing */
1956  CLEAR_FLAG(orig, FLAG_APPLIED);
1957  }
1958  object_copy(orig, toss_item);
1959  toss_item->type = THROWN_OBJ;
1960  CLEAR_FLAG(toss_item, FLAG_CHANGING);
1961  toss_item->stats.dam = 0; /* default damage */
1962  object_insert_in_ob(orig, toss_item);
1963  return toss_item;
1964 }
1965 
1966 
1987 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
1988  object *throw_ob = toss_item, *left = NULL;
1989  int eff_str = 0, str = op->stats.Str, dam = 0;
1990  int pause_f, weight_f = 0, mflags;
1991  float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1992  mapstruct *m;
1993  int16_t sx, sy;
1994  tag_t tag;
1995  char name[MAX_BUF];
1996 
1997  if (throw_ob == NULL) {
1998  if (op->type == PLAYER) {
2000  "You have nothing to throw.");
2001  }
2002  return 0;
2003  }
2004  if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
2005  if (op->type == PLAYER) {
2007  "The gods won't let you throw that.");
2008  }
2009  return 0;
2010  }
2011 
2012  tag = throw_ob->count;
2014  if (object_was_destroyed(throw_ob, tag)) {
2015  return 1;
2016  }
2017 
2018  /* Because throwing effectiveness must be reduced by the
2019  * encumbrance of the thrower and weight of the object. THus,
2020  * we use the concept of 'effective strength' as defined below.
2021  */
2022 
2023  /* if str exceeds settings.max_stat (30, eg giants), lets assign a str_factor > 1 */
2024  if (str > settings.max_stat) {
2025  str_factor = (float)str/(float)settings.max_stat;
2026  str = settings.max_stat;
2027  }
2028 
2029  /* the more we carry, the less we can throw. Limit only on players */
2030  if (op->type == PLAYER
2031  && op->carrying > (get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)
2032  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
2047  load_factor = 2.0f - (float)(op->carrying) / (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT);
2048  // Only clip to 1.0 if we get in here, since it is 1.0 if we do not get in here.
2049  load_factor = MIN(load_factor, 1.0f);
2050  }
2051 
2052  /* lighter items are thrown harder, farther, faster */
2053  if (throw_ob->weight > 0)
2054  item_factor = (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)/(float)(3.0*throw_ob->weight);
2055  else { /* 0 or negative weight?!? Odd object, can't throw it */
2056  query_name(throw_ob, name, MAX_BUF);
2058  "You can't throw %s.",
2059  name);
2060  return 0;
2061  }
2062 
2063  eff_str = str*load_factor;
2064  eff_str = (float)eff_str*item_factor*str_factor;
2065 
2066  /* alas, arrays limit us to a value of settings.max_stat (30). Use str_factor to
2067  * account for super-strong throwers. */
2068  if (eff_str > settings.max_stat)
2069  eff_str = settings.max_stat;
2070 
2071 #ifdef DEBUG_THROW
2072  LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
2073  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);
2074  LOG(llevDebug, " str_factor=%f\n", str_factor);
2075  LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
2076 #endif
2077 
2078  /* 3 things here prevent a throw, you aimed at your feet, you
2079  * have no effective throwing strength, or you threw at something
2080  * that flying objects can't get through.
2081  */
2082  mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
2083 
2084  if (!dir
2085  || (eff_str <= 1)
2086  || (mflags&P_OUT_OF_MAP)
2087  || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
2088  /* bounces off 'wall', and drops to feet */
2089  object_remove(throw_ob);
2090  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2091  if (op->type == PLAYER) {
2092  if (eff_str <= 1) {
2093  query_name(throw_ob, name, MAX_BUF);
2095  "Your load is so heavy you drop %s to the ground.",
2096  name);
2097  } else if (!dir) {
2098  query_name(throw_ob, name, MAX_BUF);
2100  "You throw %s at the ground.",
2101  name);
2102  } else
2104  "Something is in the way.");
2105  }
2106  return 0;
2107  } /* if object can't be thrown */
2108 
2109  left = throw_ob; /* these are throwing objects left to the player */
2110 
2111  /* sometimes object_split() can't split an object (because op->nrof==0?)
2112  * and returns NULL. We must use 'left' then
2113  */
2114  throw_ob = object_split(throw_ob, 1, NULL, 0);
2115  if (throw_ob == NULL) {
2116  throw_ob = left;
2117  object_remove(left);
2118  }
2119 
2120  /* special case: throwing powdery substances like dust, dirt */
2121  if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
2122  cast_dust(op, throw_ob, dir);
2123  return 1;
2124  }
2125 
2126  /* Make a thrown object -- insert real object in a 'carrier' object.
2127  * If unsuccessfull at making the "thrown_obj", we just reinsert
2128  * the original object back into inventory and exit
2129  */
2130  toss_item = make_throw_ob(throw_ob);
2131  if (toss_item) {
2132  throw_ob = toss_item;
2133  if (throw_ob->skill)
2134  free_string(throw_ob->skill);
2135  throw_ob->skill = add_string(skill->skill);
2136  } else {
2137  object_insert_in_ob(throw_ob, op);
2138  return 0;
2139  }
2140 
2141  object_set_owner(throw_ob, op);
2142  /* At some point in the attack code, the actual real object (op->inv)
2143  * becomes the hitter. As such, we need to make sure that has a proper
2144  * owner value so exp goes to the right place.
2145  */
2146  object_set_owner(throw_ob->inv, op);
2147  throw_ob->direction = dir;
2148 
2149  /* If the thrown item has reflecting flag, it can ricochet off walls.
2150  * Make sure this gets copied to the thrown object.
2151  * The common projectile code should then handle the reflection from there.
2152  */
2153  if (QUERY_FLAG(throw_ob->inv, FLAG_REFLECTING))
2154  SET_FLAG(throw_ob, FLAG_REFLECTING);
2155 
2156  /* the damage bonus from the force of the throw */
2157  dam = str_factor*get_dam_bonus(eff_str);
2158 
2159  /* Now, lets adjust the properties of the thrown_ob. */
2160 
2161  /* how far to fly */
2162  throw_ob->last_sp = (eff_str*3)/5;
2163 
2164  /* speed */
2165  throw_ob->speed = (get_speed_bonus(eff_str)+1.0)/1.5;
2166  throw_ob->speed = MIN(1.0, throw_ob->speed); /* no faster than an arrow! */
2167 
2168  /* item damage. Eff_str and item weight influence damage done */
2169  weight_f = MIN(throw_ob->weight/2000, settings.max_stat);
2170  throw_ob->stats.dam += (dam/3)+get_dam_bonus(weight_f)+(throw_ob->weight/15000)-2;
2171 
2172  /* chance of breaking. Proportional to force used and weight of item */
2173  throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
2174 
2175  /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
2176  throw_ob->stats.wc = 25-get_dex_bonus(op->stats.Dex)-get_thaco_bonus(eff_str)-skill->level;
2177 
2178 
2179  /* the properties of objects which are meant to be thrown (ie dart,
2180  * throwing knife, etc) will differ from ordinary items. Lets tailor
2181  * this stuff in here.
2182  */
2183 
2184  if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
2185  throw_ob->last_sp += eff_str/3; /* fly a little further */
2186  throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
2187  throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
2188  /* only throw objects get directional faces */
2189  if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
2190  object_update_turn_face(throw_ob);
2191  } else {
2192  /* some materials will adjust properties.. */
2193  if (throw_ob->material&M_LEATHER) {
2194  throw_ob->stats.dam -= 1;
2195  throw_ob->stats.food -= 10;
2196  }
2197  if (throw_ob->material&M_GLASS)
2198  throw_ob->stats.food += 60;
2199 
2200  if (throw_ob->material&M_ORGANIC) {
2201  throw_ob->stats.dam -= 3;
2202  throw_ob->stats.food += 55;
2203  }
2204  if (throw_ob->material&M_PAPER || throw_ob->material&M_CLOTH) {
2205  throw_ob->stats.dam -= 5;
2206  throw_ob->speed *= 0.8;
2207  throw_ob->stats.wc += 3;
2208  throw_ob->stats.food -= 30;
2209  }
2210  /* light obj have more wind resistance, fly slower*/
2211  if (throw_ob->weight > 500)
2212  throw_ob->speed *= 0.8;
2213  if (throw_ob->weight > 50)
2214  throw_ob->speed *= 0.5;
2215  } /* else tailor thrown object */
2216 
2217  /* some limits, and safeties (needed?) */
2218  if (throw_ob->stats.dam < 0)
2219  throw_ob->stats.dam = 0;
2220  if (throw_ob->last_sp > eff_str)
2221  throw_ob->last_sp = eff_str;
2222  if (throw_ob->stats.food < 0)
2223  throw_ob->stats.food = 0;
2224  if (throw_ob->stats.food > 100)
2225  throw_ob->stats.food = 100;
2226  if (throw_ob->stats.wc > 30)
2227  throw_ob->stats.wc = 30;
2228 
2229  /* how long to pause the thrower. Higher values mean less pause */
2230  pause_f = ((2*eff_str)/3)+20+skill->level;
2231 
2232  /* Put a lower limit on this */
2233  if (pause_f < 10)
2234  pause_f = 10;
2235  if (pause_f > 100)
2236  pause_f = 100;
2237 
2238  /* Changed in 0.94.2 - the calculation before was really goofy.
2239  * In short summary, a throw can take anywhere between speed 5 and
2240  * speed 0.5
2241  */
2242  op->speed_left -= 50.0/pause_f;
2243 
2244  object_update_speed(throw_ob);
2245  throw_ob->speed_left = 0;
2246 
2247  throw_ob->move_type = MOVE_FLY_LOW;
2248  throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
2249 
2250 #ifdef DEBUG_THROW
2251  LOG(llevDebug, " pause_f=%d \n", pause_f);
2252  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);
2253  LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
2254 #endif
2255  tag = throw_ob->count;
2256  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2257  if (!object_was_destroyed(throw_ob, tag))
2258  ob_process(throw_ob);
2259  return 1;
2260 }
2261 
2277 int skill_throw(object *op, object *part, int dir, object *skill) {
2278  object *throw_ob;
2279 
2280  if (op->type == PLAYER)
2281  throw_ob = find_throw_ob(op, find_string("throwing"));
2282  else
2283  throw_ob = monster_find_throw_ob(op);
2284 
2285  return do_throw(op, part, throw_ob, dir, skill);
2286 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
object_value_set
bool object_value_set(const object *op, const char *const key)
Definition: object.cpp:4369
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:522
PLAYER
@ PLAYER
Definition: object.h:110
M_LEATHER
#define M_LEATHER
Definition: material.h:17
object_get_owner
object * object_get_owner(object *op)
Definition: object.cpp:804
global.h
FREE_OBJ_NO_DESTROY_CALLBACK
#define FREE_OBJ_NO_DESTROY_CALLBACK
Definition: object.h:534
settings
struct Settings settings
Definition: init.cpp:139
liv::dam
int16_t dam
Definition: living.h:46
object_update_turn_face
void object_update_turn_face(object *op)
Definition: object.cpp:1332
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
remove_friendly_object
void remove_friendly_object(object *op)
Definition: friend.cpp:54
pl::transport
object * transport
Definition: player.h:213
FLAG_CONFUSED
#define FLAG_CONFUSED
Definition: define.h:311
BOW
@ BOW
Definition: object.h:121
llevError
@ llevError
Definition: logger.h:11
AP_NO_MERGE
#define AP_NO_MERGE
Definition: define.h:581
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
strcasestr_local
#define strcasestr_local
Definition: compat.h:28
FLAG_UNDEAD
#define FLAG_UNDEAD
Definition: define.h:270
singing
int singing(object *pl, int dir, object *skill)
Definition: skills.cpp:1149
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
drain_specific_stat
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.cpp:728
monster_can_detect_enemy
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.cpp:2579
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.cpp:1614
GLOVES
@ GLOVES
Definition: object.h:216
find_throw_ob
static object * find_throw_ob(object *op, sstring race)
Definition: skills.cpp:1849
diamondslots.x
x
Definition: diamondslots.py:15
FLAG_STARTEQUIP
#define FLAG_STARTEQUIP
Definition: define.h:268
find_random_spell_in_ob
object * find_random_spell_in_ob(object *ob, const char *skill)
Definition: spell_util.cpp:46
obj::count
tag_t count
Definition: object.h:305
obj::map
struct mapdef * map
Definition: object.h:303
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Definition: newclient.h:407
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
obj::value
int32_t value
Definition: object.h:358
liv::wc
int8_t wc
Definition: living.h:37
confuse_living
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.cpp:2266
object_merge
object * object_merge(object *op, object *top)
Definition: object.cpp:2049
make_visible
void make_visible(object *op)
Definition: player.cpp:3913
MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
Definition: newclient.h:574
SK_DET_MAGIC
@ SK_DET_MAGIC
Definition: skills.h:30
obj::path_attuned
uint32_t path_attuned
Definition: object.h:351
FOR_BELOW_PREPARE
#define FOR_BELOW_PREPARE(op_, it_)
Definition: define.h:704
M_ORGANIC
#define M_ORGANIC
Definition: material.h:19
pl
Definition: player.h:105
TRAP
@ TRAP
Definition: object.h:244
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.cpp:205
ARMOUR
@ ARMOUR
Definition: object.h:123
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
WEAPON
@ WEAPON
Definition: object.h:122
use_oratory
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.cpp:996
typedata::identifyskill
int identifyskill
Definition: define.h:93
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.cpp:840
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:405
do_throw
static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill)
Definition: skills.cpp:1987
commongive.inv
inv
Definition: commongive.py:28
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
detect_magic_on_item
int detect_magic_on_item(object *pl, object *tmp, object *skill)
Definition: skills.cpp:752
SKILL
@ SKILL
Definition: object.h:146
BODY_ARMS
#define BODY_ARMS
Definition: object.h:14
RUNE
@ RUNE
Definition: object.h:243
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.cpp:1192
do_skill_detect_magic
static int do_skill_detect_magic(object *pl, object *skill)
Definition: skills.cpp:772
fix_object
void fix_object(object *op)
Definition: living.cpp:1125
Ice.tmp
int tmp
Definition: Ice.py:207
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
skills.h
pray
int pray(object *pl, object *skill)
Definition: skills.cpp:1384
FLAG_INV_LOCKED
#define FLAG_INV_LOCKED
Definition: define.h:329
apply_manual
int apply_manual(object *op, object *tmp, int aflag)
Definition: apply.cpp:597
FOR_OB_AND_ABOVE_FINISH
#define FOR_OB_AND_ABOVE_FINISH()
Definition: define.h:743
P_IS_ALIVE
#define P_IS_ALIVE
Definition: map.h:238
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
events_execute_object_event
int events_execute_object_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: events.cpp:292
FLAG_BLIND
#define FLAG_BLIND
Definition: define.h:336
buf
StringBuffer * buf
Definition: readable.cpp:1611
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2851
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:415
MAX
#define MAX(x, y)
Definition: compat.h:24
find_traps
int find_traps(object *pl, object *skill)
Definition: skills.cpp:1240
cast_dust
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.cpp:3884
obj::nrof
uint32_t nrof
Definition: object.h:340
FOR_BELOW_FINISH
#define FOR_BELOW_FINISH()
Definition: define.h:711
archt
Definition: object.h:472
FOR_OB_AND_ABOVE_PREPARE
#define FOR_OB_AND_ABOVE_PREPARE(op_)
Definition: define.h:739
typedata
Definition: define.h:89
remove_door
void remove_door(object *op)
Definition: time.cpp:38
m
static event_registration m
Definition: citylife.cpp:425
autojail.who
who
Definition: autojail.py:3
liv::exp
int64_t exp
Definition: living.h:47
object_update
void object_update(object *op, int action)
Definition: object.cpp:1434
NROF
static uint32_t NROF(const object *const ob)
Definition: object.h:614
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
HELMET
@ HELMET
Definition: object.h:139
object_decrease_nrof_by_one
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:32
FLAG_WAS_WIZ
#define FLAG_WAS_WIZ
Definition: define.h:234
obj::name
sstring name
Definition: object.h:317
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.cpp:305
make_throw_ob
static object * make_throw_ob(object *orig)
Definition: skills.cpp:1946
get_typedata
const typedata * get_typedata(int itemtype)
Definition: item.cpp:320
monster_find_throw_ob
object * monster_find_throw_ob(object *op)
Definition: monster.cpp:2540
pick_lock
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.cpp:391
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:585
POT_DUST
#define POT_DUST
Definition: spells.h:133
POTION
@ POTION
Definition: object.h:114
can_see_monsterP
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
Definition: object.cpp:3815
MOVE_WALK
#define MOVE_WALK
Definition: define.h:392
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
SK_LITERACY
@ SK_LITERACY
Definition: skills.h:27
EVENT_TRIGGER
#define EVENT_TRIGGER
Definition: events.h:34
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.cpp:658
SK_DET_CURSE
@ SK_DET_CURSE
Definition: skills.h:33
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
HEAD
#define HEAD(op)
Definition: object.h:596
range_magic
@ range_magic
Definition: player.h:32
CONTAINER
@ CONTAINER
Definition: object.h:234
make_face_from_files.str
str
Definition: make_face_from_files.py:24
obj::speed_left
float speed_left
Definition: object.h:336
query_short_name
void query_short_name(const object *op, char *buf, size_t size)
Definition: item.cpp:510
LOCKED_DOOR
@ LOCKED_DOOR
Definition: object.h:126
SPELL_GRACE
#define SPELL_GRACE
Definition: spells.h:59
MSG_TYPE_ITEM
#define MSG_TYPE_ITEM
Definition: newclient.h:412
find_string
sstring find_string(const char *str)
Definition: shstr.cpp:236
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:382
FLAG_MAKE_INVIS
#define FLAG_MAKE_INVIS
Definition: define.h:328
navar-midane_pickup.msg
list msg
Definition: navar-midane_pickup.py:13
is_identified
int is_identified(const object *op)
Definition: item.cpp:1336
ob_describe
char * ob_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size)
Definition: ob_methods.cpp:91
isqrt
int isqrt(int n)
Definition: utils.cpp:569
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1349
FREE_AND_COPY
#define FREE_AND_COPY(sv, nv)
Definition: global.h:204
obj::x
int16_t x
Definition: object.h:333
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
hide
int hide(object *op, object *skill)
Definition: skills.cpp:496
adj_stealchance
static int adj_stealchance(object *op, object *victim, int roll)
Definition: skills.cpp:45
attempt_pick_lock
static int attempt_pick_lock(object *door, object *pl, object *skill)
Definition: skills.cpp:355
UPD_FLAGS
#define UPD_FLAGS
Definition: newclient.h:315
CFweardisguise.tag
tag
Definition: CFweardisguise.py:25
trap_disarm
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Definition: rune.cpp:433
book.h
object_free
void object_free(object *ob, int flags)
Definition: object.cpp:1592
GET_MAP_MOVE_BLOCK
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:193
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
INS_BELOW_ORIGINATOR
#define INS_BELOW_ORIGINATOR
Definition: object.h:573
MSG_TYPE_SKILL_MISSING
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:587
sstring
const typedef char * sstring
Definition: global.h:43
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
obj::speed
float speed
Definition: object.h:335
tag_t
uint32_t tag_t
Definition: object.h:12
obj::env
struct obj * env
Definition: object.h:299
skill_throw
int skill_throw(object *op, object *part, int dir, object *skill)
Definition: skills.cpp:2277
sproto.h
liv::food
int32_t food
Definition: living.h:48
EVENT_THROW
#define EVENT_THROW
Definition: events.h:33
FLAG_NO_STEAL
#define FLAG_NO_STEAL
Definition: define.h:342
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:750
mapdef
Definition: map.h:317
GET_ANIM_ID
#define GET_ANIM_ID(ob)
Definition: global.h:165
BOOK
@ BOOK
Definition: object.h:117
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.cpp:235
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
get_dam_bonus
int get_dam_bonus(int stat)
Definition: living.cpp:2376
skill_ident
int skill_ident(object *pl, object *skill)
Definition: skills.cpp:927
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:242
obj::material
uint16_t material
Definition: object.h:355
FLAG_SPLITTING
#define FLAG_SPLITTING
Definition: define.h:266
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.cpp:2098
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.cpp:202
FLAG_HITBACK
#define FLAG_HITBACK
Definition: define.h:267
SIZEOFFREE
#define SIZEOFFREE
Definition: define.h:155
MSG_TYPE_VICTIM_STEAL
#define MSG_TYPE_VICTIM_STEAL
Definition: newclient.h:652
get_dex_bonus
int get_dex_bonus(int stat)
Definition: living.cpp:2352
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:250
MAX_BUF
#define MAX_BUF
Definition: define.h:35
object_new
object * object_new(void)
Definition: object.cpp:1273
M_GLASS
#define M_GLASS
Definition: material.h:16
get_weight_limit
uint32_t get_weight_limit(int stat)
Definition: living.cpp:2360
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
trap_show
int trap_show(object *trap, object *where)
Definition: rune.cpp:403
MOVE_FLY_LOW
#define MOVE_FLY_LOW
Definition: define.h:393
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
obj::y
int16_t y
Definition: object.h:333
FLAG_KNOWN_MAGICAL
#define FLAG_KNOWN_MAGICAL
Definition: define.h:319
OUT_OF_REAL_MAP
#define OUT_OF_REAL_MAP(M, X, Y)
Definition: map.h:218
obj::arch
struct archt * arch
Definition: object.h:420
stop_jump
static void stop_jump(object *pl)
Definition: skills.cpp:536
identify_object_with_skill
int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success)
Definition: skills.cpp:810
Settings::allow_denied_spells_writing
int allow_denied_spells_writing
Definition: global.h:315
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
MSG_TYPE_SKILL_ERROR
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:588
obj::type
uint8_t type
Definition: object.h:346
hideability
int hideability(object *ob)
Definition: player.cpp:3957
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
spells.h
ob_process
method_ret ob_process(object *op)
Definition: ob_methods.cpp:67
obj::stats
living stats
Definition: object.h:376
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:590
attempt_steal
static int attempt_steal(object *op, object *who, object *skill)
Definition: skills.cpp:113
obj::direction
int8_t direction
Definition: object.h:342
steal
int steal(object *op, int dir, object *skill)
Definition: skills.cpp:279
OBJECT_TYPE_MAX
@ OBJECT_TYPE_MAX
Definition: object.h:254
archt::clone
object clone
Definition: object.h:476
get_thaco_bonus
int get_thaco_bonus(int stat)
Definition: living.cpp:2356
M_CLOTH
#define M_CLOTH
Definition: material.h:21
object_can_pick
int object_can_pick(const object *who, const object *item)
Definition: object.cpp:3860
add_friendly_object
void add_friendly_object(object *op)
Definition: friend.cpp:34
obj::weight
int32_t weight
Definition: object.h:373
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.cpp:301
item
Definition: item.py:1
MSG_TYPE_ITEM_REMOVE
#define MSG_TYPE_ITEM_REMOVE
Definition: newclient.h:642
reputation.victim
victim
Definition: reputation.py:14
is_magical
int is_magical(const object *op)
Definition: item.cpp:1215
write_note
static int write_note(object *pl, object *item, const char *msg)
Definition: skills.cpp:1489
give.op
op
Definition: give.py:33
monster_npc_call_help
void monster_npc_call_help(object *op)
Definition: monster.cpp:2010
object_split
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
Definition: object.cpp:2631
check_pick
int check_pick(object *op)
Definition: player.cpp:1697
get_speed_bonus
float get_speed_bonus(int stat)
Definition: living.cpp:2380
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.cpp:4804
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.cpp:359
FLAG_REFLECTING
#define FLAG_REFLECTING
Definition: define.h:262
Settings::max_stat
uint8_t max_stat
Definition: global.h:324
rv_vector
Definition: map.h:373
obj::last_sp
int32_t last_sp
Definition: object.h:366
BOOK_BUF
#define BOOK_BUF
Definition: book.h:16
roll-o-matic.params
params
Definition: roll-o-matic.py:193
diamondslots.y
y
Definition: diamondslots.py:16
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
apply_special
int apply_special(object *who, object *op, int aflags)
Definition: apply.cpp:1156
NUM_ANIMATIONS
#define NUM_ANIMATIONS(ob)
Definition: global.h:171
M_PAPER
#define M_PAPER
Definition: material.h:14
obj::subtype
uint8_t subtype
Definition: object.h:347
die_roll
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.cpp:122
SCRIPT_FIX_ACTIVATOR
#define SCRIPT_FIX_ACTIVATOR
Definition: global.h:381
obj::move_type
MoveType move_type
Definition: object.h:432
write_on_item
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.cpp:1762
detect_curse_on_item
int detect_curse_on_item(object *pl, object *tmp, object *skill)
Definition: skills.cpp:703
pl::ranges
object * ranges[range_size]
Definition: player.h:116
typedata::identifyskill2
int identifyskill2
Definition: define.h:94
meditate
void meditate(object *pl, object *skill)
Definition: skills.cpp:1432
make_face_from_files.int
int
Definition: make_face_from_files.py:26
obj::skill
sstring skill
Definition: object.h:327
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.cpp:308
book_overflow
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.cpp:710
get_archetype_by_type_subtype
archetype * get_archetype_by_type_subtype(int type, int subtype)
Definition: arch.cpp:99
object_remove
void object_remove(object *op)
Definition: object.cpp:1833
trap_see
int trap_see(object *op, object *trap)
Definition: rune.cpp:376
DOOR
@ DOOR
Definition: object.h:129
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
OB_TYPE_MOVE_BLOCK
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:432
write_scroll
static int write_scroll(object *pl, object *scroll, object *skill)
Definition: skills.cpp:1578
attempt_jump
static int attempt_jump(object *pl, int dir, int spaces, object *skill)
Definition: skills.cpp:557
do_skill_detect_curse
static int do_skill_detect_curse(object *pl, object *skill)
Definition: skills.cpp:723
do_skill_ident
static int do_skill_ident(object *pl, int obj_class, object *skill)
Definition: skills.cpp:874
SCROLL
@ SCROLL
Definition: object.h:224
PETMOVE
#define PETMOVE
Definition: define.h:501
Settings::no_player_stealing
uint8_t no_player_stealing
Definition: global.h:310
say.item
dictionary item
Definition: say.py:149
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:589
obj::move_on
MoveType move_on
Definition: object.h:435
AP_UNAPPLY
#define AP_UNAPPLY
Definition: define.h:575
stand_near_hostile
int stand_near_hostile(object *who)
Definition: player.cpp:4039
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.cpp:299
BOOTS
@ BOOTS
Definition: object.h:215
pl::party
partylist * party
Definition: player.h:202
FLAG_IS_CAULDRON
#define FLAG_IS_CAULDRON
Definition: define.h:338
FLAG_CHANGING
#define FLAG_CHANGING
Definition: define.h:263
SPELL
@ SPELL
Definition: object.h:217
liv::sp
int16_t sp
Definition: living.h:42
FLAG_READY_WEAPON
#define FLAG_READY_WEAPON
Definition: define.h:334
SHIELD
@ SHIELD
Definition: object.h:138
FLAG_NO_SKILL_IDENT
#define FLAG_NO_SKILL_IDENT
Definition: define.h:335
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
jump
int jump(object *pl, int dir, object *skill)
Definition: skills.cpp:666
pick_up
void pick_up(object *op, object *alt)
Definition: c_object.cpp:519
pl::socket
socket_struct * socket
Definition: player.h:107
living.h
obj::magic
int8_t magic
Definition: object.h:356
FLAG_IS_THROWN
#define FLAG_IS_THROWN
Definition: define.h:249
if
if(!(yy_init))
Definition: loader.c:2626
THROWN_OBJ
@ THROWN_OBJ
Definition: object.h:149
FLAG_SLEEP
#define FLAG_SLEEP
Definition: define.h:307
MSG_TYPE_ITEM_INFO
#define MSG_TYPE_ITEM_INFO
Definition: newclient.h:645
find_marked_object
object * find_marked_object(object *op)
Definition: c_object.cpp:1520
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
HOLY_ALTAR
@ HOLY_ALTAR
Definition: object.h:164
object.h
obj::level
int16_t level
Definition: object.h:359
llevDebug
@ llevDebug
Definition: logger.h:13
remove_trap
int remove_trap(object *op, object *skill)
Definition: skills.cpp:1311
obj::inv
struct obj * inv
Definition: object.h:296
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Definition: define.h:261
give.name
name
Definition: give.py:27
skill_attack
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.cpp:1261
SPELL_MANA
#define SPELL_MANA
Definition: spells.h:58
FREE_PLAYER_LOAD_PERCENT
#define FREE_PLAYER_LOAD_PERCENT
Definition: config.h:98
pray_at_altar
void pray_at_altar(object *pl, object *altar, object *skill)
Definition: gods.cpp:258
attempt_hide
static int attempt_hide(object *op, object *skill)
Definition: skills.cpp:465
identify
object * identify(object *op)
Definition: item.cpp:1409