Crossfire Server, Trunk
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! */
62  roll = roll/2;
63 
64  /* Easier to steal from sleeping beings, or if the thief is
65  * unseen */
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 */
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 
167  || !(inv->type)
168  || inv->type == SPELL
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) {
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) {
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  */
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 {
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. */
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 
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.");
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  }
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 */
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) {
704  && tmp->item_power < skill->level) {
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 
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)
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) {
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 
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;
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  uint32_t identified = 0;
814  for (uint32_t i = 0; i < NROF(tmp); i++) {
815  chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
816  if (skill_value >= chance) {
817  identified++;
818  }
819  }
820 
821  if (identified == 0) {
823  object_merge(tmp, NULL);
824  return 0;
825  }
826 
827  if (identified < NROF(tmp)) {
828  object *left = tmp;
829  tmp = object_split(tmp, identified, NULL, 0);
830  SET_FLAG(left, FLAG_NO_SKILL_IDENT); // Will prevent tmp to be merged right back.
831  // It may happen that tmp is merged while inserting.
832  // It should not be the case because it means somewhere items were not merged correctly.
833  // In this case the player will identify all items but get exp only for "identified" items.
834  if (left->env) {
835  tmp = object_insert_in_ob(tmp, left->env);
836  } else {
837  tmp = object_insert_in_map_at(tmp, left->map, left, INS_BELOW_ORIGINATOR, left->x, left->y);
838  }
839  object_merge(left, NULL);
840  }
841 
842  tmp = identify(tmp);
843  if (pl->type == PLAYER && print_on_success) {
844  char desc[MAX_BUF];
845 
847  "You identify %s.",
848  ob_describe(tmp, pl, 1, desc, sizeof(desc)));
849  if (tmp->msg) {
851  "The item has a story:\n%s",
852  tmp->msg);
853  }
854  }
855  success += identified * calc_skill_exp(pl, tmp, skill);
856  }
857  return success;
858 }
859 
872 static int do_skill_ident(object *pl, int obj_class, object *skill) {
873  int success = 0, area, i;
874 
875  /* check the player */
877  if (tmp->type == obj_class)
878  success += identify_object_with_skill(tmp, pl, skill, 1);
879  FOR_INV_FINISH();
880 
881  /* check the ground */
882  /* Altered to allow ident skills to increase in area with
883  * experience. -- Aaron Baugher
884  */
885 
886  if (skill->level > 64) { /* Adjust these levels? */
887  area = 49;
888  } else if (skill->level > 16) {
889  area = 25;
890  } else if (skill->level > 4) {
891  area = 9;
892  } else {
893  area = 1;
894  }
895 
896  for (i = 0; i < area; i++) {
897  int16_t x = pl->x+freearr_x[i];
898  int16_t y = pl->y+freearr_y[i];
899  mapstruct *m = pl->map;
900  int mflags;
901 
902  mflags = get_map_flags(m, &m, x, y, &x, &y);
903  if (mflags&P_OUT_OF_MAP)
904  continue;
905 
906  if (can_see_monsterP(m, pl->x, pl->y, i)) {
907  FOR_MAP_PREPARE(m, x, y, tmp)
908  if (tmp->type == obj_class)
909  success += identify_object_with_skill(tmp, pl, skill, 1);
910  FOR_MAP_FINISH();
911  }
912  }
913  return success;
914 }
915 
925 int skill_ident(object *pl, object *skill) {
926  int success = 0;
927  int i, identifiable_types=0;
928  const typedata *tmptype;
929 
930  if (pl->type != PLAYER)
931  return 0; /* only players will skill-identify */
932 
934  "You look at the objects nearby with your %s skill...", skill->name);
935 
936  switch (skill->subtype) {
937  case SK_DET_CURSE:
938  success = do_skill_detect_curse(pl, skill);
939  if (success)
941  "...and discover cursed items!");
942  break;
943 
944  case SK_DET_MAGIC:
945  success = do_skill_detect_magic(pl, skill);
946  if (success)
948  "...and discover items imbued with mystic forces!");
949  break;
950 
951  default:
952  /* we will try to identify items with this skill instead */
953  for (i=0; i<=OBJECT_TYPE_MAX; i++) {
954  tmptype = get_typedata(i);
955  if (tmptype) {
956  if (skill->subtype == tmptype->identifyskill || skill->subtype == tmptype->identifyskill2) {
957  success += do_skill_ident(pl, i, skill);
958  identifiable_types++;
959  }
960  }
961  }
962  if (identifiable_types == 0) {
963  LOG(llevError, "Error: skill_ident() called with skill %d which can't identify any items\n", skill->subtype);
964  return 0;
965  }
966  break;
967  }
968  if (!success) {
970  "...and learn nothing more.");
971  }
972  return success;
973 }
974 
975 
994 int use_oratory(object *pl, int dir, object *skill) {
995  int16_t x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
996  int mflags, chance;
997  object *tmp;
998  mapstruct *m;
999  char name[MAX_BUF];
1000 
1001  if (pl->type != PLAYER)
1002  return 0; /* only players use this skill */
1003  m = pl->map;
1004  mflags = get_map_flags(m, &m, x, y, &x, &y);
1005  if (mflags&P_OUT_OF_MAP)
1006  return 0;
1007 
1008  /* Save some processing - we have the flag already anyways
1009  */
1010  if (!(mflags&P_IS_ALIVE)) {
1012  "There is nothing to orate to.");
1013  return 0;
1014  }
1015 
1016  tmp = NULL;
1017  FOR_MAP_PREPARE(m, x, y, tmp2) {
1018  tmp2 = HEAD(tmp2);
1019  /* can't persuade players - return because there is nothing else
1020  * on that space to charm.
1021  */
1022  if (tmp2->type == PLAYER)
1023  return 0;
1024 
1025  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1026  if (object_value_set(tmp2, "no_mood_change"))
1027  return 0;
1028 
1029  tmp = tmp2;
1030  break;
1031  }
1032  } FOR_MAP_FINISH();
1033 
1034  if (!tmp) {
1036  "There is nothing to orate to.");
1037  return 0;
1038  }
1039 
1042  "You orate to the %s.",
1043  name);
1044 
1045  /* the following conditions limit who may be 'charmed' */
1046 
1047  /* it's hostile! */
1051  "Too bad the %s isn't listening!",
1052  name);
1053  return 0;
1054  }
1055 
1056  /* it's already allied! */
1057  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
1058  if (object_get_owner(tmp) == pl) {
1060  "Your follower loves your speech.");
1061  return 0;
1062  }
1063 
1064  if (skill->level > tmp->level) {
1065  /* you steal the follower. Perhaps we should really look at the
1066  * level of the owner above?
1067  */
1071  "You convince the %s to follow you instead!",
1072  name);
1073 
1074  FREE_AND_COPY(tmp->skill, skill->skill);
1075 
1076  /* Abuse fix - don't give exp since this can otherwise
1077  * be used by a couple players to gets lots of exp.
1078  */
1079  return 0;
1080  }
1081 
1082  /* In this case, you can't steal it from the other player */
1083  return 0;
1084  } /* Creature was already a pet of someone */
1085 
1086  chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
1087 
1088  /* Ok, got a 'sucker' lets try to make them a follower */
1089  if (chance > 0 && tmp->level < random_roll(0, chance-1, pl, PREFER_HIGH)-1) {
1090  int64_t exp;
1093  "You convince the %s to become your follower.",
1094  name);
1095 
1097  /* compute exp before setting to 0, else the monster's experience is not taken into account. */
1098  tmp->stats.exp /= 5; /* why 5? because. */
1099  exp = calc_skill_exp(pl, tmp, skill);
1100  tmp->stats.exp = 0;
1103  tmp->attack_movement = PETMOVE;
1104  /* keep oratory skill, so exp goes where it should if the pet kills something */
1105  FREE_AND_COPY(tmp->skill, skill->skill);
1106  return exp;
1107  }
1108 
1109  /* Charm failed. Creature may be angry now */
1110  if (skill->level+(pl->stats.Cha-10)/2 < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
1113  "Your speech angers the %s!",
1114  name);
1115 
1116  if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1119  tmp->attack_movement = 0; /* needed? */
1120  }
1122  }
1123 
1124  return 0; /* Fall through - if we get here, we didn't charm anything */
1125 }
1126 
1147 int singing(object *pl, int dir, object *skill) {
1148  int i, exp = 0, chance, mflags;
1149  object *tmp;
1150  mapstruct *m;
1151  int16_t x, y;
1152  char name[MAX_BUF];
1153 
1154  if (pl->type != PLAYER)
1155  return 0; /* only players use this skill */
1156 
1158  "You sing");
1159  for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
1160  x = pl->x+freearr_x[i];
1161  y = pl->y+freearr_y[i];
1162  m = pl->map;
1163 
1164  mflags = get_map_flags(m, &m, x, y, &x, &y);
1165  if (mflags&P_OUT_OF_MAP)
1166  continue;
1167  if (!(mflags&P_IS_ALIVE))
1168  continue;
1169 
1170  tmp = NULL;
1171  FOR_MAP_PREPARE(m, x, y, tmp2) {
1172  tmp2 = HEAD(tmp2);
1173  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1174  tmp = tmp2;
1175  break;
1176  }
1177  /* can't affect players */
1178  if (tmp2->type == PLAYER) {
1179  tmp = tmp2;
1180  break;
1181  }
1182  } FOR_MAP_FINISH();
1183 
1184  /* Whole bunch of checks to see if this is a type of monster that would
1185  * listen to singing.
1186  */
1187  if (tmp
1189  && !QUERY_FLAG(tmp, FLAG_NO_STEAL) /* Been charmed or abused before */
1190  && !QUERY_FLAG(tmp, FLAG_SPLITTING) /* no ears */
1191  && !QUERY_FLAG(tmp, FLAG_HITBACK) /* was here before */
1192  && (tmp->level <= skill->level)
1193  && !QUERY_FLAG(tmp, FLAG_UNDEAD)
1194  && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) /* already calm */
1195  && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) { /* already calm */
1196  /* stealing isn't really related (although, maybe it should
1197  * be). This is mainly to prevent singing to the same monster
1198  * over and over again and getting exp for it.
1199  */
1200  chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
1201 
1202  if (object_value_set(tmp, "no_mood_change"))
1203  chance = 0;
1204 
1205  if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
1209  "You calm down the %s",
1210  name);
1211 
1212  /* Give exp only if they are not aware */
1213  if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
1214  exp += calc_skill_exp(pl, tmp, skill);
1216  } else {
1219  "Too bad the %s isn't listening!",
1220  name);
1222  }
1223  }
1224  }
1225  return exp;
1226 }
1227 
1238 int find_traps(object *pl, object *skill) {
1239  int i, expsum = 0, mflags;
1240  int16_t x, y;
1241  mapstruct *m;
1242 
1243  /* First we search all around us for runes and traps, which are
1244  * all type RUNE
1245  */
1246 
1247  for (i = 0; i < 9; i++) {
1248  x = pl->x+freearr_x[i];
1249  y = pl->y+freearr_y[i];
1250  m = pl->map;
1251 
1252  mflags = get_map_flags(m, &m, x, y, &x, &y);
1253  if (mflags&P_OUT_OF_MAP)
1254  continue;
1255 
1256  /* Check everything in the square for trapness */
1257  FOR_MAP_PREPARE(m, x, y, tmp) {
1258  /* And now we'd better do an inventory traversal of each
1259  * of these objects' inventory
1260  * We can narrow this down a bit - no reason to search through
1261  * the players inventory or monsters for that matter.
1262  */
1263  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1264  FOR_INV_PREPARE(tmp, tmp2)
1265  if (tmp2->type == RUNE || tmp2->type == TRAP)
1266  if (trap_see(pl, tmp2)) {
1267  trap_show(tmp2, tmp);
1268  if (tmp2->stats.Cha > 1) {
1269  object *owner;
1270 
1271  owner = object_get_owner(tmp2);
1272  if (owner == NULL || owner->type != PLAYER)
1273  expsum += calc_skill_exp(pl, tmp2, skill);
1274 
1275  tmp2->stats.Cha = 1; /* unhide the trap */
1276  }
1277  }
1278  FOR_INV_FINISH();
1279  }
1280  if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
1281  trap_show(tmp, tmp);
1282  if (tmp->stats.Cha > 1) {
1283  object *owner;
1284 
1285  owner = object_get_owner(tmp);
1286  if (owner == NULL || owner->type != PLAYER)
1287  expsum += calc_skill_exp(pl, tmp, skill);
1288  tmp->stats.Cha = 1; /* unhide the trap */
1289  }
1290  }
1291  } FOR_MAP_FINISH();
1292  }
1294  "You search the area.");
1295  return expsum;
1296 }
1297 
1309 int remove_trap(object *op, object *skill) {
1310  int i, success = 0, mflags;
1311  mapstruct *m;
1312  int16_t x, y;
1313 
1314  for (i = 0; i < 9; i++) {
1315  x = op->x+freearr_x[i];
1316  y = op->y+freearr_y[i];
1317  m = op->map;
1318 
1319  mflags = get_map_flags(m, &m, x, y, &x, &y);
1320  if (mflags&P_OUT_OF_MAP)
1321  continue;
1322 
1323  /* Check everything in the square for trapness */
1324  FOR_MAP_PREPARE(m, x, y, tmp) {
1325  /* And now we'd better do an inventory traversal of each
1326  * of these objects inventory. Like above, only
1327  * do this for interesting objects.
1328  */
1329 
1330  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1331  FOR_INV_PREPARE(tmp, tmp2)
1332  if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
1333  object *owner;
1334 
1335  trap_show(tmp2, tmp);
1336  if (trap_disarm(op, tmp2, 1, skill) && ((owner = object_get_owner(tmp2)) == NULL || owner->type != PLAYER)) {
1337  tmp2->stats.exp = tmp2->stats.Cha*tmp2->level;
1338  success += calc_skill_exp(op, tmp2, skill);
1339  } else {
1340  /* Can't continue to disarm after failure */
1341  return success;
1342  }
1343  }
1344  FOR_INV_FINISH();
1345  }
1346  if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
1347  object *owner;
1348 
1349  trap_show(tmp, tmp);
1350  if (trap_disarm(op, tmp, 1, skill) && ((owner = object_get_owner(tmp)) == NULL || owner->type != PLAYER)) {
1351  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1352  success += calc_skill_exp(op, tmp, skill);
1353  } else {
1354  /* Can't continue to disarm after failure */
1355  return success;
1356  }
1357  }
1358  } FOR_MAP_FINISH();
1359  }
1360  return success;
1361 }
1362 
1363 
1382 int pray(object *pl, object *skill) {
1383  char buf[MAX_BUF];
1384 
1385  if (pl->type != PLAYER)
1386  return 0;
1387 
1388  snprintf(buf, sizeof(buf), "You pray.");
1389 
1390  /* Check all objects - we could stop at floor objects,
1391  * but if someone buries an altar, I don't see a problem with
1392  * going through all the objects, and it shouldn't be much slower
1393  * than extra checks on object attributes.
1394  */
1396  /* Only if the altar actually belongs to someone do you get special benefits */
1397  if (tmp->type == HOLY_ALTAR && tmp->other_arch) {
1398  snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
1399  pray_at_altar(pl, tmp, skill);
1400  break; /* Only pray at one altar */
1401  }
1402  FOR_BELOW_FINISH();
1403 
1405  buf);
1406 
1407  if (pl->stats.grace < pl->stats.maxgrace) {
1408  pl->stats.grace++;
1409  pl->last_grace = -1;
1410  }
1411  return 0;
1412 }
1413 
1430 void meditate(object *pl, object *skill) {
1431  if (pl->type != PLAYER)
1432  return; /* players only */
1433 
1434  /* check if pl has removed encumbering armour and weapons */
1435  if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && skill->level < 6) {
1437  "You can't concentrate while wielding a weapon!");
1438  return;
1439  }
1440 
1442  if (((tmp->type == ARMOUR && skill->level < 12)
1443  || (tmp->type == HELMET && skill->level < 10)
1444  || (tmp->type == SHIELD && skill->level < 6)
1445  || (tmp->type == BOOTS && skill->level < 4)
1446  || (tmp->type == GLOVES && skill->level < 2))
1447  && QUERY_FLAG(tmp, FLAG_APPLIED)) {
1449  "You can't concentrate while wearing so much armour!");
1450  return;
1451  }
1452  FOR_INV_FINISH();
1453 
1454  /* ok let's meditate! Spell points are regained first, then once
1455  * they are maxed we get back hp. Actual incrementing of values
1456  * is handled by the do_some_living() (in player.c). This way magical
1457  * bonuses for healing/sp regeneration are included properly
1458  * No matter what, we will eat up some playing time trying to
1459  * meditate. (see 'factor' variable for what sets the amount of time)
1460  */
1461 
1463  "You meditate.");
1464 
1465  if (pl->stats.sp < pl->stats.maxsp) {
1466  pl->stats.sp++;
1467  pl->last_sp = -1;
1468  } else if (pl->stats.hp < pl->stats.maxhp) {
1469  pl->stats.hp++;
1470  pl->last_heal = -1;
1471  }
1472 }
1473 
1487 static int write_note(object *pl, object *item, const char *msg) {
1488  char buf[BOOK_BUF];
1489  object *newBook = NULL;
1490 
1491  // The item should exist and be a book, but check it just in case.
1492  if (!item || item->type != BOOK) {
1494  "That was interesting...");
1495  // TODO: Print a scary log message.
1496  return 0;
1497  }
1498 
1499  // The message should never be NULL, but check it just in case.
1500  if (!msg) {
1502  "Hmm... what was I going to write?");
1503  // TODO: Print a scary log message.
1504  return 0;
1505  }
1506 
1507  // Don't let the player write a reserved keyword.
1508  if (strcasestr_local(msg, "endmsg")) {
1510  "Trying to cheat now are we?");
1511  return 0;
1512  }
1513 
1515  return strlen(msg);
1516 
1517  buf[0] = 0;
1518 
1519  // Write the message in the book if it doesn't overflow the buffer.
1520  if (!book_overflow(item->msg, msg, BOOK_BUF)) {
1521  // TODO: Garble some characters depending on intelligence/skill.
1522 
1523  // If there was already text, append the new message on the end.
1524  if (item->msg) {
1525  snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
1526  } else {
1527  snprintf(buf, sizeof(buf), "%s\n", msg);
1528  }
1529 
1530  // If there were multiple items in a stack, unstack one and write.
1531  if (item->nrof > 1) {
1532  newBook = object_new();
1533  object_copy(item, newBook);
1535  newBook->nrof = 1;
1536  object_set_msg(newBook, buf);
1537  newBook = object_insert_in_ob(newBook, pl);
1538  } else {
1540 
1541  // This shouldn't be necessary; the object hasn't changed visibly.
1542  //esrv_send_item(pl, item);
1543  }
1544 
1545  // Tell the player that he/she wrote in the object.
1549  "You write in the %s.", buf);
1550 
1551  // Give the player experience for writing.
1552  return strlen(msg);
1553  } else {
1557  "Your message won't fit in the %s!", buf);
1558  return 0;
1559  }
1560 }
1561 
1576 static int write_scroll(object *pl, object *scroll, object *skill) {
1577  int success = 0, confused = 0, grace_cost = 0;
1578  object *newscroll, *chosen_spell, *tmp;
1579 
1580  // The item should exist and be a scroll, but check it just in case.
1581  if (!scroll || scroll->type != SCROLL) {
1583  "A spell must be written on a magic scroll!");
1584  // TODO: Print a scary log message.
1585  return 0;
1586  }
1587 
1588  // The player must have a spell readied to inscribe.
1589  chosen_spell = pl->contr->ranges[range_magic];
1590  if (!chosen_spell) {
1592  "You should ready the spell you wish to inscribe.");
1593  return 0;
1594  }
1595 
1596  // Make sure the player has enough SP or grace to write the spell.
1597  grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1598  if (grace_cost > 0 && grace_cost > pl->stats.grace) {
1601  "You don't have enough grace to write a scroll of %s.",
1602  chosen_spell->name);
1603  return 0;
1604  }
1605 
1606  if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
1609  "You don't have enough mana to write a scroll of %s.",
1610  chosen_spell->name);
1611  return 0;
1612  }
1613 
1614  // Prevent players from writing spells that they are denied.
1615  if (chosen_spell->path_attuned & pl->path_denied
1617  char name[MAX_BUF];
1618 
1619  query_name(chosen_spell, name, MAX_BUF);
1622  "Just the idea of writing a scroll of %s makes you sick!",
1623  name);
1624  return 0;
1625  }
1626 
1627  // If the scroll already had a spell written on it, the player could
1628  // accidentally read it while trying to write a new one. Give the player
1629  // a 50% chance to overwrite a spell at their own level.
1630  if ((scroll->stats.sp || scroll->inv) &&
1631  random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
1633  "Oops! You accidently read it while trying to write on it.");
1634  apply_manual(pl, scroll, 0);
1635  return 0;
1636  }
1637 
1638  if (events_execute_object_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0) {
1639  return 0;
1640  }
1641 
1642  // Find out if the player is confused or not.
1643  if (QUERY_FLAG(pl, FLAG_CONFUSED)) {
1644  confused = 1;
1645  }
1646 
1647  // Mana or grace is lost no matter if the inscription is successful.
1648  pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1649  pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
1650 
1651  if (random_roll(0, chosen_spell->level * 4 - 1, pl, PREFER_LOW) <
1652  skill->level) {
1653  // If there were multiple items in a stack, unstack one.
1654  if (scroll->nrof > 1) {
1655  newscroll = object_new();
1656  object_copy(scroll, newscroll);
1658  newscroll->nrof = 1;
1659  } else {
1660  newscroll = scroll;
1661  }
1662 
1663  // Write spell if not confused; otherwise write random spell.
1664  newscroll->level = MAX(skill->level, chosen_spell->level);
1665 
1666  if (!confused) {
1667  draw_ext_info(
1669  "You succeed in writing a new scroll.");
1670  } else {
1671  chosen_spell = find_random_spell_in_ob(pl, NULL);
1672  if (!chosen_spell) {
1673  return 0;
1674  }
1675 
1676  draw_ext_info(
1678  "In your confused state, you write down some odd spell.");
1679  }
1680 
1681  if (newscroll->inv) {
1682  object *ninv;
1683  ninv = newscroll->inv;
1684  object_remove(ninv);
1686  }
1687 
1688  tmp = object_new();
1689  object_copy(chosen_spell, tmp);
1690  object_insert_in_ob(tmp, newscroll);
1691 
1692  // This is needed so casting from the scroll works correctly with
1693  // moving_ball types, which checks attunements.
1694  newscroll->path_attuned = tmp->path_repelled;
1695 
1696  // Same code as from treasure.c - so they can better merge.
1697  // If players want to sell them, so be it.
1698  newscroll->value = newscroll->arch->clone.value *
1699  newscroll->inv->value * (newscroll->level + 50 ) /
1700  (newscroll->inv->level + 50);
1701  newscroll->stats.exp = newscroll->value / 5;
1702 
1703  // Finish manipulating the scroll before inserting it.
1704  if (newscroll == scroll) {
1705  // Remove object to stack correctly with other items.
1706  object_remove(newscroll);
1707  }
1708 
1709  newscroll = object_insert_in_ob(newscroll, pl);
1710  success = calc_skill_exp(pl, newscroll, skill);
1711 
1712  if (!confused) {
1713  success *= 2;
1714  }
1715 
1716  success = success * skill->level;
1717  return success;
1718  }
1719 
1720  // Inscription wasn't successful; do something bad to the player.
1721  if (chosen_spell->level > skill->level || confused) {
1722  draw_ext_info(
1724  "Ouch! Your attempt to write a new scroll strains your mind!");
1725 
1726  // Either drain a stat or subtract experience.
1727  if (random_roll(0, 1, pl, PREFER_LOW) == 1) {
1728  drain_specific_stat(pl, 4);
1729  } else {
1730  confuse_living(pl, pl, 99);
1731  return -30 * chosen_spell->level;
1732  }
1733  } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
1734  draw_ext_info(
1736  "Your attempt to write a new scroll rattles your mind!");
1737  confuse_living(pl, pl, 99);
1738  } else {
1739  draw_ext_info(
1741  "You fail to write a new scroll.");
1742  }
1743 
1744  return 0;
1745 }
1746 
1760 int write_on_item(object *pl, const char *params, object *skill) {
1761  object *item;
1762  const char *string = params;
1763  int msgtype;
1764  archetype *skat;
1765 
1766  // Only players can use the inscription skill.
1767  if (pl->type != PLAYER) {
1768  return 0;
1769  }
1770 
1771  // No message was given, so both strings are set to empty.
1772  if (!params) {
1773  params = "";
1774  string = params;
1775  }
1776 
1777  // You must be able to read before you can write.
1779 
1780  if (!find_skill_by_name(pl, skat->clone.skill)) {
1782  "You must learn to read before you can write!");
1783  return 0;
1784  }
1785 
1786  // You must not be blind to write, unless you're a DM.
1787  if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
1789  "You are unable to write while blind.");
1790  return 0;
1791  }
1792 
1793  // If a message was given, write a book. Otherwise, write a scroll.
1794  if (string[0] != '\0') {
1795  msgtype = BOOK;
1796  } else {
1797  msgtype = SCROLL;
1798  }
1799 
1800  // Find and attempt to write on the player's marked item.
1802  if (item == NULL) {
1804  "You haven't marked any items to write on yet.");
1805  return 0;
1806  }
1807 
1808  // Don't let the player write on an unpaid item.
1809  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1811  "You had better pay for that before you write on it!");
1812  return 0;
1813  }
1814 
1815  // Check if the marked item is the type of item we're writing.
1816  if (msgtype != item->type) {
1819  "You must mark a %s to write %s.",
1820  msgtype == BOOK ? "book" : "magic scroll",
1821  msgtype == BOOK ? "your message on" : "your spell down");
1822  return 0;
1823  }
1824 
1825  if (msgtype == BOOK) {
1826  return write_note(pl, item, string);
1827  } else if (msgtype == SCROLL) {
1828  return write_scroll(pl, item, skill);
1829  } else {
1830  // This case should never be reached.
1831  abort();
1832  }
1833 }
1834 
1847 static object *find_throw_ob(object *op, sstring race) {
1848  object *tmp;
1849  char name[MAX_BUF];
1850 
1851  if (!op) { /* safety */
1852  LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1853  return (object *)NULL;
1854  }
1855 
1856  /* prefer marked item */
1858  if (tmp != NULL) {
1859  /* can't toss invisible or inv-locked items */
1860  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1861  tmp = NULL;
1862  }
1863  }
1864 
1865  /* look through the inventory if no marked found */
1866  if (tmp == NULL) {
1867  FOR_INV_PREPARE(op, tmp2) {
1868  /* can't toss invisible items */
1869  if (tmp2->invisible)
1870  continue;
1871 
1872  if (tmp2->type == CONTAINER && QUERY_FLAG(tmp2, FLAG_APPLIED) && tmp2->race == race) {
1873  tmp = find_throw_ob(tmp2, race);
1874  if (tmp != NULL) {
1875  break;
1876  }
1877  }
1878 
1879  /* if not a container, then don't look if locked */
1880  if (tmp2->type == CONTAINER || QUERY_FLAG(tmp2, FLAG_INV_LOCKED))
1881  continue;
1882 
1883  if (tmp2->race == race) {
1884  tmp = tmp2;
1885  break;
1886  }
1887 
1888  } FOR_INV_FINISH();
1889  }
1890 
1891  /* this should prevent us from throwing away
1892  * cursed items, worn armour, etc. Only weapons
1893  * can be thrown from 'hand'.
1894  */
1895  if (!tmp)
1896  return NULL;
1897 
1898  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1899  if (tmp->type != WEAPON) {
1902  "You can't throw %s.",
1903  name);
1904  tmp = NULL;
1905  } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
1909  "The %s sticks to your hand!",
1910  name);
1911  tmp = NULL;
1912  } else {
1914  LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1915  tmp = NULL;
1916  }
1917  }
1918  } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1921  "You should pay for the %s first.",
1922  name);
1923  tmp = NULL;
1924  }
1925 
1926  if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1927  LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
1928  tmp = NULL;
1929  }
1930  return tmp;
1931 }
1932 
1944 static object *make_throw_ob(object *orig) {
1945  object *toss_item;
1946 
1947  if (!orig)
1948  return NULL;
1949 
1950  toss_item = object_new();
1951  if (QUERY_FLAG(orig, FLAG_APPLIED)) {
1952  LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
1953  /* insufficient workaround, but better than nothing */
1954  CLEAR_FLAG(orig, FLAG_APPLIED);
1955  }
1956  object_copy(orig, toss_item);
1957  toss_item->type = THROWN_OBJ;
1958  CLEAR_FLAG(toss_item, FLAG_CHANGING);
1959  toss_item->stats.dam = 0; /* default damage */
1960  object_insert_in_ob(orig, toss_item);
1961  return toss_item;
1962 }
1963 
1964 
1985 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
1986  object *throw_ob = toss_item, *left = NULL;
1987  int eff_str = 0, str = op->stats.Str, dam = 0;
1988  int pause_f, weight_f = 0, mflags;
1989  float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1990  mapstruct *m;
1991  int16_t sx, sy;
1992  tag_t tag;
1993  char name[MAX_BUF];
1994 
1995  if (throw_ob == NULL) {
1996  if (op->type == PLAYER) {
1998  "You have nothing to throw.");
1999  }
2000  return 0;
2001  }
2002  if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
2003  if (op->type == PLAYER) {
2005  "The gods won't let you throw that.");
2006  }
2007  return 0;
2008  }
2009 
2010  tag = throw_ob->count;
2012  if (object_was_destroyed(throw_ob, tag)) {
2013  return 1;
2014  }
2015 
2016  /* Because throwing effectiveness must be reduced by the
2017  * encumbrance of the thrower and weight of the object. THus,
2018  * we use the concept of 'effective strength' as defined below.
2019  */
2020 
2021  /* if str exceeds settings.max_stat (30, eg giants), lets assign a str_factor > 1 */
2022  if (str > settings.max_stat) {
2023  str_factor = (float)str/(float)settings.max_stat;
2024  str = settings.max_stat;
2025  }
2026 
2027  /* the more we carry, the less we can throw. Limit only on players */
2028  if (op->type == PLAYER
2029  && op->carrying > (get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)
2030  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
2045  load_factor = 2.0f - (float)(op->carrying) / (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT);
2046  // Only clip to 1.0 if we get in here, since it is 1.0 if we do not get in here.
2047  load_factor = MIN(load_factor, 1.0f);
2048  }
2049 
2050  /* lighter items are thrown harder, farther, faster */
2051  if (throw_ob->weight > 0)
2052  item_factor = (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)/(float)(3.0*throw_ob->weight);
2053  else { /* 0 or negative weight?!? Odd object, can't throw it */
2054  query_name(throw_ob, name, MAX_BUF);
2056  "You can't throw %s.",
2057  name);
2058  return 0;
2059  }
2060 
2061  eff_str = str*load_factor;
2062  eff_str = (float)eff_str*item_factor*str_factor;
2063 
2064  /* alas, arrays limit us to a value of settings.max_stat (30). Use str_factor to
2065  * account for super-strong throwers. */
2066  if (eff_str > settings.max_stat)
2067  eff_str = settings.max_stat;
2068 
2069 #ifdef DEBUG_THROW
2070  LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
2071  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);
2072  LOG(llevDebug, " str_factor=%f\n", str_factor);
2073  LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
2074 #endif
2075 
2076  /* 3 things here prevent a throw, you aimed at your feet, you
2077  * have no effective throwing strength, or you threw at something
2078  * that flying objects can't get through.
2079  */
2080  mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
2081 
2082  if (!dir
2083  || (eff_str <= 1)
2084  || (mflags&P_OUT_OF_MAP)
2085  || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
2086  /* bounces off 'wall', and drops to feet */
2087  object_remove(throw_ob);
2088  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2089  if (op->type == PLAYER) {
2090  if (eff_str <= 1) {
2091  query_name(throw_ob, name, MAX_BUF);
2093  "Your load is so heavy you drop %s to the ground.",
2094  name);
2095  } else if (!dir) {
2096  query_name(throw_ob, name, MAX_BUF);
2098  "You throw %s at the ground.",
2099  name);
2100  } else
2102  "Something is in the way.");
2103  }
2104  return 0;
2105  } /* if object can't be thrown */
2106 
2107  left = throw_ob; /* these are throwing objects left to the player */
2108 
2109  /* sometimes object_split() can't split an object (because op->nrof==0?)
2110  * and returns NULL. We must use 'left' then
2111  */
2112  throw_ob = object_split(throw_ob, 1, NULL, 0);
2113  if (throw_ob == NULL) {
2114  throw_ob = left;
2115  object_remove(left);
2116  }
2117 
2118  /* special case: throwing powdery substances like dust, dirt */
2119  if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
2120  cast_dust(op, throw_ob, dir);
2121  return 1;
2122  }
2123 
2124  /* Make a thrown object -- insert real object in a 'carrier' object.
2125  * If unsuccessfull at making the "thrown_obj", we just reinsert
2126  * the original object back into inventory and exit
2127  */
2128  toss_item = make_throw_ob(throw_ob);
2129  if (toss_item) {
2130  throw_ob = toss_item;
2131  if (throw_ob->skill)
2132  free_string(throw_ob->skill);
2133  throw_ob->skill = add_string(skill->skill);
2134  } else {
2135  object_insert_in_ob(throw_ob, op);
2136  return 0;
2137  }
2138 
2139  object_set_owner(throw_ob, op);
2140  /* At some point in the attack code, the actual real object (op->inv)
2141  * becomes the hitter. As such, we need to make sure that has a proper
2142  * owner value so exp goes to the right place.
2143  */
2144  object_set_owner(throw_ob->inv, op);
2145  throw_ob->direction = dir;
2146 
2147  /* If the thrown item has reflecting flag, it can ricochet off walls.
2148  * Make sure this gets copied to the thrown object.
2149  * The common projectile code should then handle the reflection from there.
2150  */
2151  if (QUERY_FLAG(throw_ob->inv, FLAG_REFLECTING))
2152  SET_FLAG(throw_ob, FLAG_REFLECTING);
2153 
2154  /* the damage bonus from the force of the throw */
2155  dam = str_factor*get_dam_bonus(eff_str);
2156 
2157  /* Now, lets adjust the properties of the thrown_ob. */
2158 
2159  /* how far to fly */
2160  throw_ob->last_sp = (eff_str*3)/5;
2161 
2162  /* speed */
2163  throw_ob->speed = (get_speed_bonus(eff_str)+1.0)/1.5;
2164  throw_ob->speed = MIN(1.0, throw_ob->speed); /* no faster than an arrow! */
2165 
2166  /* item damage. Eff_str and item weight influence damage done */
2167  weight_f = MIN(throw_ob->weight/2000, settings.max_stat);
2168  throw_ob->stats.dam += (dam/3)+get_dam_bonus(weight_f)+(throw_ob->weight/15000)-2;
2169 
2170  /* chance of breaking. Proportional to force used and weight of item */
2171  throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
2172 
2173  /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
2174  throw_ob->stats.wc = 25-get_dex_bonus(op->stats.Dex)-get_thaco_bonus(eff_str)-skill->level;
2175 
2176 
2177  /* the properties of objects which are meant to be thrown (ie dart,
2178  * throwing knife, etc) will differ from ordinary items. Lets tailor
2179  * this stuff in here.
2180  */
2181 
2182  if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
2183  throw_ob->last_sp += eff_str/3; /* fly a little further */
2184  throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
2185  throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
2186  /* only throw objects get directional faces */
2187  if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
2188  object_update_turn_face(throw_ob);
2189  } else {
2190  /* some materials will adjust properties.. */
2191  if (throw_ob->material&M_LEATHER) {
2192  throw_ob->stats.dam -= 1;
2193  throw_ob->stats.food -= 10;
2194  }
2195  if (throw_ob->material&M_GLASS)
2196  throw_ob->stats.food += 60;
2197 
2198  if (throw_ob->material&M_ORGANIC) {
2199  throw_ob->stats.dam -= 3;
2200  throw_ob->stats.food += 55;
2201  }
2202  if (throw_ob->material&M_PAPER || throw_ob->material&M_CLOTH) {
2203  throw_ob->stats.dam -= 5;
2204  throw_ob->speed *= 0.8;
2205  throw_ob->stats.wc += 3;
2206  throw_ob->stats.food -= 30;
2207  }
2208  /* light obj have more wind resistance, fly slower*/
2209  if (throw_ob->weight > 500)
2210  throw_ob->speed *= 0.8;
2211  if (throw_ob->weight > 50)
2212  throw_ob->speed *= 0.5;
2213  } /* else tailor thrown object */
2214 
2215  /* some limits, and safeties (needed?) */
2216  if (throw_ob->stats.dam < 0)
2217  throw_ob->stats.dam = 0;
2218  if (throw_ob->last_sp > eff_str)
2219  throw_ob->last_sp = eff_str;
2220  if (throw_ob->stats.food < 0)
2221  throw_ob->stats.food = 0;
2222  if (throw_ob->stats.food > 100)
2223  throw_ob->stats.food = 100;
2224  if (throw_ob->stats.wc > 30)
2225  throw_ob->stats.wc = 30;
2226 
2227  /* how long to pause the thrower. Higher values mean less pause */
2228  pause_f = ((2*eff_str)/3)+20+skill->level;
2229 
2230  /* Put a lower limit on this */
2231  if (pause_f < 10)
2232  pause_f = 10;
2233  if (pause_f > 100)
2234  pause_f = 100;
2235 
2236  /* Changed in 0.94.2 - the calculation before was really goofy.
2237  * In short summary, a throw can take anywhere between speed 5 and
2238  * speed 0.5
2239  */
2240  op->speed_left -= 50.0/pause_f;
2241 
2242  object_update_speed(throw_ob);
2243  throw_ob->speed_left = 0;
2244 
2245  throw_ob->move_type = MOVE_FLY_LOW;
2246  throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
2247 
2248 #ifdef DEBUG_THROW
2249  LOG(llevDebug, " pause_f=%d \n", pause_f);
2250  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);
2251  LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
2252 #endif
2253  tag = throw_ob->count;
2254  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2255  if (!object_was_destroyed(throw_ob, tag))
2256  ob_process(throw_ob);
2257  return 1;
2258 }
2259 
2275 int skill_throw(object *op, object *part, int dir, object *skill) {
2276  object *throw_ob;
2277 
2278  if (op->type == PLAYER)
2279  throw_ob = find_throw_ob(op, find_string("throwing"));
2280  else
2281  throw_ob = monster_find_throw_ob(op);
2282 
2283  return do_throw(op, part, throw_ob, dir, skill);
2284 }
object_was_destroyed
#define object_was_destroyed(op, old_tag)
Definition: object.h:68
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
get_weight_limit
uint32_t get_weight_limit(int stat)
Definition: living.c:2362
remove_trap
int remove_trap(object *op, object *skill)
Definition: skills.c:1309
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:519
PLAYER
@ PLAYER
Definition: object.h:107
M_LEATHER
#define M_LEATHER
Definition: material.h:17
write_note
static int write_note(object *pl, object *item, const char *msg)
Definition: skills.c:1487
attempt_pick_lock
static int attempt_pick_lock(object *door, object *pl, object *skill)
Definition: skills.c:353
global.h
FREE_OBJ_NO_DESTROY_CALLBACK
#define FREE_OBJ_NO_DESTROY_CALLBACK
Definition: object.h:531
liv::dam
int16_t dam
Definition: living.h:46
object_free
void object_free(object *ob, int flags)
Definition: object.c:1578
add_string
sstring add_string(const char *str)
Definition: shstr.c:124
object_remove
void object_remove(object *op)
Definition: object.c:1819
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
remove_friendly_object
void remove_friendly_object(object *op)
Definition: friend.cpp:56
pl::transport
object * transport
Definition: player.h:213
FLAG_CONFUSED
#define FLAG_CONFUSED
Definition: define.h:311
BOW
@ BOW
Definition: object.h:118
llevError
@ llevError
Definition: logger.h:11
AP_NO_MERGE
#define AP_NO_MERGE
Definition: define.h:581
strcasestr_local
#define strcasestr_local
Definition: compat.h:28
FLAG_UNDEAD
#define FLAG_UNDEAD
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
singing
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1147
monster_can_detect_enemy
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Definition: monster.c:2569
esrv_map_scroll
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1613
GLOVES
@ GLOVES
Definition: object.h:213
object_update
void object_update(object *op, int action)
Definition: object.c:1420
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.c:46
obj::count
tag_t count
Definition: object.h:302
obj::map
struct mapdef * map
Definition: object.h:300
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:355
liv::wc
int8_t wc
Definition: living.h:37
confuse_living
void confuse_living(object *op, object *hitter, int dam)
Definition: attack.c:2266
write_on_item
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1760
make_visible
void make_visible(object *op)
Definition: player.c:3920
MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
Definition: newclient.h:574
object_new
object * object_new(void)
Definition: object.c:1255
SK_DET_MAGIC
@ SK_DET_MAGIC
Definition: skills.h:30
obj::path_attuned
uint32_t path_attuned
Definition: object.h:348
pl::socket
socket_struct socket
Definition: player.h:107
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
steal
int steal(object *op, int dir, object *skill)
Definition: skills.c:277
TRAP
@ TRAP
Definition: object.h:241
spring_trap
void spring_trap(object *trap, object *victim)
Definition: rune.c:205
ARMOUR
@ ARMOUR
Definition: object.h:120
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
WEAPON
@ WEAPON
Definition: object.h:119
typedata::identifyskill
int identifyskill
Definition: define.h:93
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:405
commongive.inv
inv
Definition: commongive.py:28
make_throw_ob
static object * make_throw_ob(object *orig)
Definition: skills.c:1944
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Definition: object.h:143
BODY_ARMS
#define BODY_ARMS
Definition: object.h:14
RUNE
@ RUNE
Definition: object.h:240
Ice.tmp
int tmp
Definition: Ice.py:207
attempt_steal
static int attempt_steal(object *op, object *who, object *skill)
Definition: skills.c:111
jump
int jump(object *pl, int dir, object *skill)
Definition: skills.c:664
skills.h
hide
int hide(object *op, object *skill)
Definition: skills.c:494
FLAG_INV_LOCKED
#define FLAG_INV_LOCKED
Definition: define.h:329
apply_manual
int apply_manual(object *op, object *tmp, int aflag)
Definition: apply.c: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
get_dex_bonus
int get_dex_bonus(int stat)
Definition: living.c:2354
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:274
FLAG_BLIND
#define FLAG_BLIND
Definition: define.h:336
isqrt
int isqrt(int n)
Definition: utils.c:569
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:415
ob_process
method_ret ob_process(object *op)
Definition: ob_methods.c:67
MAX
#define MAX(x, y)
Definition: compat.h:24
cast_dust
void cast_dust(object *op, object *throw_ob, int dir)
Definition: player.c:3891
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.c:299
obj::nrof
uint32_t nrof
Definition: object.h:337
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.c:305
FOR_BELOW_FINISH
#define FOR_BELOW_FINISH()
Definition: define.h:711
archt
Definition: object.h:469
settings
struct Settings settings
Definition: init.c:39
object_merge
object * object_merge(object *op, object *top)
Definition: object.c:2031
FOR_OB_AND_ABOVE_PREPARE
#define FOR_OB_AND_ABOVE_PREPARE(op_)
Definition: define.h:739
identify
object * identify(object *op)
Definition: item.c:1409
free_string
void free_string(sstring str)
Definition: shstr.c:280
typedata
Definition: define.h:89
remove_door
void remove_door(object *op)
Definition: time.c:38
m
static event_registration m
Definition: citylife.cpp:427
autojail.who
who
Definition: autojail.py:3
liv::exp
int64_t exp
Definition: living.h:47
NROF
static uint32_t NROF(const object *const ob)
Definition: object.h:611
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
HELMET
@ HELMET
Definition: object.h:136
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:314
monster_find_throw_ob
object * monster_find_throw_ob(object *op)
Definition: monster.c:2530
object_copy
void object_copy(const object *src_ob, object *dest_ob)
Definition: object.c:1064
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:585
POT_DUST
#define POT_DUST
Definition: spells.h:133
skill_throw
int skill_throw(object *op, object *part, int dir, object *skill)
Definition: skills.c:2275
POTION
@ POTION
Definition: object.h:111
object_update_turn_face
void object_update_turn_face(object *op)
Definition: object.c:1313
MOVE_WALK
#define MOVE_WALK
Definition: define.h:392
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
do_skill_detect_curse
static int do_skill_detect_curse(object *pl, object *skill)
Definition: skills.c:721
SK_LITERACY
@ SK_LITERACY
Definition: skills.h:27
EVENT_TRIGGER
#define EVENT_TRIGGER
Definition: events.h:33
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:658
SK_DET_CURSE
@ SK_DET_CURSE
Definition: skills.h:33
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
HEAD
#define HEAD(op)
Definition: object.h:593
range_magic
@ range_magic
Definition: player.h:32
object_can_pick
int object_can_pick(const object *who, const object *item)
Definition: object.c:3838
identify_object_with_skill
int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success)
Definition: skills.c:808
CONTAINER
@ CONTAINER
Definition: object.h:231
make_face_from_files.str
str
Definition: make_face_from_files.py:24
obj::speed_left
float speed_left
Definition: object.h:333
LOCKED_DOOR
@ LOCKED_DOOR
Definition: object.h:123
find_string
sstring find_string(const char *str)
Definition: shstr.c:236
SPELL_GRACE
#define SPELL_GRACE
Definition: spells.h:59
MSG_TYPE_ITEM
#define MSG_TYPE_ITEM
Definition: newclient.h:412
SCRIPT_FIX_ALL
#define SCRIPT_FIX_ALL
Definition: global.h:377
query_short_name
void query_short_name(const object *op, char *buf, size_t size)
Definition: item.c:510
FLAG_MAKE_INVIS
#define FLAG_MAKE_INVIS
Definition: define.h:328
navar-midane_pickup.msg
list msg
Definition: navar-midane_pickup.py:13
get_dam_bonus
int get_dam_bonus(int stat)
Definition: living.c:2378
FREE_AND_COPY
#define FREE_AND_COPY(sv, nv)
Definition: global.h:201
find_traps
int find_traps(object *pl, object *skill)
Definition: skills.c:1238
skill_ident
int skill_ident(object *pl, object *skill)
Definition: skills.c:925
obj::x
int16_t x
Definition: object.h:330
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
fix_object
void fix_object(object *op)
Definition: living.c:1126
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.c:433
book.h
is_magical
int is_magical(const object *op)
Definition: item.c:1215
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
adj_stealchance
static int adj_stealchance(object *op, object *victim, int roll)
Definition: skills.c:43
INS_BELOW_ORIGINATOR
#define INS_BELOW_ORIGINATOR
Definition: object.h:570
MSG_TYPE_SKILL_MISSING
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:587
sstring
const typedef char * sstring
Definition: global.h:40
rndm
int rndm(int min, int max)
Definition: utils.c:162
FLAG_UNAGGRESSIVE
#define FLAG_UNAGGRESSIVE
Definition: define.h:272
obj::speed
float speed
Definition: object.h:332
tag_t
uint32_t tag_t
Definition: object.h:12
obj::env
struct obj * env
Definition: object.h:296
do_skill_ident
static int do_skill_ident(object *pl, int obj_class, object *skill)
Definition: skills.c:872
sproto.h
liv::food
int32_t food
Definition: living.h:48
EVENT_THROW
#define EVENT_THROW
Definition: events.h:32
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:162
BOOK
@ BOOK
Definition: object.h:114
SP_level_spellpoint_cost
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Definition: spell_util.c:235
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:352
FLAG_SPLITTING
#define FLAG_SPLITTING
Definition: define.h:266
write_scroll
static int write_scroll(object *pl, object *scroll, object *skill)
Definition: skills.c:1576
FLAG_MONSTER
#define FLAG_MONSTER
Definition: define.h:245
object_set_owner
void object_set_owner(object *op, object *owner)
Definition: object.c:844
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c: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
use_oratory
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.c:994
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:250
MAX_BUF
#define MAX_BUF
Definition: define.h:35
M_GLASS
#define M_GLASS
Definition: material.h:16
trap_show
int trap_show(object *trap, object *where)
Definition: rune.c:403
MOVE_FLY_LOW
#define MOVE_FLY_LOW
Definition: define.h:393
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
obj::y
int16_t y
Definition: object.h:330
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:417
ob_describe
char * ob_describe(const object *op, const object *observer, int use_media_tags, char *buf, size_t size)
Definition: ob_methods.c:91
object_set_msg
void object_set_msg(object *op, const char *msg)
Definition: object.c:4781
Settings::allow_denied_spells_writing
int allow_denied_spells_writing
Definition: global.h:310
do_throw
static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill)
Definition: skills.c:1985
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:343
hideability
int hideability(object *ob)
Definition: player.c:3964
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
FLAG_FRIENDLY
#define FLAG_FRIENDLY
Definition: define.h:246
spells.h
die_roll
int die_roll(int num, int size, const object *op, int goodbad)
Definition: utils.c:122
obj::stats
living stats
Definition: object.h:373
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:590
meditate
void meditate(object *pl, object *skill)
Definition: skills.c:1430
pray
int pray(object *pl, object *skill)
Definition: skills.c:1382
obj::direction
int8_t direction
Definition: object.h:339
OBJECT_TYPE_MAX
@ OBJECT_TYPE_MAX
Definition: object.h:251
archt::clone
object clone
Definition: object.h:473
M_CLOTH
#define M_CLOTH
Definition: material.h:21
add_friendly_object
void add_friendly_object(object *op)
Definition: friend.cpp:36
obj::weight
int32_t weight
Definition: object.h:370
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
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
give.op
op
Definition: give.py:33
monster_npc_call_help
void monster_npc_call_help(object *op)
Definition: monster.c:2004
object_value_set
bool object_value_set(const object *op, const char *const key)
Definition: object.c:4347
attempt_jump
static int attempt_jump(object *pl, int dir, int spaces, object *skill)
Definition: skills.c:555
check_pick
int check_pick(object *op)
Definition: player.c:1704
esrv_update_item
void esrv_update_item(int flags, object *pl, object *op)
Definition: main.c:360
FLAG_REFLECTING
#define FLAG_REFLECTING
Definition: define.h:262
Settings::max_stat
uint8_t max_stat
Definition: global.h:319
rv_vector
Definition: map.h:373
obj::last_sp
int32_t last_sp
Definition: object.h:363
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.c:1156
buf
StringBuffer * buf
Definition: readable.c:1610
NUM_ANIMATIONS
#define NUM_ANIMATIONS(ob)
Definition: global.h:168
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2833
get_typedata
const typedata * get_typedata(int itemtype)
Definition: item.c:320
object_update_speed
void object_update_speed(object *op)
Definition: object.c:1330
M_PAPER
#define M_PAPER
Definition: material.h:14
get_speed_bonus
float get_speed_bonus(int stat)
Definition: living.c:2382
obj::subtype
uint8_t subtype
Definition: object.h:344
SCRIPT_FIX_ACTIVATOR
#define SCRIPT_FIX_ACTIVATOR
Definition: global.h:376
obj::move_type
MoveType move_type
Definition: object.h:429
pl::ranges
object * ranges[range_size]
Definition: player.h:116
typedata::identifyskill2
int identifyskill2
Definition: define.h:94
make_face_from_files.int
int
Definition: make_face_from_files.py:26
obj::skill
sstring skill
Definition: object.h:324
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.c:2080
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.c:309
detect_curse_on_item
int detect_curse_on_item(object *pl, object *tmp, object *skill)
Definition: skills.c:701
get_archetype_by_type_subtype
archetype * get_archetype_by_type_subtype(int type, int subtype)
Definition: arch.cpp:101
get_thaco_bonus
int get_thaco_bonus(int stat)
Definition: living.c:2358
trap_see
int trap_see(object *op, object *trap)
Definition: rune.c:376
DOOR
@ DOOR
Definition: object.h:126
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
OB_TYPE_MOVE_BLOCK
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:432
stop_jump
static void stop_jump(object *pl)
Definition: skills.c:534
SCROLL
@ SCROLL
Definition: object.h:221
pick_lock
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:389
PETMOVE
#define PETMOVE
Definition: define.h:501
Settings::no_player_stealing
uint8_t no_player_stealing
Definition: global.h:305
say.item
dictionary item
Definition: say.py:149
attempt_hide
static int attempt_hide(object *op, object *skill)
Definition: skills.c:463
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:589
find_throw_ob
static object * find_throw_ob(object *op, sstring race)
Definition: skills.c:1847
obj::move_on
MoveType move_on
Definition: object.h:432
AP_UNAPPLY
#define AP_UNAPPLY
Definition: define.h:575
book_overflow
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Definition: readable.c:709
stand_near_hostile
int stand_near_hostile(object *who)
Definition: player.c:4046
BOOTS
@ BOOTS
Definition: object.h:212
pl::party
partylist * party
Definition: player.h:202
FLAG_IS_CAULDRON
#define FLAG_IS_CAULDRON
Definition: define.h:338
do_skill_detect_magic
static int do_skill_detect_magic(object *pl, object *skill)
Definition: skills.c:770
FLAG_CHANGING
#define FLAG_CHANGING
Definition: define.h:263
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.c:301
SPELL
@ SPELL
Definition: object.h:214
drain_specific_stat
void drain_specific_stat(object *op, int deplete_stats)
Definition: living.c:729
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:135
FLAG_NO_SKILL_IDENT
#define FLAG_NO_SKILL_IDENT
Definition: define.h:335
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
pick_up
void pick_up(object *op, object *alt)
Definition: c_object.c:519
living.h
obj::magic
int8_t magic
Definition: object.h:353
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:146
FLAG_SLEEP
#define FLAG_SLEEP
Definition: define.h:307
object_split
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
Definition: object.c:2613
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.c:1520
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
HOLY_ALTAR
@ HOLY_ALTAR
Definition: object.h:161
can_see_monsterP
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
Definition: object.c:3793
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,...)
Definition: main.c:319
object.h
obj::level
int16_t level
Definition: object.h:356
llevDebug
@ llevDebug
Definition: logger.h:13
detect_magic_on_item
int detect_magic_on_item(object *pl, object *tmp, object *skill)
Definition: skills.c:750
obj::inv
struct obj * inv
Definition: object.h:293
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.c:1272
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.c:258
object_get_owner
object * object_get_owner(object *op)
Definition: object.c:808
is_identified
int is_identified(const object *op)
Definition: item.c:1336