Crossfire Server, Trunk  R20513
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 
16 #include "global.h"
17 
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "book.h"
22 #include "living.h"
23 #include "object.h"
24 #include "skills.h"
25 #include "spells.h"
26 #include "sproto.h"
27 
42 static int adj_stealchance(object *op, object *victim, int roll) {
43  if (!op || !victim || !roll)
44  return -1;
45 
46  /* Only prohibit stealing if the player does not have a free
47  * hand available and in fact does have hands.
48  */
49  if (op->type == PLAYER
50  && op->body_used[BODY_ARMS] <= 0
51  && op->body_info[BODY_ARMS]) {
53  "But you have no free hands to steal with!");
54  return -1;
55  }
56 
57  /* ADJUSTMENTS */
58 
59  /* Its harder to steal from hostile beings! */
60  if (!QUERY_FLAG(victim, FLAG_UNAGGRESSIVE))
61  roll = roll/2;
62 
63  /* Easier to steal from sleeping beings, or if the thief is
64  * unseen */
65  if (QUERY_FLAG(victim, FLAG_SLEEP))
66  roll = roll*3;
67  else if (op->invisible)
68  roll = roll*2;
69 
70  /* check stealing 'encumberance'. Having this equipment applied makes
71  * it quite a bit harder to steal.
72  */
73  FOR_INV_PREPARE(op, equip) {
74  if (equip->type == WEAPON && QUERY_FLAG(equip, FLAG_APPLIED)) {
75  roll -= equip->weight/10000;
76  }
77  if (equip->type == BOW && QUERY_FLAG(equip, FLAG_APPLIED))
78  roll -= equip->weight/5000;
79  if (equip->type == SHIELD && QUERY_FLAG(equip, FLAG_APPLIED)) {
80  roll -= equip->weight/2000;
81  }
82  if (equip->type == ARMOUR && QUERY_FLAG(equip, FLAG_APPLIED))
83  roll -= equip->weight/5000;
84  if (equip->type == GLOVES && QUERY_FLAG(equip, FLAG_APPLIED))
85  roll -= equip->weight/100;
86  } FOR_INV_FINISH();
87  if (roll < 0)
88  roll = 0;
89  return roll;
90 }
91 
110 static int attempt_steal(object *op, object *who, object *skill) {
111  object *success = NULL, *tmp = NULL;
112  int roll = 0, chance = 0, stats_value;
113  rv_vector rv;
114  char name[MAX_BUF];
115 
116  stats_value = ((who->stats.Dex+who->stats.Int)*3)/2;
117 
118  /* if the victim is aware of a thief in the area (FLAG_NO_STEAL set on them)
119  * they will try to prevent stealing if they can. Only unseen theives will
120  * have much chance of success.
121  */
122  if (op->type != PLAYER && QUERY_FLAG(op, FLAG_NO_STEAL)) {
123  if (monster_can_detect_enemy(op, who, &rv)) {
127  "Your attempt is prevented!");
128  return 0;
129  }
130 
131  /* help npc to detect thief next time by raising its wisdom
132  * This probably isn't the right approach - we shouldn't be
133  * changing the stats of the monsters - better approach
134  * might be to use force objects for this - MSW 2009/02/24
135  */
136  op->stats.Wis += (op->stats.Int/5)+1;
137  if (op->stats.Wis > settings.max_stat)
138  op->stats.Wis = settings.max_stat;
139  }
140  if (op->type == PLAYER && QUERY_FLAG(op, FLAG_WIZ)) {
142  "You can't steal from the dungeon master!");
143  return 0;
144  }
145  if (op->type == PLAYER && who->type == PLAYER && settings.no_player_stealing) {
147  "You can't steal from other players!");
148  return 0;
149  }
150 
151 
152  /* Ok then, go thru their inventory, stealing */
153  FOR_INV_PREPARE(op, inv) {
154  /* you can't steal worn items, starting items, wiz stuff,
155  * innate abilities, or items w/o a type. Generally
156  * speaking, the invisibility flag prevents experience or
157  * abilities from being stolen since these types are currently
158  * always invisible objects. I was implicit here so as to prevent
159  * future possible problems. -b.t.
160  * Flesh items generated w/ fix_flesh_item should have FLAG_NO_STEAL
161  * already -b.t.
162  */
163 
164  if (QUERY_FLAG(inv, FLAG_WAS_WIZ)
165  || QUERY_FLAG(inv, FLAG_APPLIED)
166  || !(inv->type)
167  || inv->type == SPELL
168  || QUERY_FLAG(inv, FLAG_STARTEQUIP)
169  || QUERY_FLAG(inv, FLAG_NO_STEAL)
170  || inv->invisible)
171  continue;
172 
173  /* Okay, try stealing this item. Dependent on dexterity of thief,
174  * skill level, see the adj_stealroll fctn for more detail.
175  */
176 
177  roll = die_roll(2, 100, who, PREFER_LOW)/2; /* weighted 1-100 */
178 
179  chance = adj_stealchance(who, op, stats_value+skill->level*10-op->level*3);
180  if (chance == -1)
181  return 0;
182  if (roll < chance) {
183  tag_t inv_count = inv->count;
184 
185  pick_up(who, inv);
186  /* need to see if the player actually stole this item -
187  * if it is in the players inv, assume it is. This prevents
188  * abuses where the player can not carry the item, so just
189  * keeps stealing it over and over.
190  */
191  if (object_was_destroyed(inv, inv_count) || inv->env != op) {
192  /* for players, play_sound: steals item */
193  success = inv;
195  }
196  tmp = inv;
197  break;
198  }
199  } FOR_INV_FINISH(); /* for loop looking for an item */
200 
201  if (!tmp) {
202  query_name(op, name, MAX_BUF);
204  "%s%s has nothing you can steal!",
205  op->type == PLAYER ? "" : "The ", name);
206  return 0;
207  }
208 
209  /* If you arent high enough level, you might get something BUT
210  * the victim will notice your stealing attempt. Ditto if you
211  * attempt to steal something heavy off them, they're bound to notice
212  */
213 
214  if (roll >= skill->level
215  || !chance
216  || (tmp && tmp->weight > 250*random_roll(0, stats_value+skill->level*10-1, who, PREFER_LOW))) {
217  /* victim figures out where the thief is! */
218  if (who->hide)
219  make_visible(who);
220 
221  if (op->type != PLAYER) {
222  /* The unaggressives look after themselves 8) */
223  if (who->type == PLAYER) {
225  query_name(op, name, MAX_BUF);
227  "%s notices your attempted pilfering!",
228  name);
229  }
231  /* all remaining npc items are guarded now. Set flag NO_STEAL
232  * on the victim.
233  */
234  SET_FLAG(op, FLAG_NO_STEAL);
235  } else { /* stealing from another player */
236  char buf[MAX_BUF];
237 
238  /* Notify the other player */
239  if (success && who->stats.Int > random_roll(0, 19, op, PREFER_LOW)) {
240  query_name(success, name, MAX_BUF);
241  snprintf(buf, sizeof(buf), "Your %s is missing!", name);
242  } else {
243  snprintf(buf, sizeof(buf), "Your pack feels strangely lighter.");
244  }
246  buf);
247  if (!success) {
248  if (who->invisible) {
249  snprintf(buf, sizeof(buf), "you feel itchy fingers getting at your pack.");
250  } else {
251  query_name(who, name, MAX_BUF);
252  snprintf(buf, sizeof(buf), "%s looks very shifty.", name);
253  }
255  buf);
256  }
257  } /* else stealing from another player */
258  /* play_sound("stop! thief!"); kindofthing */
259  } /* if you weren't 100% successful */
260  return success ? 1 : 0;
261 }
262 
263 
276 int steal(object *op, int dir, object *skill) {
277  object *tmp;
278  int16_t x, y;
279  mapstruct *m;
280  int mflags;
281 
282  x = op->x+freearr_x[dir];
283  y = op->y+freearr_y[dir];
284 
285  if (dir == 0) {
286  /* Can't steal from ourself! */
287  return 0;
288  }
289 
290  m = op->map;
291  mflags = get_map_flags(m, &m, x, y, &x, &y);
292  /* Out of map - can't do it. If nothing alive on this space,
293  * don't need to look any further.
294  */
295  if ((mflags&P_OUT_OF_MAP) || !(mflags&P_IS_ALIVE))
296  return 0;
297 
298  /* If player can't move onto the space, can't steal from it. */
299  if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y)))
300  return 0;
301 
302  /* Find the topmost object at this spot */
303  tmp = GET_MAP_OB(m, x, y);
305  if (tmp->above == NULL)
306  break;
308 
309  /* For all the stacked objects at this point, attempt a steal */
311  /* Minor hack--for multi square beings - make sure we get
312  * the 'head' coz 'tail' objects have no inventory! - b.t.
313  */
314  tmp = HEAD(tmp);
315  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER))
316  continue;
317 
318  /* do not reveal hidden DMs */
319  if (tmp->type == PLAYER && QUERY_FLAG(tmp, FLAG_WIZ) && tmp->contr->hidden)
320  continue;
321  if (attempt_steal(tmp, op, skill)) {
322  if (tmp->type == PLAYER) /* no xp for stealing from another player */
323  return 0;
324 
325  /* no xp for stealing from pets (of players) */
326  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
327  object *owner = object_get_owner(tmp);
328  if (owner != NULL && owner->type == PLAYER)
329  return 0;
330  }
331 
332  return (calc_skill_exp(op, tmp, skill));
333  }
335  return 0;
336 }
337 
352 static int attempt_pick_lock(object *door, object *pl, object *skill) {
353  int difficulty = pl->map->difficulty ? pl->map->difficulty : 0;
354  int success = 0, number; /* did we get anything? */
355 
356  /* Try to pick the lock on this item (doors only for now).
357  * Dependent on dexterity/skill SK_level of the player and
358  * the map level difficulty.
359  */
360  number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2;
361  if (number < pl->stats.Dex + skill->level*2 - difficulty ) {
362  remove_door(door);
363  success = difficulty;
364  } else if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) { /* set off any traps? */
365  spring_trap(door->inv, pl);
366  }
367  return success;
368 }
369 
370 
386 int pick_lock(object *pl, int dir, object *skill) {
387  object *tmp;
388  int x = pl->x+freearr_x[dir];
389  int y = pl->y+freearr_y[dir];
390  int difficulty=0;
391 
392  if (!dir)
393  dir = pl->facing;
394 
395  /* For all the stacked objects at this point find a door*/
396  if (out_of_map(pl->map, x, y)) {
398  "There is no lock there.");
399  return 0;
400  }
401 
402  for (tmp = GET_MAP_OB(pl->map, x, y); tmp; tmp = tmp->above)
403  if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
404  break;
405 
406  if (!tmp) {
408  "There is no lock there.");
409  return 0;
410  }
411 
412  if (execute_event(tmp, EVENT_TRIGGER, pl, skill, NULL, SCRIPT_FIX_ALL) != 0)
413  return 0;
414 
415  if (tmp->type == LOCKED_DOOR) {
417  "You can't pick that lock!");
418  return 0;
419  }
420 
421  if (!tmp->move_block) {
423  "The door has no lock!");
424  return 0;
425  }
426 
427  difficulty = attempt_pick_lock(tmp, pl, skill);
428  /* Failure */
429  if (!difficulty) {
431  "You fail to pick the lock.");
432  return 0;
433  }
434 
436  "You pick the lock.");
437  return (calc_skill_exp(pl, NULL, skill) * isqrt(difficulty));
438 }
439 
440 
460 static int attempt_hide(object *op, object *skill) {
461  int number, difficulty = op->map->difficulty;
462  int terrain = hideability(op);
463 
464  if (terrain < -10) /* not enough cover here */
465  return 0;
466 
467  /* Hiding success and duration dependant on skill level,
468  * op->stats.Dex, map difficulty and terrain.
469  */
470 
471  number = (die_roll(2, 25, op, PREFER_LOW)-2)/2;
472  if (!stand_near_hostile(op) && number < op->stats.Dex+skill->level+terrain-difficulty) {
473  op->invisible += 100; /* set the level of 'hiddeness' */
474  if (op->type == PLAYER)
475  op->contr->tmp_invis = 1;
476  op->hide = 1;
477  return 1;
478  }
479  return 0;
480 }
481 
491 int hide(object *op, object *skill) {
492  /* the preliminaries -- Can we really hide now? */
493  /* this keeps monsters from using invisibilty spells and hiding */
494 
495  if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
497  "You don't need to hide while invisible!");
498  return 0;
499  }
500 
501  if (!op->hide && op->invisible > 0 && op->type == PLAYER) {
503  "Your attempt to hide breaks the invisibility spell!");
504  make_visible(op);
505  }
506 
507  if (op->invisible > 50*skill->level) {
509  "You are as hidden as you can get.");
510  return 0;
511  }
512 
513  if (attempt_hide(op, skill)) {
515  "You hide in the shadows.");
517  return calc_skill_exp(op, NULL, skill);
518  }
520  "You fail to conceal yourself.");
521  return 0;
522 }
523 
524 
531 static void stop_jump(object *pl) {
532  fix_object(pl);
533  object_insert_in_map_at(pl, pl->map, pl, 0, pl->x, pl->y);
534 }
535 
549 static int attempt_jump(object *pl, int dir, int spaces, object *skill) {
554  if (pl->contr->transport){
555  char trans_name[MAX_BUF];
556  query_name(pl->contr->transport, trans_name, MAX_BUF);
558  "Your bounce off the walls of %s.", trans_name);
559  // We failed to jump. Return as a failure.
560  return 0;
561  }
562 
563  int i, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
564  int16_t x, y;
565  mapstruct *m;
566 
567  /* Jump loop. Go through spaces opject wants to jump. Halt the
568  * jump if a wall or creature is in the way. We set FLY_LOW
569  * temporarily to allow player to aviod exits/archs that are not
570  * move_on/off fly_low. This will also prevent pickup of objects
571  * while jumping over them.
572  */
573 
574  object_remove(pl);
575 
576  /*
577  * I don't think this is actually needed - all the movement
578  * code is handled in this function, and I don't see anyplace
579  * that cares about the move_type being flying.
580  */
581  pl->move_type |= MOVE_FLY_LOW;
582 
583  for (i = 0; i <= spaces; i++) {
584  x = pl->x+dx;
585  y = pl->y+dy;
586  m = pl->map;
587 
588  mflags = get_map_flags(m, &m, x, y, &x, &y);
589 
590  if (mflags&P_OUT_OF_MAP) {
591  (void)stop_jump(pl);
592  return 0;
593  }
594  if (OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, x, y))) {
596  "Your jump is blocked.");
597  stop_jump(pl);
598  return 0;
599  }
600 
601  FOR_MAP_PREPARE(m, x, y, tmp) {
602  tmp = HEAD(tmp);
603  /* Jump into creature */
604  if (QUERY_FLAG(tmp, FLAG_MONSTER)
605  || (tmp->type == PLAYER && (!QUERY_FLAG(tmp, FLAG_WIZ) || !tmp->contr->hidden))) {
607  "You jump into %s%s.",
608  tmp->type == PLAYER ? "" : "the ", tmp->name);
609 
610  stop_jump(pl);
611  if (tmp->type != PLAYER
612  || (pl->type == PLAYER && pl->contr->party == NULL)
613  || (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
614  skill_attack(tmp, pl, pl->facing, "kicked", skill); /* pl makes an attack */
615 
616  return 1;
617  }
618  /* If the space has fly on set (no matter what the space is),
619  * we should get the effects - after all, the player is
620  * effectively flying.
621  */
622  if (tmp->move_on&MOVE_FLY_LOW) {
623  pl->x = x;
624  pl->y = y;
625  pl->map = m;
626  if (pl->contr)
627  esrv_map_scroll(&pl->contr->socket, dx, dy);
628  stop_jump(pl);
629  return 1;
630  }
631  } FOR_MAP_FINISH();
632  pl->x = x;
633  pl->y = y;
634  pl->map = m;
635  if (pl->contr)
636  esrv_map_scroll(&pl->contr->socket, dx, dy);
637  }
638  stop_jump(pl);
639  return 1;
640 }
641 
658 int jump(object *pl, int dir, object *skill) {
659  int spaces = 0, stats;
660  int str = pl->stats.Str;
661  int dex = pl->stats.Dex;
662 
663  dex = dex ? dex : 15;
664  str = str ? str : 10;
665 
666  stats = str*str*str*dex*skill->level;
667 
668  if (pl->carrying != 0) /* don't want div by zero !! */
669  spaces = (int)(stats/pl->carrying);
670  else
671  spaces = 2; /* pl has no objects - gets the far jump */
672 
673  if (spaces > 2)
674  spaces = 2;
675  else if (spaces == 0) {
677  "You are carrying too much weight to jump.");
678  return 0;
679  }
680  return attempt_jump(pl, dir, spaces, skill);
681 }
682 
695 int detect_curse_on_item(object *pl, object *tmp, object *skill) {
697  && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
698  && tmp->item_power < skill->level) {
700  esrv_update_item(UPD_FLAGS, pl, tmp);
701  return calc_skill_exp(pl, tmp, skill);
702  }
703  return 0;
704 }
715 static int do_skill_detect_curse(object *pl, object *skill) {
716  int success = 0;
717 
718  FOR_INV_PREPARE(pl, tmp)
719  if (!tmp->invisible) success += detect_curse_on_item(pl, tmp, skill);
720  FOR_INV_FINISH();
721 
722  /* Check ground, too, but only objects the player could pick up. Cauldrons are exceptions,
723  * you definitely want to know if they are cursed */
724  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
725  if (object_can_pick(pl, tmp) || QUERY_FLAG(tmp, FLAG_IS_CAULDRON))
726  success += detect_curse_on_item(pl, tmp, skill);
727  FOR_MAP_FINISH();
728 
729  return success;
730 }
744 int detect_magic_on_item(object *pl, object *tmp, object *skill) {
746  && (is_magical(tmp)) && tmp->item_power < skill->level) {
748  esrv_update_item(UPD_FLAGS, pl, tmp);
749  return calc_skill_exp(pl, tmp, skill);
750  }
751  return 0;
752 }
753 
764 static int do_skill_detect_magic(object *pl, object *skill) {
765  int success = 0;
766 
767  FOR_INV_PREPARE(pl, tmp)
768  if (!tmp->invisible)
769  success += detect_magic_on_item(pl, tmp, skill);
770  FOR_INV_FINISH();
771 
772  /* Check ground, too, but like above, only if the object can be picked up*/
773  FOR_MAP_PREPARE(pl->map, pl->x, pl->y, tmp)
774  if (object_can_pick(pl, tmp))
775  success += detect_magic_on_item(pl, tmp, skill);
776  FOR_MAP_FINISH();
777 
778  return success;
779 }
780 
796 int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success) {
797  int success = 0, chance, ip;
798  int skill_value = skill->level*pl->stats.Int ? pl->stats.Int : 10;
799 
800  if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)
802  && need_identify(tmp)
803  && !tmp->invisible) {
804  ip = tmp->magic;
805  if (tmp->item_power > ip)
806  ip = tmp->item_power;
807 
808  chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
809  if (skill_value >= chance) {
810  tmp = identify(tmp);
811  if (pl->type == PLAYER && print_on_success) {
812  char desc[MAX_BUF];
813 
815  "You identify %s.",
816  ob_describe(tmp, pl, desc, sizeof(desc)));
817  if (tmp->msg) {
819  "The item has a story:\n%s",
820  tmp->msg);
821  }
822  }
823  success += calc_skill_exp(pl, tmp, skill);
824  } else
826  }
827  return success;
828 }
829 
842 static int do_skill_ident(object *pl, int obj_class, object *skill) {
843  int success = 0, area, i;
844 
845  /* check the player */
846  FOR_INV_PREPARE(pl, tmp)
847  if (tmp->type == obj_class)
848  success += identify_object_with_skill(tmp, pl, skill, 1);
849  FOR_INV_FINISH();
850 
851  /* check the ground */
852  /* Altered to allow ident skills to increase in area with
853  * experience. -- Aaron Baugher
854  */
855 
856  if (skill->level > 64) { /* Adjust these levels? */
857  area = 49;
858  } else if (skill->level > 16) {
859  area = 25;
860  } else if (skill->level > 4) {
861  area = 9;
862  } else {
863  area = 1;
864  }
865 
866  for (i = 0; i < area; i++) {
867  int16_t x = pl->x+freearr_x[i];
868  int16_t y = pl->y+freearr_y[i];
869  mapstruct *m = pl->map;
870  int mflags;
871 
872  mflags = get_map_flags(m, &m, x, y, &x, &y);
873  if (mflags&P_OUT_OF_MAP)
874  continue;
875 
876  if (can_see_monsterP(m, pl->x, pl->y, i)) {
877  FOR_MAP_PREPARE(m, x, y, tmp)
878  if (tmp->type == obj_class)
879  success += identify_object_with_skill(tmp, pl, skill, 1);
880  FOR_MAP_FINISH();
881  }
882  }
883  return success;
884 }
885 
895 int skill_ident(object *pl, object *skill) {
896  int success = 0;
897  int i, identifiable_types=0;
898  const typedata *tmptype;
899 
900  if (pl->type != PLAYER)
901  return 0; /* only players will skill-identify */
902 
904  "You look at the objects nearby...");
905 
906  switch (skill->subtype) {
907  case SK_DET_CURSE:
908  success = do_skill_detect_curse(pl, skill);
909  if (success)
911  "...and discover cursed items!");
912  break;
913 
914  case SK_DET_MAGIC:
915  success = do_skill_detect_magic(pl, skill);
916  if (success)
918  "...and discover items imbued with mystic forces!");
919  break;
920 
921  default:
922  /* we will try to identify items with this skill instead */
923  for (i=0; i<=OBJECT_TYPE_MAX; i++) {
924  tmptype = get_typedata(i);
925  if (tmptype) {
926  if (skill->subtype == tmptype->identifyskill || skill->subtype == tmptype->identifyskill2) {
927  success += do_skill_ident(pl, i, skill);
928  identifiable_types++;
929  }
930  }
931  }
932  if (identifiable_types == 0) {
933  LOG(llevError, "Error: skill_ident() called with skill %d which can't identify any items\n", skill->subtype);
934  return 0;
935  break;
936  }
937  break;
938  }
939  if (!success) {
941  "...and learn nothing more.");
942  }
943  return success;
944 }
945 
946 
965 int use_oratory(object *pl, int dir, object *skill) {
966  int16_t x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
967  int mflags, chance;
968  object *tmp;
969  mapstruct *m;
970  char name[MAX_BUF];
971 
972  if (pl->type != PLAYER)
973  return 0; /* only players use this skill */
974  m = pl->map;
975  mflags = get_map_flags(m, &m, x, y, &x, &y);
976  if (mflags&P_OUT_OF_MAP)
977  return 0;
978 
979  /* Save some processing - we have the flag already anyways
980  */
981  if (!(mflags&P_IS_ALIVE)) {
983  "There is nothing to orate to.");
984  return 0;
985  }
986 
987  tmp = NULL;
988  FOR_MAP_PREPARE(m, x, y, tmp2) {
989  tmp2 = HEAD(tmp2);
990  /* can't persuade players - return because there is nothing else
991  * on that space to charm.
992  */
993  if (tmp2->type == PLAYER)
994  return 0;
995 
996  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
997  const char *value = object_get_value(tmp2, "no_mood_change");
998  if (value && strcmp(value, "1") == 0)
999  return 0;
1000 
1001  tmp = tmp2;
1002  break;
1003  }
1004  } FOR_MAP_FINISH();
1005 
1006  if (!tmp) {
1008  "There is nothing to orate to.");
1009  return 0;
1010  }
1011 
1012  query_name(tmp, name, MAX_BUF);
1014  "You orate to the %s.",
1015  name);
1016 
1017  /* the following conditions limit who may be 'charmed' */
1018 
1019  /* it's hostile! */
1020  if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1021  query_name(tmp, name, MAX_BUF);
1023  "Too bad the %s isn't listening!",
1024  name);
1025  return 0;
1026  }
1027 
1028  /* it's already allied! */
1029  if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
1030  if (object_get_owner(tmp) == pl) {
1032  "Your follower loves your speech.");
1033  return 0;
1034  }
1035 
1036  if (skill->level > tmp->level) {
1037  /* you steal the follower. Perhaps we should really look at the
1038  * level of the owner above?
1039  */
1040  object_set_owner(tmp, pl);
1041  query_name(tmp, name, MAX_BUF);
1043  "You convince the %s to follow you instead!",
1044  name);
1045 
1046  FREE_AND_COPY(tmp->skill, skill->skill);
1047 
1048  /* Abuse fix - don't give exp since this can otherwise
1049  * be used by a couple players to gets lots of exp.
1050  */
1051  return 0;
1052  }
1053 
1054  /* In this case, you can't steal it from the other player */
1055  return 0;
1056  } /* Creature was already a pet of someone */
1057 
1058  chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
1059 
1060  /* Ok, got a 'sucker' lets try to make them a follower */
1061  if (chance > 0 && tmp->level < random_roll(0, chance-1, pl, PREFER_HIGH)-1) {
1062  int64_t exp;
1063  query_name(tmp, name, MAX_BUF);
1065  "You convince the %s to become your follower.",
1066  name);
1067 
1068  object_set_owner(tmp, pl);
1069  /* compute exp before setting to 0, else the monster's experience is not taken into account. */
1070  tmp->stats.exp /= 5; /* why 5? because. */
1071  exp = calc_skill_exp(pl, tmp, skill);
1072  tmp->stats.exp = 0;
1073  add_friendly_object(tmp);
1074  SET_FLAG(tmp, FLAG_FRIENDLY);
1075  tmp->attack_movement = PETMOVE;
1076  /* keep oratory skill, so exp goes where it should if the pet kills something */
1077  FREE_AND_COPY(tmp->skill, skill->skill);
1078  return exp;
1079  }
1080 
1081  /* Charm failed. Creature may be angry now */
1082  if (skill->level+(pl->stats.Cha-10)/2 < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
1083  query_name(tmp, name, MAX_BUF);
1085  "Your speech angers the %s!",
1086  name);
1087 
1088  if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
1089  CLEAR_FLAG(tmp, FLAG_FRIENDLY);
1091  tmp->attack_movement = 0; /* needed? */
1092  }
1094  }
1095 
1096  return 0; /* Fall through - if we get here, we didn't charm anything */
1097 }
1098 
1119 int singing(object *pl, int dir, object *skill) {
1120  int i, exp = 0, chance, mflags;
1121  object *tmp;
1122  mapstruct *m;
1123  int16_t x, y;
1124  char name[MAX_BUF];
1125  const char *value;
1126 
1127  if (pl->type != PLAYER)
1128  return 0; /* only players use this skill */
1129 
1131  "You sing");
1132  for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
1133  x = pl->x+freearr_x[i];
1134  y = pl->y+freearr_y[i];
1135  m = pl->map;
1136 
1137  mflags = get_map_flags(m, &m, x, y, &x, &y);
1138  if (mflags&P_OUT_OF_MAP)
1139  continue;
1140  if (!(mflags&P_IS_ALIVE))
1141  continue;
1142 
1143  tmp = NULL;
1144  FOR_MAP_PREPARE(m, x, y, tmp2) {
1145  tmp2 = HEAD(tmp2);
1146  if (QUERY_FLAG(tmp2, FLAG_MONSTER)) {
1147  tmp = tmp2;
1148  break;
1149  }
1150  /* can't affect players */
1151  if (tmp2->type == PLAYER) {
1152  tmp = tmp2;
1153  break;
1154  }
1155  } FOR_MAP_FINISH();
1156 
1157  /* Whole bunch of checks to see if this is a type of monster that would
1158  * listen to singing.
1159  */
1160  if (tmp
1161  && QUERY_FLAG(tmp, FLAG_MONSTER)
1162  && !QUERY_FLAG(tmp, FLAG_NO_STEAL) /* Been charmed or abused before */
1163  && !QUERY_FLAG(tmp, FLAG_SPLITTING) /* no ears */
1164  && !QUERY_FLAG(tmp, FLAG_HITBACK) /* was here before */
1165  && (tmp->level <= skill->level)
1166  && !QUERY_FLAG(tmp, FLAG_UNDEAD)
1167  && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) /* already calm */
1168  && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) { /* already calm */
1169  /* stealing isn't really related (although, maybe it should
1170  * be). This is mainly to prevent singing to the same monster
1171  * over and over again and getting exp for it.
1172  */
1173  chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
1174 
1175  value = object_get_value(tmp, "no_mood_change");
1176  if (value && strcmp(value, "1") == 0)
1177  chance = 0;
1178 
1179  if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
1181  query_name(tmp, name, MAX_BUF);
1183  "You calm down the %s",
1184  name);
1185 
1186  /* Give exp only if they are not aware */
1187  if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
1188  exp += calc_skill_exp(pl, tmp, skill);
1189  SET_FLAG(tmp, FLAG_NO_STEAL);
1190  } else {
1191  query_name(tmp, name, MAX_BUF);
1193  "Too bad the %s isn't listening!",
1194  name);
1195  SET_FLAG(tmp, FLAG_NO_STEAL);
1196  }
1197  }
1198  }
1199  return exp;
1200 }
1201 
1212 int find_traps(object *pl, object *skill) {
1213  int i, expsum = 0, mflags;
1214  int16_t x, y;
1215  mapstruct *m;
1216 
1217  /* First we search all around us for runes and traps, which are
1218  * all type RUNE
1219  */
1220 
1221  for (i = 0; i < 9; i++) {
1222  x = pl->x+freearr_x[i];
1223  y = pl->y+freearr_y[i];
1224  m = pl->map;
1225 
1226  mflags = get_map_flags(m, &m, x, y, &x, &y);
1227  if (mflags&P_OUT_OF_MAP)
1228  continue;
1229 
1230  /* Check everything in the square for trapness */
1231  FOR_MAP_PREPARE(m, x, y, tmp) {
1232  /* And now we'd better do an inventory traversal of each
1233  * of these objects' inventory
1234  * We can narrow this down a bit - no reason to search through
1235  * the players inventory or monsters for that matter.
1236  */
1237  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1238  FOR_INV_PREPARE(tmp, tmp2)
1239  if (tmp2->type == RUNE || tmp2->type == TRAP)
1240  if (trap_see(pl, tmp2)) {
1241  trap_show(tmp2, tmp);
1242  if (tmp2->stats.Cha > 1) {
1243  object *owner;
1244 
1245  owner = object_get_owner(tmp2);
1246  if (owner == NULL || owner->type != PLAYER)
1247  expsum += calc_skill_exp(pl, tmp2, skill);
1248 
1249  tmp2->stats.Cha = 1; /* unhide the trap */
1250  }
1251  }
1252  FOR_INV_FINISH();
1253  }
1254  if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
1255  trap_show(tmp, tmp);
1256  if (tmp->stats.Cha > 1) {
1257  object *owner;
1258 
1259  owner = object_get_owner(tmp);
1260  if (owner == NULL || owner->type != PLAYER)
1261  expsum += calc_skill_exp(pl, tmp, skill);
1262  tmp->stats.Cha = 1; /* unhide the trap */
1263  }
1264  }
1265  } FOR_MAP_FINISH();
1266  }
1268  "You search the area.");
1269  return expsum;
1270 }
1271 
1283 int remove_trap(object *op, object *skill) {
1284  int i, success = 0, mflags;
1285  mapstruct *m;
1286  int16_t x, y;
1287 
1288  for (i = 0; i < 9; i++) {
1289  x = op->x+freearr_x[i];
1290  y = op->y+freearr_y[i];
1291  m = op->map;
1292 
1293  mflags = get_map_flags(m, &m, x, y, &x, &y);
1294  if (mflags&P_OUT_OF_MAP)
1295  continue;
1296 
1297  /* Check everything in the square for trapness */
1298  FOR_MAP_PREPARE(m, x, y, tmp) {
1299  /* And now we'd better do an inventory traversal of each
1300  * of these objects inventory. Like above, only
1301  * do this for interesting objects.
1302  */
1303 
1304  if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
1305  FOR_INV_PREPARE(tmp, tmp2)
1306  if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
1307  object *owner;
1308 
1309  trap_show(tmp2, tmp);
1310  if (trap_disarm(op, tmp2, 1, skill) && ((owner = object_get_owner(tmp2)) == NULL || owner->type != PLAYER)) {
1311  tmp2->stats.exp = tmp2->stats.Cha*tmp2->level;
1312  success += calc_skill_exp(op, tmp2, skill);
1313  } else {
1314  /* Can't continue to disarm after failure */
1315  return success;
1316  }
1317  }
1318  FOR_INV_FINISH();
1319  }
1320  if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
1321  object *owner;
1322 
1323  trap_show(tmp, tmp);
1324  if (trap_disarm(op, tmp, 1, skill) && ((owner = object_get_owner(tmp)) == NULL || owner->type != PLAYER)) {
1325  tmp->stats.exp = tmp->stats.Cha*tmp->level;
1326  success += calc_skill_exp(op, tmp, skill);
1327  } else {
1328  /* Can't continue to disarm after failure */
1329  return success;
1330  }
1331  }
1332  } FOR_MAP_FINISH();
1333  }
1334  return success;
1335 }
1336 
1337 
1356 int pray(object *pl, object *skill) {
1357  char buf[MAX_BUF];
1358 
1359  if (pl->type != PLAYER)
1360  return 0;
1361 
1362  snprintf(buf, sizeof(buf), "You pray.");
1363 
1364  /* Check all objects - we could stop at floor objects,
1365  * but if someone buries an altar, I don't see a problem with
1366  * going through all the objects, and it shouldn't be much slower
1367  * than extra checks on object attributes.
1368  */
1369  FOR_BELOW_PREPARE(pl, tmp)
1370  /* Only if the altar actually belongs to someone do you get special benefits */
1371  if (tmp->type == HOLY_ALTAR && tmp->other_arch) {
1372  snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
1373  pray_at_altar(pl, tmp, skill);
1374  break; /* Only pray at one altar */
1375  }
1376  FOR_BELOW_FINISH();
1377 
1379  buf);
1380 
1381  if (pl->stats.grace < pl->stats.maxgrace) {
1382  pl->stats.grace++;
1383  pl->last_grace = -1;
1384  }
1385  return 0;
1386 }
1387 
1404 void meditate(object *pl, object *skill) {
1405  if (pl->type != PLAYER)
1406  return; /* players only */
1407 
1408  /* check if pl has removed encumbering armour and weapons */
1409  if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && skill->level < 6) {
1411  "You can't concentrate while wielding a weapon!");
1412  return;
1413  }
1414 
1415  FOR_INV_PREPARE(pl, tmp)
1416  if (((tmp->type == ARMOUR && skill->level < 12)
1417  || (tmp->type == HELMET && skill->level < 10)
1418  || (tmp->type == SHIELD && skill->level < 6)
1419  || (tmp->type == BOOTS && skill->level < 4)
1420  || (tmp->type == GLOVES && skill->level < 2))
1421  && QUERY_FLAG(tmp, FLAG_APPLIED)) {
1423  "You can't concentrate while wearing so much armour!");
1424  return;
1425  }
1426  FOR_INV_FINISH();
1427 
1428  /* ok let's meditate! Spell points are regained first, then once
1429  * they are maxed we get back hp. Actual incrementing of values
1430  * is handled by the do_some_living() (in player.c). This way magical
1431  * bonuses for healing/sp regeneration are included properly
1432  * No matter what, we will eat up some playing time trying to
1433  * meditate. (see 'factor' variable for what sets the amount of time)
1434  */
1435 
1437  "You meditate.");
1438 
1439  if (pl->stats.sp < pl->stats.maxsp) {
1440  pl->stats.sp++;
1441  pl->last_sp = -1;
1442  } else if (pl->stats.hp < pl->stats.maxhp) {
1443  pl->stats.hp++;
1444  pl->last_heal = -1;
1445  }
1446 }
1447 
1461 static int write_note(object *pl, object *item, const char *msg) {
1462  char buf[BOOK_BUF];
1463  object *newBook = NULL;
1464 
1465  // The item should exist and be a book, but check it just in case.
1466  if (!item || item->type != BOOK) {
1468  "That was interesting...");
1469  // TODO: Print a scary log message.
1470  return 0;
1471  }
1472 
1473  // The message should never be NULL, but check it just in case.
1474  if (!msg) {
1476  "Hmm... what was I going to write?");
1477  // TODO: Print a scary log message.
1478  return 0;
1479  }
1480 
1481  // Don't let the player write a reserved keyword.
1482  if (strcasestr_local(msg, "endmsg")) {
1484  "Trying to cheat now are we?");
1485  return 0;
1486  }
1487 
1488  /* Lauwenmark: Handle for plugin book writing (trigger) event */
1489  if (execute_event(item, EVENT_TRIGGER, pl, NULL, msg, SCRIPT_FIX_ALL) != 0)
1490  return strlen(msg);
1491 
1492  buf[0] = 0;
1493 
1494  // Write the message in the book if it doesn't overflow the buffer.
1495  if (!book_overflow(item->msg, msg, BOOK_BUF)) {
1496  // TODO: Garble some characters depending on intelligence/skill.
1497 
1498  // If there was already text, append the new message on the end.
1499  if (item->msg) {
1500  snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
1501  } else {
1502  snprintf(buf, sizeof(buf), "%s\n", msg);
1503  }
1504 
1505  // If there were multiple items in a stack, unstack one and write.
1506  if (item->nrof > 1) {
1507  newBook = object_new();
1508  object_copy(item, newBook);
1510  newBook->nrof = 1;
1511  object_set_msg(newBook, buf);
1512  newBook = object_insert_in_ob(newBook, pl);
1513  } else {
1514  object_set_msg(item, buf);
1515 
1516  // This shouldn't be necessary; the object hasn't changed visibly.
1517  //esrv_send_item(pl, item);
1518  }
1519 
1520  // Tell the player that he/she wrote in the object.
1521  query_short_name(item, buf, BOOK_BUF);
1524  "You write in the %s.", buf);
1525 
1526  // Give the player experience for writing.
1527  return strlen(msg);
1528  } else {
1529  query_short_name(item, buf, BOOK_BUF);
1532  "Your message won't fit in the %s!", buf);
1533  return 0;
1534  }
1535 }
1536 
1551 static int write_scroll(object *pl, object *scroll, object *skill) {
1552  int success = 0, confused = 0, grace_cost = 0;
1553  object *newscroll, *chosen_spell, *tmp;
1554 
1555  // The item should exist and be a scroll, but check it just in case.
1556  if (!scroll || scroll->type != SCROLL) {
1558  "A spell must be written on a magic scroll!");
1559  // TODO: Print a scary log message.
1560  return 0;
1561  }
1562 
1563  // The player must have a spell readied to inscribe.
1564  chosen_spell = pl->contr->ranges[range_magic];
1565  if (!chosen_spell) {
1567  "You should ready the spell you wish to inscribe.");
1568  return 0;
1569  }
1570 
1571  // Make sure the player has enough SP or grace to write the spell.
1572  grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1573  if (grace_cost > 0 && grace_cost > pl->stats.grace) {
1576  "You don't have enough grace to write a scroll of %s.",
1577  chosen_spell->name);
1578  return 0;
1579  }
1580 
1581  if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
1584  "You don't have enough mana to write a scroll of %s.",
1585  chosen_spell->name);
1586  return 0;
1587  }
1588 
1589  // Prevent players from writing spells that they are denied.
1590  if (chosen_spell->path_attuned & pl->path_denied
1592  char name[MAX_BUF];
1593 
1594  query_name(chosen_spell, name, MAX_BUF);
1597  "Just the idea of writing a scroll of %s makes you sick!",
1598  name);
1599  return 0;
1600  }
1601 
1602  // If the scroll already had a spell written on it, the player could
1603  // accidentally read it while trying to write a new one. Give the player
1604  // a 50% chance to overwrite a spell at their own level.
1605  if ((scroll->stats.sp || scroll->inv) &&
1606  random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
1608  "Oops! You accidently read it while trying to write on it.");
1609  apply_manual(pl, scroll, 0);
1610  return 0;
1611  }
1612 
1613  if (execute_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0) {
1614  return 0;
1615  }
1616 
1617  // Find out if the player is confused or not.
1618  if (QUERY_FLAG(pl, FLAG_CONFUSED)) {
1619  confused = 1;
1620  }
1621 
1622  // Mana or grace is lost no matter if the inscription is successful.
1623  pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
1624  pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
1625 
1626  if (random_roll(0, chosen_spell->level * 4 - 1, pl, PREFER_LOW) <
1627  skill->level) {
1628  // If there were multiple items in a stack, unstack one.
1629  if (scroll->nrof > 1) {
1630  newscroll = object_new();
1631  object_copy(scroll, newscroll);
1633  newscroll->nrof = 1;
1634  } else {
1635  newscroll = scroll;
1636  }
1637 
1638  // Write spell if not confused; otherwise write random spell.
1639  newscroll->level = MAX(skill->level, chosen_spell->level);
1640 
1641  if (!confused) {
1642  draw_ext_info(
1644  "You succeed in writing a new scroll.");
1645  } else {
1646  chosen_spell = find_random_spell_in_ob(pl, NULL);
1647  if (!chosen_spell) {
1648  return 0;
1649  }
1650 
1651  draw_ext_info(
1653  "In your confused state, you write down some odd spell.");
1654  }
1655 
1656  if (newscroll->inv) {
1657  object *ninv;
1658  ninv = newscroll->inv;
1659  object_remove(ninv);
1661  }
1662 
1663  tmp = object_new();
1664  object_copy(chosen_spell, tmp);
1665  object_insert_in_ob(tmp, newscroll);
1666 
1667  // This is needed so casting from the scroll works correctly with
1668  // moving_ball types, which checks attunements.
1669  newscroll->path_attuned = tmp->path_repelled;
1670 
1671  // Same code as from treasure.c - so they can better merge.
1672  // If players want to sell them, so be it.
1673  newscroll->value = newscroll->arch->clone.value *
1674  newscroll->inv->value * (newscroll->level + 50 ) /
1675  (newscroll->inv->level + 50);
1676  newscroll->stats.exp = newscroll->value / 5;
1677 
1678  // Finish manipulating the scroll before inserting it.
1679  if (newscroll == scroll) {
1680  // Remove object to stack correctly with other items.
1681  object_remove(newscroll);
1682  }
1683 
1684  newscroll = object_insert_in_ob(newscroll, pl);
1685  success = calc_skill_exp(pl, newscroll, skill);
1686 
1687  if (!confused) {
1688  success *= 2;
1689  }
1690 
1691  success = success * skill->level;
1692  return success;
1693  }
1694 
1695  // Inscription wasn't successful; do something bad to the player.
1696  if (chosen_spell->level > skill->level || confused) {
1697  draw_ext_info(
1699  "Ouch! Your attempt to write a new scroll strains your mind!");
1700 
1701  // Either drain a stat or subtract experience.
1702  if (random_roll(0, 1, pl, PREFER_LOW) == 1) {
1703  drain_specific_stat(pl, 4);
1704  } else {
1705  confuse_living(pl, pl, 99);
1706  return -30 * chosen_spell->level;
1707  }
1708  } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
1709  draw_ext_info(
1711  "Your attempt to write a new scroll rattles your mind!");
1712  confuse_living(pl, pl, 99);
1713  } else {
1714  draw_ext_info(
1716  "You fail to write a new scroll.");
1717  }
1718 
1719  return 0;
1720 }
1721 
1735 int write_on_item(object *pl, const char *params, object *skill) {
1736  object *item;
1737  const char *string = params;
1738  int msgtype;
1739  archetype *skat;
1740 
1741  // Only players can use the inscription skill.
1742  if (pl->type != PLAYER) {
1743  return 0;
1744  }
1745 
1746  // No message was given, so both strings are set to empty.
1747  if (!params) {
1748  params = "";
1749  string = params;
1750  }
1751 
1752  // You must be able to read before you can write.
1754 
1755  if (!find_skill_by_name(pl, skat->clone.skill)) {
1757  "You must learn to read before you can write!");
1758  return 0;
1759  }
1760 
1761  // You must not be blind to write, unless you're a DM.
1762  if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
1764  "You are unable to write while blind.");
1765  return 0;
1766  }
1767 
1768  // If a message was given, write a book. Otherwise, write a scroll.
1769  if (string[0] != '\0') {
1770  msgtype = BOOK;
1771  } else {
1772  msgtype = SCROLL;
1773  }
1774 
1775  // Find and attempt to write on the player's marked item.
1776  item = find_marked_object(pl);
1777  if (item == NULL) {
1779  "You haven't marked any items to write on yet.");
1780  return 0;
1781  }
1782 
1783  // Don't let the player write on an unpaid item.
1784  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1786  "You had better pay for that before you write on it!");
1787  return 0;
1788  }
1789 
1790  // Check if the marked item is the type of item we're writing.
1791  if (msgtype != item->type) {
1794  "You must mark a %s to write %s.",
1795  msgtype == BOOK ? "book" : "magic scroll",
1796  msgtype == BOOK ? "your message on" : "your spell down");
1797  return 0;
1798  }
1799 
1800  if (msgtype == BOOK) {
1801  return write_note(pl, item, string);
1802  } else if (msgtype == SCROLL) {
1803  return write_scroll(pl, item, skill);
1804  } else {
1805  // This case should never be reached.
1806  abort();
1807  }
1808 }
1809 
1822 static object *find_throw_ob(object *op, sstring race) {
1823  object *tmp;
1824  char name[MAX_BUF];
1825 
1826  if (!op) { /* safety */
1827  LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
1828  return (object *)NULL;
1829  }
1830 
1831  /* prefer marked item */
1832  tmp = find_marked_object(op);
1833  if (tmp != NULL) {
1834  /* can't toss invisible or inv-locked items */
1835  if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1836  tmp = NULL;
1837  }
1838  }
1839 
1840  /* look through the inventory if no marked found */
1841  if (tmp == NULL) {
1842  FOR_INV_PREPARE(op, tmp2) {
1843  /* can't toss invisible items */
1844  if (tmp2->invisible)
1845  continue;
1846 
1847  if (tmp2->type == CONTAINER && QUERY_FLAG(tmp2, FLAG_APPLIED) && tmp2->race == race) {
1848  tmp = find_throw_ob(tmp2, race);
1849  if (tmp != NULL) {
1850  break;
1851  }
1852  }
1853 
1854  /* if not a container, then don't look if locked */
1855  if (tmp2->type == CONTAINER || QUERY_FLAG(tmp2, FLAG_INV_LOCKED))
1856  continue;
1857 
1858  if (tmp2->race == race) {
1859  tmp = tmp2;
1860  break;
1861  }
1862 
1863  } FOR_INV_FINISH();
1864  }
1865 
1866  /* this should prevent us from throwing away
1867  * cursed items, worn armour, etc. Only weapons
1868  * can be thrown from 'hand'.
1869  */
1870  if (!tmp)
1871  return NULL;
1872 
1873  if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
1874  if (tmp->type != WEAPON) {
1875  query_name(tmp, name, MAX_BUF);
1877  "You can't throw %s.",
1878  name);
1879  tmp = NULL;
1880  } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
1881  query_name(tmp, name, MAX_BUF);
1884  "The %s sticks to your hand!",
1885  name);
1886  tmp = NULL;
1887  } else {
1888  if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) {
1889  LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
1890  tmp = NULL;
1891  }
1892  }
1893  } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1894  query_name(tmp, name, MAX_BUF);
1896  "You should pay for the %s first.",
1897  name);
1898  tmp = NULL;
1899  }
1900 
1901  if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
1902  LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
1903  tmp = NULL;
1904  }
1905  return tmp;
1906 }
1907 
1919 static object *make_throw_ob(object *orig) {
1920  object *toss_item;
1921 
1922  if (!orig)
1923  return NULL;
1924 
1925  toss_item = object_new();
1926  if (QUERY_FLAG(orig, FLAG_APPLIED)) {
1927  LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
1928  /* insufficient workaround, but better than nothing */
1929  CLEAR_FLAG(orig, FLAG_APPLIED);
1930  }
1931  object_copy(orig, toss_item);
1932  toss_item->type = THROWN_OBJ;
1933  CLEAR_FLAG(toss_item, FLAG_CHANGING);
1934  toss_item->stats.dam = 0; /* default damage */
1935  object_insert_in_ob(orig, toss_item);
1936  return toss_item;
1937 }
1938 
1939 
1960 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
1961  object *throw_ob = toss_item, *left = NULL;
1962  int eff_str = 0, str = op->stats.Str, dam = 0;
1963  int pause_f, weight_f = 0, mflags;
1964  float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
1965  mapstruct *m;
1966  int16_t sx, sy;
1967  tag_t tag;
1968  char name[MAX_BUF];
1969 
1970  if (throw_ob == NULL) {
1971  if (op->type == PLAYER) {
1973  "You have nothing to throw.");
1974  }
1975  return 0;
1976  }
1977  if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
1978  if (op->type == PLAYER) {
1980  "The gods won't let you throw that.");
1981  }
1982  return 0;
1983  }
1984 
1985  /* Lauwenmark - Now we can call the associated script_throw event (if any) */
1986  tag = throw_ob->count;
1987  execute_event(throw_ob, EVENT_THROW, op, NULL, NULL, SCRIPT_FIX_ACTIVATOR);
1988  if (object_was_destroyed(throw_ob, tag)) {
1989  return 1;
1990  }
1991 
1992  /* Because throwing effectiveness must be reduced by the
1993  * encumbrance of the thrower and weight of the object. THus,
1994  * we use the concept of 'effective strength' as defined below.
1995  */
1996 
1997  /* if str exceeds settings.max_stat (30, eg giants), lets assign a str_factor > 1 */
1998  if (str > settings.max_stat) {
1999  str_factor = (float)str/(float)settings.max_stat;
2000  str = settings.max_stat;
2001  }
2002 
2003  /* the more we carry, the less we can throw. Limit only on players */
2004  /* This logic is basically grabbed right out of fix_object() */
2005  if (op->type == PLAYER
2007  && (FREE_PLAYER_LOAD_PERCENT < 1.0)) {
2008  int extra_weight = op->carrying-get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT;
2009  load_factor = (float)extra_weight/(float)(get_weight_limit(op->stats.Str)*(1.0-FREE_PLAYER_LOAD_PERCENT));
2010  }
2011 
2012  /* lighter items are thrown harder, farther, faster */
2013  if (throw_ob->weight > 0)
2014  item_factor = (float)(get_weight_limit(op->stats.Str)*FREE_PLAYER_LOAD_PERCENT)/(float)(3.0*throw_ob->weight);
2015  else { /* 0 or negative weight?!? Odd object, can't throw it */
2016  query_name(throw_ob, name, MAX_BUF);
2018  "You can't throw %s.",
2019  name);
2020  return 0;
2021  }
2022 
2023  eff_str = str*MIN(load_factor, 1.0);
2024  eff_str = (float)eff_str*item_factor*str_factor;
2025 
2026  /* alas, arrays limit us to a value of settings.max_stat (30). Use str_factor to
2027  * account for super-strong throwers. */
2028  if (eff_str > settings.max_stat)
2029  eff_str = settings.max_stat;
2030 
2031 #ifdef DEBUG_THROW
2032  LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
2033  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);
2034  LOG(llevDebug, " str_factor=%f\n", str_factor);
2035  LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
2036 #endif
2037 
2038  /* 3 things here prevent a throw, you aimed at your feet, you
2039  * have no effective throwing strength, or you threw at something
2040  * that flying objects can't get through.
2041  */
2042  mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
2043 
2044  if (!dir
2045  || (eff_str <= 1)
2046  || (mflags&P_OUT_OF_MAP)
2047  || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
2048  /* bounces off 'wall', and drops to feet */
2049  object_remove(throw_ob);
2050  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2051  if (op->type == PLAYER) {
2052  if (eff_str <= 1) {
2053  query_name(throw_ob, name, MAX_BUF);
2055  "Your load is so heavy you drop %s to the ground.",
2056  name);
2057  } else if (!dir) {
2058  query_name(throw_ob, name, MAX_BUF);
2060  "You throw %s at the ground.",
2061  name);
2062  } else
2064  "Something is in the way.");
2065  }
2066  return 0;
2067  } /* if object can't be thrown */
2068 
2069  left = throw_ob; /* these are throwing objects left to the player */
2070 
2071  /* sometimes object_split() can't split an object (because op->nrof==0?)
2072  * and returns NULL. We must use 'left' then
2073  */
2074  throw_ob = object_split(throw_ob, 1, NULL, 0);
2075  if (throw_ob == NULL) {
2076  throw_ob = left;
2077  object_remove(left);
2078  }
2079 
2080  /* special case: throwing powdery substances like dust, dirt */
2081  if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
2082  cast_dust(op, throw_ob, dir);
2083  return 1;
2084  }
2085 
2086  /* Make a thrown object -- insert real object in a 'carrier' object.
2087  * If unsuccessfull at making the "thrown_obj", we just reinsert
2088  * the original object back into inventory and exit
2089  */
2090  toss_item = make_throw_ob(throw_ob);
2091  if (toss_item) {
2092  throw_ob = toss_item;
2093  if (throw_ob->skill)
2094  free_string(throw_ob->skill);
2095  throw_ob->skill = add_string(skill->skill);
2096  } else {
2097  object_insert_in_ob(throw_ob, op);
2098  return 0;
2099  }
2100 
2101  object_set_owner(throw_ob, op);
2102  /* At some point in the attack code, the actual real object (op->inv)
2103  * becomes the hitter. As such, we need to make sure that has a proper
2104  * owner value so exp goes to the right place.
2105  */
2106  object_set_owner(throw_ob->inv, op);
2107  throw_ob->direction = dir;
2108 
2109  /* the damage bonus from the force of the throw */
2110  dam = str_factor*get_dam_bonus(eff_str);
2111 
2112  /* Now, lets adjust the properties of the thrown_ob. */
2113 
2114  /* how far to fly */
2115  throw_ob->last_sp = (eff_str*3)/5;
2116 
2117  /* speed */
2118  throw_ob->speed = (get_speed_bonus(eff_str)+1.0)/1.5;
2119  throw_ob->speed = MIN(1.0, throw_ob->speed); /* no faster than an arrow! */
2120 
2121  /* item damage. Eff_str and item weight influence damage done */
2122  weight_f = MIN(throw_ob->weight/2000, settings.max_stat);
2123  throw_ob->stats.dam += (dam/3)+get_dam_bonus(weight_f)+(throw_ob->weight/15000)-2;
2124 
2125  /* chance of breaking. Proportional to force used and weight of item */
2126  throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
2127 
2128  /* replace 25 with a call to clone.arch wc? messes up w/ NPC */
2129  throw_ob->stats.wc = 25-get_dex_bonus(op->stats.Dex)-get_thaco_bonus(eff_str)-skill->level;
2130 
2131 
2132  /* the properties of objects which are meant to be thrown (ie dart,
2133  * throwing knife, etc) will differ from ordinary items. Lets tailor
2134  * this stuff in here.
2135  */
2136 
2137  if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
2138  throw_ob->last_sp += eff_str/3; /* fly a little further */
2139  throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
2140  throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
2141  /* only throw objects get directional faces */
2142  if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
2143  object_update_turn_face(throw_ob);
2144  } else {
2145  /* some materials will adjust properties.. */
2146  if (throw_ob->material&M_LEATHER) {
2147  throw_ob->stats.dam -= 1;
2148  throw_ob->stats.food -= 10;
2149  }
2150  if (throw_ob->material&M_GLASS)
2151  throw_ob->stats.food += 60;
2152 
2153  if (throw_ob->material&M_ORGANIC) {
2154  throw_ob->stats.dam -= 3;
2155  throw_ob->stats.food += 55;
2156  }
2157  if (throw_ob->material&M_PAPER || throw_ob->material&M_CLOTH) {
2158  throw_ob->stats.dam -= 5;
2159  throw_ob->speed *= 0.8;
2160  throw_ob->stats.wc += 3;
2161  throw_ob->stats.food -= 30;
2162  }
2163  /* light obj have more wind resistance, fly slower*/
2164  if (throw_ob->weight > 500)
2165  throw_ob->speed *= 0.8;
2166  if (throw_ob->weight > 50)
2167  throw_ob->speed *= 0.5;
2168  } /* else tailor thrown object */
2169 
2170  /* some limits, and safeties (needed?) */
2171  if (throw_ob->stats.dam < 0)
2172  throw_ob->stats.dam = 0;
2173  if (throw_ob->last_sp > eff_str)
2174  throw_ob->last_sp = eff_str;
2175  if (throw_ob->stats.food < 0)
2176  throw_ob->stats.food = 0;
2177  if (throw_ob->stats.food > 100)
2178  throw_ob->stats.food = 100;
2179  if (throw_ob->stats.wc > 30)
2180  throw_ob->stats.wc = 30;
2181 
2182  /* how long to pause the thrower. Higher values mean less pause */
2183  pause_f = ((2*eff_str)/3)+20+skill->level;
2184 
2185  /* Put a lower limit on this */
2186  if (pause_f < 10)
2187  pause_f = 10;
2188  if (pause_f > 100)
2189  pause_f = 100;
2190 
2191  /* Changed in 0.94.2 - the calculation before was really goofy.
2192  * In short summary, a throw can take anywhere between speed 5 and
2193  * speed 0.5
2194  */
2195  op->speed_left -= 50.0/pause_f;
2196 
2197  object_update_speed(throw_ob);
2198  throw_ob->speed_left = 0;
2199 
2200  throw_ob->move_type = MOVE_FLY_LOW;
2201  throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
2202 
2203 #ifdef DEBUG_THROW
2204  LOG(llevDebug, " pause_f=%d \n", pause_f);
2205  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);
2206  LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
2207 #endif
2208  tag = throw_ob->count;
2209  object_insert_in_map_at(throw_ob, part->map, op, 0, part->x, part->y);
2210  if (!object_was_destroyed(throw_ob, tag))
2211  ob_process(throw_ob);
2212  return 1;
2213 }
2214 
2230 int skill_throw(object *op, object *part, int dir, object *skill) {
2231  object *throw_ob;
2232 
2233  if (op->type == PLAYER)
2234  throw_ob = find_throw_ob(op, find_string("throwing"));
2235  else
2236  throw_ob = monster_find_throw_ob(op);
2237 
2238  return do_throw(op, part, throw_ob, dir, skill);
2239 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
#define AP_UNAPPLY
Item is to be remvoed.
Definition: define.h:611
void spring_trap(object *trap, object *victim)
This function generalizes attacks by runes/traps.
Definition: rune.c:205
int8_t Int
Definition: living.h:35
One player.
Definition: player.h:92
int apply_special(object *who, object *op, int aflags)
Apply an object.
Definition: apply.c:1082
static object * make_throw_ob(object *orig)
We construct the &#39;carrier&#39; object in which we will insert the object that is being thrown...
Definition: skills.c:1919
int apply_manual(object *op, object *tmp, int aflag)
Main apply handler.
Definition: apply.c:510
#define FLAG_DAMNED
The object is very cursed.
Definition: define.h:318
#define FLAG_UNPAID
Object hasn&#39;t been paid for yet.
Definition: define.h:236
#define MOVE_WALK
Object walks.
Definition: define.h:407
#define UP_OBJ_FACE
Only thing that changed was the face.
Definition: object.h:519
MoveType move_type
Type of movement this object uses.
Definition: object.h:424
#define MSG_TYPE_ITEM
Item related information.
Definition: newclient.h:388
Spell-related defines: spellpath, subtypes, ...
MoveType move_on
Move types affected moving on to this space.
Definition: object.h:427
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Calculates amount of experience can be gained for successful use of a skill.
Definition: skill_util.c:658
uint8_t max_stat
Maximum stat value - 255 should be sufficient.
Definition: global.h:322
This is used by get_rangevector to determine where the other creature is.
Definition: map.h:380
static int adj_stealchance(object *op, object *victim, int roll)
Computes stealing chance.
Definition: skills.c:42
#define FLAG_SLEEP
NPC is sleeping.
Definition: define.h:308
int is_magical(const object *op)
Checks whether object is magical.
Definition: item.c:1242
void remove_door(object *op)
Remove non locked doors.
Definition: time.c:37
#define FLAG_HITBACK
Object will hit back when hit.
Definition: define.h:267
#define FOR_OB_AND_ABOVE_PREPARE(op_)
Constructs a loop iterating over an object and all objects above it in the same pile.
Definition: define.h:774
uint16_t attack_movement
What kind of attack movement.
Definition: object.h:391
static int attempt_jump(object *pl, int dir, int spaces, object *skill)
Someone is trying to jump.
Definition: skills.c:549
static int do_skill_ident(object *pl, int obj_class, object *skill)
Workhorse for skill_ident() -b.t.
Definition: skills.c:842
#define SET_FLAG(xyz, p)
Definition: define.h:223
See Scroll.
Definition: object.h:221
int identifyskill
Skill used to identify this object class.
Definition: define.h:93
int get_dam_bonus(int stat)
Definition: living.c:2271
#define MSG_TYPE_ITEM_INFO
Information related to items.
Definition: newclient.h:641
uint16_t material
What materials this object consist of.
Definition: object.h:347
void cast_dust(object *op, object *throw_ob, int dir)
Handles op throwing objects of type &#39;DUST&#39;.
Definition: player.c:3912
#define FLAG_FRIENDLY
Will help players.
Definition: define.h:246
static int attempt_hide(object *op, object *skill)
Someone is trying to hide.
Definition: skills.c:460
Update if you add new types.
Definition: object.h:250
See Holy Altar.
Definition: object.h:161
#define SPELL_GRACE
Definition: spells.h:59
int32_t last_heal
Last healed.
Definition: object.h:357
static int do_skill_detect_magic(object *pl, object *skill)
Check for magic object with the &#39;detect magic&#39; skill.
Definition: skills.c:764
int16_t maxgrace
Maximum grace.
Definition: living.h:44
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.c:280
void esrv_update_item(int flags, object *pl, object *op)
Updates object *op for player *pl.
Definition: main.c:342
const char * object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.c:4246
void pick_up(object *op, object *alt)
Try to pick up an item.
Definition: c_object.c:446
object clone
An object from which to do object_copy()
Definition: object.h:470
socket_struct socket
Socket information for this player.
Definition: player.h:94
int16_t invisible
How much longer the object will be invis.
Definition: object.h:360
short freearr_x[SIZEOFFREE]
X offset when searching around a spot.
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:600
See Rune.
Definition: object.h:240
Detect curse.
Definition: skills.h:33
int16_t SP_level_spellpoint_cost(object *caster, object *spell, int flags)
Scales the spellpoint cost of a spell by it&#39;s increased effectiveness.
Definition: spell_util.c:277
See Weapon.
Definition: object.h:119
int isqrt(int n)
Compute the square root.
Definition: utils.c:585
See Helmet.
Definition: object.h:136
#define FLAG_CONFUSED
Will also be unable to cast spells.
Definition: define.h:312
int32_t last_sp
As last_heal, but for spell points.
Definition: object.h:358
#define MSG_TYPE_SKILL_MISSING
Don&#39;t have the skill.
Definition: newclient.h:582
object * ranges[range_size]
Object for each range.
Definition: player.h:103
void meditate(object *pl, object *skill)
Meditation skill handling.
Definition: skills.c:1404
int pray(object *pl, object *skill)
Praying skill handling.
Definition: skills.c:1356
uint8_t subtype
Subtype of object.
Definition: object.h:339
#define M_CLOTH
Cloth.
Definition: material.h:21
#define MSG_TYPE_VICTIM_STEAL
Someone tried to steal from the player.
Definition: newclient.h:648
uint8_t hide
The object is hidden, not invisible.
Definition: object.h:387
int64_t exp
Experience.
Definition: living.h:46
#define FREE_OBJ_NO_DESTROY_CALLBACK
Do not run the destroy callback.
Definition: object.h:533
struct obj * above
Pointer to the object stacked above this one.
Definition: object.h:288
int stand_near_hostile(object *who)
Determine if who is standing near a hostile creature.
Definition: player.c:4066
int singing(object *pl, int dir, object *skill)
Singing skill handling.
Definition: skills.c:1119
method_ret ob_process(object *op)
Processes an object, giving it the opportunity to move or react.
Definition: ob_methods.c:62
#define MSG_TYPE_SKILL_ERROR
Doing something wrong.
Definition: newclient.h:583
#define object_was_destroyed(op, old_tag)
Checks if an object still exists.
Definition: object.h:68
int trap_disarm(object *disarmer, object *trap, int risk, object *skill)
Try to disarm a trap/rune.
Definition: rune.c:432
void remove_friendly_object(object *op)
Removes the specified object from the linked list of friendly objects.
Definition: friend.c:56
uint32_t path_attuned
Paths the object is attuned to.
Definition: object.h:343
#define MAX(x, y)
Definition: compat.h:20
void object_update(object *op, int action)
object_update() updates the array which represents the map.
Definition: object.c:1239
#define MSG_TYPE_ITEM_REMOVE
Item removed from inv.
Definition: newclient.h:638
int16_t sp
Spell points.
Definition: living.h:41
uint32_t get_weight_limit(int stat)
Definition: living.c:2255
#define NDI_BLACK
Definition: newclient.h:221
Global type definitions and header inclusions.
Link an object type with skill needed to identify, and general name.
Definition: define.h:89
#define PETMOVE
If the upper four bits of attack_movement are set to this number, the monster follows a player until ...
Definition: define.h:517
uint32_t path_repelled
Paths the object is repelled from.
Definition: object.h:344
See Boots.
Definition: object.h:212
#define SCRIPT_FIX_ALL
Definition: global.h:361
uint32_t hidden
If True, player (DM) is hidden from view.
Definition: player.h:132
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.c:310
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:465
#define FOR_OB_AND_BELOW_FINISH()
Finishes FOR_OB_AND_BELOW_PREPARE().
Definition: define.h:789
int16_t maxsp
Max spell points.
Definition: living.h:42
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Hit Points.
Definition: living.h:39
int pick_lock(object *pl, int dir, object *skill)
Lock pick handling.
Definition: skills.c:386
short freearr_y[SIZEOFFREE]
Y offset when searching around a spot.
Definition: object.c:71
partylist * party
Party this player is part of.
Definition: player.h:186
#define FLAG_KNOWN_MAGICAL
The object is known to be magical.
Definition: define.h:320
const typedata * get_typedata(int itemtype)
Definition: item.c:334
#define strcasestr_local
Definition: compat.h:24
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.c:161
#define FREE_PLAYER_LOAD_PERCENT
Definition: config.h:98
#define FLAG_UNDEAD
Monster is undead.
Definition: define.h:270
void object_set_owner(object *op, object *owner)
Sets the owner and sets the skill and exp pointers to owner&#39;s current skill and experience objects...
Definition: object.c:601
int trap_show(object *trap, object *where)
Handles showing a trap/rune detonation.
Definition: rune.c:407
See Trap.
Definition: object.h:241
int16_t y
Position in the map for this object.
Definition: object.h:326
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Same as object_insert_in_map() except it handle separate coordinates and do a clean job preparing mul...
Definition: object.c:1921
int16_t maxhp
Max hit points.
Definition: living.h:40
sstring find_string(const char *str)
Searches a string in the shared strings.
Definition: shstr.c:236
void object_update_turn_face(object *op)
If an object with the IS_TURNABLE() flag needs to be turned due to the closest player being on the ot...
Definition: object.c:1109
See Shooting Weapon.
Definition: object.h:118
uint32_t path_denied
Paths the object is denied access to.
Definition: object.h:345
void confuse_living(object *op, object *hitter, int dam)
Confuse a living thing.
Definition: attack.c:2221
static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill)
Op throws any object toss_item.
Definition: skills.c:1960
See Book.
Definition: object.h:114
#define MSG_TYPE_VICTIM
Something bad is happening to the player.
Definition: newclient.h:392
object * object_new(void)
Grabs an object from the list of unused objects, makes sure it is initialised, and returns it...
Definition: object.c:1037
#define FOR_OB_AND_ABOVE_FINISH()
Finishes FOR_OB_AND_ABOVE_PREPARE().
Definition: define.h:778
int object_can_pick(const object *who, const object *item)
Finds out if an object can be picked up.
Definition: object.c:3793
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.c:2690
float speed_left
How much speed is left to spend this round.
Definition: object.h:329
#define FLAG_NO_STEAL
Item can&#39;t be stolen.
Definition: define.h:343
signed short int16_t
Definition: win32.h:160
static object * find_throw_ob(object *op, sstring race)
Find an object to throw.
Definition: skills.c:1822
int32_t weight
Attributes of the object.
Definition: object.h:365
int hide(object *op, object *skill)
Main hide handling.
Definition: skills.c:491
#define MOVE_FLY_LOW
Low flying object.
Definition: define.h:408
uint32_t tmp_invis
Will invis go away when we attack?
Definition: player.h:125
int8_t Wis
Definition: living.h:35
#define FLAG_UNAGGRESSIVE
Monster doesn&#39;t attack players.
Definition: define.h:272
void monster_npc_call_help(object *op)
A monster calls for help against its enemy.
Definition: monster.c:1767
struct mapdef * map
Pointer to the map in which this object is present.
Definition: object.h:297
#define BOOK_BUF
Maximum message buf size for books.
Definition: book.h:16
#define snprintf
Definition: win32.h:46
int remove_trap(object *op, object *skill)
This skill will disarm any previously discovered trap.
Definition: skills.c:1283
void drain_specific_stat(object *op, int deplete_stats)
Drain a specified stat from op.
Definition: living.c:724
#define MSG_TYPE_ATTRIBUTE
Changes to attributes (stats, resistances, etc)
Definition: newclient.h:380
int detect_curse_on_item(object *pl, object *tmp, object *skill)
Runs a &#39;detect curse&#39; check on a given item.
Definition: skills.c:695
static int write_note(object *pl, object *item, const char *msg)
Called by write_on_item() to inscribe a message in an ordinary book.
Definition: skills.c:1461
object * transport
Transport the player is in.
Definition: player.h:195
#define FLAG_IDENTIFIED
Player knows full info about item.
Definition: define.h:261
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:712
int write_on_item(object *pl, const char *params, object *skill)
Implement the &#39;inscription&#39; skill, which checks for the required skills and marked items before runni...
Definition: skills.c:1735
int16_t dam
How much damage this object does when hitting.
Definition: living.h:45
int die_roll(int num, int size, const object *op, int goodbad)
Roll a number of dice (2d3, 4d6).
Definition: utils.c:121
void add_friendly_object(object *op)
Add a new friendly object to the linked list of friendly objects.
Definition: friend.c:30
int32_t carrying
How much weight this object contains.
Definition: object.h:367
const char * name
The name of the object, obviously...
Definition: object.h:311
#define FLAG_CHANGING
Changes to other_arch when anim is done.
Definition: define.h:263
int steal(object *op, int dir, object *skill)
Main stealing function.
Definition: skills.c:276
#define M_GLASS
Glass.
Definition: material.h:16
int detect_magic_on_item(object *pl, object *tmp, object *skill)
Runs a &#39;detect magic&#39; check on a given item.
Definition: skills.c:744
int16_t last_grace
As last_sp, except for grace.
Definition: object.h:359
int monster_can_detect_enemy(object *op, object *enemy, rv_vector *rv)
Determine if we can &#39;detect&#39; the enemy.
Definition: monster.c:2322
int8_t direction
Means the object is moving that way.
Definition: object.h:334
uint32_t nrof
How many of the objects.
Definition: object.h:333
int8_t Cha
Definition: living.h:35
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Basic macro to see if if ob1 can not move onto a space based on the &#39;type&#39; move_block parameter Add c...
Definition: define.h:447
Detect magic.
Definition: skills.h:30
#define UPD_FLAGS
Definition: newclient.h:290
#define FLAG_IS_CAULDRON
container can make alchemical stuff
Definition: define.h:339
#define SIZEOFFREE
Definition: define.h:154
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:251
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Gets the blocking state of a square.
Definition: map.h:192
See Potion.
Definition: object.h:111
struct pl * contr
Pointer to the player which control this object.
Definition: object.h:276
int8_t item_power
Power rating of the object.
Definition: object.h:362
int trap_see(object *op, object *trap)
Should op see trap?
Definition: rune.c:380
#define MSG_TYPE_SKILL_FAILURE
Failure in using skill.
Definition: newclient.h:585
object * find_marked_object(object *op)
Return the object the player has marked with the &#39;mark&#39; command below.
Definition: c_object.c:1256
#define M_ORGANIC
General organic.
Definition: material.h:19
uint32_t tag_t
Object tag, unique during the whole game.
Definition: object.h:12
float speed
The overall speed of this object.
Definition: object.h:328
See Spell.
Definition: object.h:214
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
#define HEAD(op)
Returns the head part of an object.
Definition: object.h:594
static void stop_jump(object *pl)
End of jump.
Definition: skills.c:531
#define FLAG_WIZ
Object has special privilegies.
Definition: define.h:231
See Locked Door.
Definition: object.h:123
#define EVENT_THROW
Object is thrown.
Definition: plugin.h:73
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
char * ob_describe(const object *op, const object *observer, char *buf, size_t size)
Returns the description of an object, as seen by the given observer.
Definition: ob_methods.c:85
See Door.
Definition: object.h:126
int16_t x
Definition: object.h:326
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Core routine for use when we attack using a skills system.
Definition: skill_util.c:1240
Literacy.
Definition: skills.h:27
int find_traps(object *pl, object *skill)
Checks for traps on the spaces around the player or in certain objects.
Definition: skills.c:1212
const char * skill
Name of the skill this object uses/grants.
Definition: object.h:321
Skill-related defines, including subtypes.
void esrv_map_scroll(socket_struct *ns, int dx, int dy)
Definition: request.c:1499
int8_t wc
Weapon Class, how skilled, the lower the better.
Definition: living.h:36
See Container.
Definition: object.h:231
#define FLAG_IS_THROWN
Object is designed to be thrown.
Definition: define.h:249
static int attempt_pick_lock(object *door, object *pl, object *skill)
Attempt to pick a lock.
Definition: skills.c:352
uint16_t difficulty
What level the player should be to play here.
Definition: map.h:343
signed __int64 int64_t
Definition: win32.h:168
#define FLAG_READY_WEAPON
(Monster or Player) has a weapon readied
Definition: define.h:335
int identifyskill2
Second skill used to identify this object class.
Definition: define.h:94
#define FOR_MAP_FINISH()
Finishes FOR_MAP_PREPARE().
Definition: define.h:765
int8_t Str
Definition: living.h:35
#define FLAG_KNOWN_CURSED
The object is known to be cursed.
Definition: define.h:321
const char * sstring
Strings that should be manipulated through add_string() and free_string().
Definition: global.h:40
int need_identify(const object *op)
This function really should not exist - by default, any item not identified should need it...
Definition: item.c:1331
#define FLAG_CURSED
The object is cursed.
Definition: define.h:317
See Player.
Definition: object.h:107
#define AP_NO_MERGE
Don&#39;t try to merge object after (un)applying it.
Definition: define.h:616
See Shield.
Definition: object.h:135
int identify_object_with_skill(object *tmp, object *pl, object *skill, int print_on_success)
Helper function for do_skill_ident, so that we can loop over inventory AND objects on the ground conv...
Definition: skills.c:796
Object structure, the core of Crossfire.
int8_t body_info[NUM_BODY_LOCATIONS]
Body info as loaded from the file.
Definition: object.h:372
#define PREFER_HIGH
Definition: define.h:599
object * object_split(object *orig_ob, uint32_t nr, char *err, size_t size)
object_split(ob,nr) splits up ob into two parts.
Definition: object.c:2463
#define FLAG_BLIND
If set, object cannot see (visually)
Definition: define.h:337
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:28
void pray_at_altar(object *pl, object *altar, object *skill)
Player prays at altar.
Definition: gods.c:296
int16_t grace
Grace.
Definition: living.h:43
#define FREE_AND_COPY(sv, nv)
Release the shared string if not NULL, and make it a reference to nv.
Definition: global.h:213
object * find_random_spell_in_ob(object *ob, const char *skill)
This returns a random spell from &#39;ob&#39;.
Definition: spell_util.c:48
#define NUM_ANIMATIONS(ob)
Definition: global.h:179
Also see SKILL_TOOL (74) below.
Definition: object.h:143
#define POT_DUST
Definition: spells.h:133
archetype * get_archetype_by_type_subtype(int type, int subtype)
Retrieves an archetype by type and subtype.
Definition: arch.c:136
static int write_scroll(object *pl, object *scroll, object *skill)
Called by write_on_item() to inscribe spell scrolls that the player can cast.
Definition: skills.c:1551
object * monster_find_throw_ob(object *op)
Find an item for the monster to throw.
Definition: monster.c:2283
tag_t count
Unique object number for this object.
Definition: object.h:299
living stats
Str, Con, Dex, etc.
Definition: object.h:368
int8_t Dex
Definition: living.h:35
struct archt * arch
Pointer to archetype.
Definition: object.h:412
Only for debugging purposes.
Definition: logger.h:13
uint8_t no_player_stealing
If 1, can not steal from other players.
Definition: global.h:308
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
struct Settings settings
Server settings.
Definition: init.c:40
static int do_skill_detect_curse(object *pl, object *skill)
Check for cursed object with the &#39;detect curse&#39; skill.
Definition: skills.c:715
void object_free2(object *ob, int flags)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.c:1391
int book_overflow(const char *buf1, const char *buf2, size_t booksize)
Checks if buf1 and buf2 can be combined.
Definition: readable.c:710
#define FLAG_SPLITTING
Object splits into stats.food other objs.
Definition: define.h:266
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:235
void query_short_name(const object *op, char *buf, size_t size)
query_short_name(object) is similar to query_name(), but doesn&#39;t contain any information about object...
Definition: item.c:547
int out_of_map(mapstruct *m, int x, int y)
this returns TRUE if the coordinates (x,y) are out of map m.
Definition: map.c:2294
int execute_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix)
Definition: main.c:364
const char * msg
If this is a book/sign/magic mouth/etc.
Definition: object.h:322
int hideability(object *ob)
Look at the surrounding terrain to determine the hideability of this object.
Definition: player.c:3984
#define FLAG_MAKE_INVIS
(Item) gives invisibility when applied
Definition: define.h:329
#define FOR_OB_AND_BELOW_PREPARE(op_)
Constructs a loop iterating over an object and all objects below it in the same pile.
Definition: define.h:785
#define FLAG_STARTEQUIP
Object was given to player at start.
Definition: define.h:268
#define GET_ANIM_ID(ob)
Definition: global.h:173
sstring add_string(const char *str)
This will add &#39;str&#39; to the hash table.
Definition: shstr.c:124
object * identify(object *op)
Identifies an item.
Definition: item.c:1437
#define M_PAPER
Paper.
Definition: material.h:14
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:172
#define FLAG_MONSTER
Will attack players.
Definition: define.h:245
void object_copy(const object *src_ob, object *dest_ob)
Copy object first frees everything allocated by the second object, and then copies the contents of th...
Definition: object.c:838
#define MSG_TYPE_SKILL
Messages related to skill use.
Definition: newclient.h:383
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
This rolls up wall, blocks_magic, blocks_view, etc, all into one function that just returns a P_...
Definition: map.c:302
int use_oratory(object *pl, int dir, object *skill)
Oratory skill handling.
Definition: skills.c:965
struct obj * inv
Pointer to the first object in the inventory.
Definition: object.h:290
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
#define M_LEATHER
Leather.
Definition: material.h:17
int skill_throw(object *op, object *part, int dir, object *skill)
Throwing skill handling.
Definition: skills.c:2230
See Gloves.
Definition: object.h:213
Spells.
Definition: player.h:19
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
#define SCRIPT_FIX_ACTIVATOR
Definition: global.h:360
void make_visible(object *op)
Makes an object visible again.
Definition: player.c:3941
#define FLAG_WAS_WIZ
Player was once a wiz.
Definition: define.h:234
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Constructs a loop iterating over all objects of a map tile.
Definition: define.h:758
MoveType move_block
What movement types this blocks.
Definition: object.h:425
Structure containing object statistics.
static int attempt_steal(object *op, object *who, object *skill)
Steal objects.
Definition: skills.c:110
void object_set_msg(object *op, const char *msg)
Set the message field of an object.
Definition: object.c:4695
int can_see_monsterP(mapstruct *m, int x, int y, int dir)
Recursive routine to see if we can find a path to a certain point.
Definition: object.c:3745
#define MSG_TYPE_SKILL_SUCCESS
Successfully used skill.
Definition: newclient.h:584
int jump(object *pl, int dir, object *skill)
Jump skill handling.
Definition: skills.c:658
#define SPELL_MANA
Definition: spells.h:58
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.c:625
#define FOR_BELOW_PREPARE(op_, it_)
Constructs a loop iterating over all objects below an object.
Definition: define.h:739
int allow_denied_spells_writing
If set, players can write spells they can&#39;t cast.
Definition: global.h:313
This is a game-map.
Definition: map.h:325
#define P_IS_ALIVE
Something alive is on this space.
Definition: map.h:237
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.c:42
#define MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END
End of a good effect.
Definition: newclient.h:569
#define FLAG_NO_SKILL_IDENT
If set, item cannot be identified w/ a skill.
Definition: define.h:336
See Breastplate Armor.
Definition: object.h:120
int get_dex_bonus(int stat)
Definition: living.c:2247
int16_t level
Level of creature or object.
Definition: object.h:351
int8_t facing
Object is oriented/facing that way.
Definition: object.h:335
int skill_ident(object *pl, object *skill)
Main identification skill handling.
Definition: skills.c:895
#define FLAG_INV_LOCKED
Item will not be dropped from inventory.
Definition: define.h:330
void fix_object(object *op)
Updates all abilities given by applied objects in the inventory of the given object.
Definition: living.c:1120
Describes fundental parameters of &#39;books&#39; - objects with type==BOOK.
object * find_skill_by_name(object *who, const char *name)
This returns the skill pointer of the given name (the one that accumulates exp, has the level...
Definition: skill_util.c:213
float get_speed_bonus(int stat)
Definition: living.c:2275
void object_update_speed(object *op)
Updates the speed of an object.
Definition: object.c:1129
int32_t value
How much money it is worth (or contains)
Definition: object.h:350
object * object_get_owner(object *op)
Returns the object which this object marks as being the owner.
Definition: object.c:559
int8_t magic
Any magical bonuses to this item.
Definition: object.h:348
#define EVENT_TRIGGER
Button pushed, lever pulled, etc.
Definition: plugin.h:74
#define FOR_BELOW_FINISH()
Finishes FOR_BELOW_PREPARE().
Definition: define.h:746
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:705
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to...
Definition: object.c:1654
int8_t body_used[NUM_BODY_LOCATIONS]
Calculated value based on items equipped.
Definition: object.h:373
int32_t food
How much food in stomach.
Definition: living.h:47
int get_thaco_bonus(int stat)
Definition: living.c:2251
#define BODY_ARMS
This should be the index of the arms.
Definition: object.h:14