Crossfire Server, Trunk
skill_util.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 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, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
32 /* define the following for skills utility debugging */
33 /* #define SKILL_UTIL_DEBUG */
34 
35 #define WANT_UNARMED_SKILLS
36 
37 #include "global.h"
38 
39 #include <assert.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <math.h>
44 
45 #include "living.h"
46 #include "object.h"
47 #include "shop.h"
48 #include "skills.h"
49 #include "spells.h"
50 #include "sproto.h"
51 #include "assets.h"
52 
53 static void attack_hth(object *pl, int dir, const char *string, object *skill);
54 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill);
55 
59 const char *skill_names[MAX_SKILLS];
64 
70 
71 static int free_skill_index() {
72  int i;
73  for (i = 0; i < MAX_SKILLS; i++) {
74  if (skill_names[i] == NULL && skill_faces[i] == NULL) {
75  break;
76  }
77  }
78  return i;
79 }
80 static void do_each_skill(archetype *at) {
81  if (at->clone.type == SKILL) {
82  int i = free_skill_index();
83  if (i == MAX_SKILLS) {
84  LOG(llevError, "init_skills: too many skills, increase MAX_SKILLS and rebuild server!");
86  }
88  if (at->clone.face != NULL)
89  skill_faces[i] = at->clone.face;
90  if (at->clone.msg)
92  }
93 }
94 
99 void init_skills(void) {
100  int i;
101 
102  for (i = 0; i < MAX_SKILLS; i++) {
103  skill_names[i] = NULL;
104  skill_faces[i] = NULL;
105  skill_messages[i] = NULL;
106  }
107 
109 }
110 
116 int get_skill_client_code(const char *skill_name)
117 {
118  int index;
119  for (index = 0; index < MAX_SKILLS && skill_names[index] != NULL; index++)
120  if (strcmp(skill_names[index], skill_name) == 0)
121  return index;
122 
123  assert("invalid skill!");
124  return 0;
125 }
126 
149 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
150  if (!skill && !skill_tool)
151  return NULL;
152 
153  /* If this is a skill that can be used without a tool and no tool found, return it */
154  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
155  return skill;
156 
157  /* Player has a tool to use the skill. If not applied, apply it -
158  * if not successful, return skill if can be used. If they do have the skill tool
159  * but not the skill itself, give it to them.
160  */
161  if (skill_tool) {
162  if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
163  if (apply_special(who, skill_tool, 0)) {
164  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
165  return skill;
166  else
167  return NULL;
168  }
169  }
170  if (!skill) {
171  skill = give_skill_by_name(who, skill_tool->skill);
173  }
174  return skill;
175  }
176  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
177  return skill;
178  else
179  return NULL;
180 }
181 
211 object *find_skill_by_name(object *who, const char *name) {
212  object *skill = NULL, *skills[MAX_SKILLS], *skill_tools[MAX_SKILLS];
213  const char *skill_names[MAX_SKILLS];
214  char *ourname=NULL;
215  int num_names, highest_level_skill=0, i;
216 
217  if (!name)
218  return NULL;
219 
220  /* Simple case - no commas in past in name, so don't need to tokenize */
221  if (!strchr(name, ',')) {
222  skill_names[0] = name;
223  skill_tools[0] = NULL;
224  skills[0] = NULL;
225  num_names = 1;
226  } else {
227  /* strtok_r is destructive, so we need our own copy */
228  char *lasts;
229  ourname = strdup(name);
230 
231  if ((skill_names[0] = strtok_r(ourname, ",", &lasts)) == NULL) {
232  /* This should really never happen */
233  LOG(llevError, "find_skill_by_name: strtok_r returned null, but strchr did not?\n");
234  free(ourname);
235  return NULL;
236  } else {
237  skill_tools[0] = NULL;
238  skills[0] = NULL;
239  /* we already have the first name from the strtok_r above */
240  num_names=1;
241  while ((skill_names[num_names] = strtok_r(NULL, ",", &lasts)) != NULL) {
242  /* Clean out any leading spacing. typical string would be
243  * skill1, skill2, skill3, ...
244  */
245  while (isspace(*skill_names[num_names]))
246  skill_names[num_names]++;
247  skills[num_names] = NULL;
248  skill_tools[num_names] = NULL;
249  num_names++;
250  }
251  }
252  /* While we don't use ourname below this point, the skill_names[] points into
253  * it, so we can't free it yet.
254  */
255  }
256 
258  /* We make sure the length of the string in the object is greater
259  * in length than the passed string. Eg, if we have a skill called
260  * 'hi', we don't want to match if the user passed 'high'
261  */
262  if (tmp->type == SKILL || (tmp->type == SKILL_TOOL && !QUERY_FLAG(tmp, FLAG_UNPAID))) {
263  for (i = 0; i<num_names; i++) {
264  if (!strncasecmp(skill_names[i], tmp->skill, strlen(skill_names[i])) &&
265  strlen(tmp->skill) >= strlen(skill_names[i])) {
266  if (tmp->type == SKILL) {
267  skills[i] = tmp;
268  if (!skill || tmp->level > skill->level) {
269  skill = tmp;
270  highest_level_skill=i;
271  }
272  }
273  else {
274  /* Skill tools don't have levels, so we basically find the
275  * 'best' skill tool for this skill.
276  */
277  if (QUERY_FLAG(tmp, FLAG_APPLIED) || !skill_tools[i] ||
278  !QUERY_FLAG(skill_tools[i], FLAG_APPLIED)) {
279  skill_tools[i] = tmp;
280  }
281  }
282  /* Got a matching name - no reason to look through rest of names */
283  break;
284  }
285  }
286  }
287  } FOR_INV_FINISH();
288  free(ourname);
289  return adjust_skill_tool(who, skills[highest_level_skill], skill_tools[highest_level_skill]);
290 }
291 
314 object *find_skill_by_number(object *who, int skillno) {
315  object *skill = NULL, *skill_tool = NULL;
316 
317  if (skillno <= 0 || skillno > MAX_SKILLS) {
318  return NULL;
319  }
320 
322  if (tmp->type == SKILL && tmp->subtype == skillno)
323  skill = tmp;
324 
325  /* Try to find appropriate skilltool. If the player has one already
326  * applied, we try to keep using that one.
327  */
328  else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
330  skill_tool = tmp;
331  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
332  skill_tool = tmp;
333  }
334  } FOR_INV_FINISH();
335 
336  return adjust_skill_tool(who, skill, skill_tool);
337 }
338 
359 int change_skill(object *who, object *new_skill, int flag) {
360  rangetype old_range;
361 
362  if (who->type != PLAYER)
363  return 0;
364 
365  old_range = who->contr->shoottype;
366 
367  /* The readied skill can be a skill tool, so check on actual skill instead of object. */
368  if (new_skill && who->chosen_skill && who->chosen_skill->skill == new_skill->skill) {
369  /* optimization for changing skill to current skill */
370  if (!(flag&0x1))
371  who->contr->shoottype = range_skill;
372  return 1;
373  }
374 
375  if (who->chosen_skill)
376  apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
377 
378  /* Only goal in this case was to unapply a skill */
379  if (!new_skill)
380  return 0;
381 
382  if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
383  return 0;
384  }
385  if (flag&0x1)
386  who->contr->shoottype = old_range;
387 
388  return 1;
389 }
390 
398 void clear_skill(object *who) {
399  who->chosen_skill = NULL;
401  if (who->type == PLAYER) {
402  who->contr->ranges[range_skill] = NULL;
403  if (who->contr->shoottype == range_skill)
404  who->contr->shoottype = range_none;
405  }
406 }
407 
431 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
432  int success = 0, exp = 0;
433 
434  if (!skill)
435  return 0;
436 
437  /* The code below presumes that the skill points to the object that
438  * holds the exp, level, etc of the skill. So if this is a player
439  * go and try to find the actual real skill pointer, and if the
440  * the player doesn't have a bucket for that, create one.
441  */
442  if (skill->type != SKILL && op->type == PLAYER) {
443  object *tmp;
444 
446  if (!tmp) {
447  tmp = give_skill_by_name(op, skill->skill);
448  if (!tmp) {
449  LOG(llevError, "do_skill: asked for skill %s but couldn't find matching SKILL archetype.\n", skill->skill);
450  return 0;
451  }
452  }
453  skill = tmp;
454  }
455 
456  if (skill->anim_suffix)
458 
459  switch (skill->subtype) {
460  case SK_LEVITATION:
461  /* Not 100% sure if this will work with new movement code -
462  * the levitation skill has move_type for flying, so when
463  * equipped, that should transfer to player, when not,
464  * shouldn't.
465  */
466  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
467  CLEAR_FLAG(skill, FLAG_APPLIED);
469  "You come to earth.");
470  } else {
471  SET_FLAG(skill, FLAG_APPLIED);
473  "You rise into the air!.");
474  }
475  fix_object(op);
476  success = 1;
477  break;
478 
479  case SK_STEALING:
480  exp = success = steal(op, dir, skill);
481  break;
482 
483  case SK_LOCKPICKING:
484  exp = success = pick_lock(op, dir, skill);
485  break;
486 
487  case SK_HIDING:
488  exp = success = hide(op, skill);
489  break;
490 
491  case SK_JUMPING:
492  success = jump(op, dir, skill);
493  break;
494 
495  case SK_INSCRIPTION:
496  exp = success = write_on_item(op, string, skill);
497  break;
498 
499  case SK_MEDITATION:
500  meditate(op, skill);
501  success = 1;
502  break;
503  /* note that the following 'attack' skills gain exp through hit_player() */
504 
505  case SK_KARATE:
506  attack_hth(op, dir, "karate-chopped", skill);
507  break;
508 
509  case SK_PUNCHING:
510  attack_hth(op, dir, "punched", skill);
511  break;
512 
513  case SK_FLAME_TOUCH:
514  attack_hth(op, dir, "flamed", skill);
515  break;
516 
517  case SK_CLAWING:
518  attack_hth(op, dir, "clawed", skill);
519  break;
520 
521  case SK_WRAITH_FEED:
522  attack_hth(op, dir, "fed upon", skill);
523  break;
524 
527  (void)attack_melee_weapon(op, dir, NULL, skill);
528  break;
529 
530  case SK_FIND_TRAPS:
531  exp = success = find_traps(op, skill);
532  break;
533 
534  case SK_SINGING:
535  exp = success = singing(op, dir, skill);
536  break;
537 
538  case SK_ORATORY:
539  exp = success = use_oratory(op, dir, skill);
540  break;
541 
542  case SK_SMITHERY:
543  case SK_BOWYER:
544  case SK_JEWELER:
545  case SK_ALCHEMY:
546  case SK_THAUMATURGY:
547  case SK_LITERACY:
548  case SK_WOODSMAN:
549  if (use_alchemy(op) == 0)
550  exp = success = skill_ident(op, skill);
551  break;
552 
553  case SK_DET_MAGIC:
554  case SK_DET_CURSE:
555  exp = success = skill_ident(op, skill);
556  break;
557 
558  case SK_DISARM_TRAPS:
559  exp = success = remove_trap(op, skill);
560  break;
561 
562  case SK_THROWING:
563  success = skill_throw(op, part, dir, skill);
564  break;
565 
566  case SK_SET_TRAP:
568  "This skill is not currently implemented.");
569  break;
570 
571  case SK_USE_MAGIC_ITEM:
572  case SK_MISSILE_WEAPON:
574  "There is no special attack for this skill.");
575  break;
576 
577  case SK_PRAYING:
578  success = pray(op, skill);
579  break;
580 
581  case SK_BARGAINING:
582  success = shop_describe(op);
583  break;
584 
585  case SK_SORCERY:
586  case SK_EVOCATION:
587  case SK_PYROMANCY:
588  case SK_SUMMONING:
589  case SK_CLIMBING:
590  case SK_EARTH_MAGIC:
591  case SK_AIR_MAGIC:
592  case SK_FIRE_MAGIC:
593  case SK_WATER_MAGIC:
595  "This skill is already in effect.");
596  break;
597 
598  case SK_HARVESTING:
599  do_harvest(op, dir, skill);
600  success = 0;
601  break;
602 
603  default: {
604  char name[MAX_BUF];
605 
607  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
608  break;
609  }
610  }
611 
612  /* For players we now update the speed_left from using the skill.
613  * Monsters have no skill use time because of the random nature in
614  * which use_monster_skill is called already simulates this.
615  * If certain skills should take more/less time, that should be
616  * in the code for the skill itself.
617  */
618 
619  if (op->type == PLAYER)
620  op->speed_left -= 1.0;
621 
622  /* this is a good place to add experience for successful use of skills.
623  * Note that add_exp() will figure out player/monster experience
624  * gain problems.
625  */
626 
627  if (success && exp)
628  change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
629 
630  return success;
631 }
632 
667 int64_t calc_skill_exp(const object *who, const object *op, const object *skill) {
668  int64_t op_exp;
669  int op_lvl;
670  float base, value, lvl_mult;
671 
672  if (!skill)
673  skill = who;
674 
675  /* Oct 95 - where we have an object, I expanded our treatment
676  * to 3 cases:
677  * non-living magic obj, runes and everything else.
678  *
679  * If an object is not alive and magical we set the base exp higher to
680  * help out exp awards for skill_ident skills. Also, if
681  * an item is type RUNE, we give out exp based on stats.Cha
682  * and level (this was the old system) -b.t.
683  */
684 
685  if (!op) { /* no item/creature */
686  op_lvl = MAX(who->map->difficulty, 1);
687  op_exp = 0;
688  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
689  * for the amount of experience */
690  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
691  op_lvl = op->level;
692  } else { /* all other items/living creatures */
693  op_exp = op->stats.exp;
694  op_lvl = op->level;
695  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
696  op_lvl += 5*abs(op->magic);
697  }
698  }
699 
700  if (op_lvl < 1)
701  op_lvl = 1;
702 
703  if (who->type != PLAYER) { /* for monsters only */
704  return ((int64_t)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
705  }
706 
707  /* for players */
708  base = op_exp;
709  /* if skill really is a skill, then we can look at the skill archetype for
710  * base reward value (exp) and level multiplier factor.
711  */
712  if (skill->type == SKILL) {
713  base += skill->arch->clone.stats.exp;
714  if (settings.simple_exp) {
715  if (skill->arch->clone.level)
716  lvl_mult = (float)skill->arch->clone.level/100.0;
717  else
718  lvl_mult = 1.0; /* no adjustment */
719  } else {
720  if (skill->level)
721  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
722  else
723  lvl_mult = 1.0;
724  }
725  } else {
726  /* Don't divide by zero here! */
727  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
728  }
729 
730  /* assemble the exp total, and return value */
731 
732  value = base*lvl_mult;
733  if (value < 1)
734  value = 1; /* Always give at least 1 exp point */
735 
736 #ifdef SKILL_UTIL_DEBUG
737  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op ? op->name : "", op_lvl);
738 #endif
739  return ((int64_t)value);
740 }
741 
759 int learn_skill(object *pl, object *scroll) {
760  object *tmp;
761 
762  if (!scroll->skill) {
763  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
764  return 2;
765  }
766 
767  /* can't use find_skill_by_name because we want skills the player knows
768  * but can't use natively.
769  */
770 
771  tmp = NULL;
773  if (inv->type == SKILL && !strncasecmp(scroll->skill, inv->skill, strlen(scroll->skill))) {
774  tmp = inv;
775  break;
776  }
777  FOR_INV_FINISH();
778 
779  /* player already knows it */
781  return 0;
782 
783  /* now a random change to learn, based on player Int.
784  * give bonus based on level - otherwise stupid characters
785  * might never be able to learn anything.
786  */
787  if (random_roll(0, 99, pl, PREFER_LOW) > (get_learn_spell(pl->stats.Int)+(pl->level/5)))
788  return 2; /* failure :< */
789 
790  if (!tmp)
791  tmp = give_skill_by_name(pl, scroll->skill);
792 
793  if (!tmp) {
794  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
795  return 2;
796  }
797 
800  return 1;
801 }
802 
814 static int clipped_percent(int64_t a, int64_t b) {
815  int rv;
816 
817  if (b <= 0)
818  return 0;
819 
820  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
821 
822  if (rv < 0)
823  return 0;
824  else if (rv > 100)
825  return 100;
826 
827  return rv;
828 }
829 
830 
839 static int digits_in_long(int64_t num)
840 {
841  return num == 0 ? 1 : floor( log10( labs(num) ) ) + 1;
842 }
843 
860 void show_skills(object *op, const char *parms) {
861  const char *cp;
862  int i, num_skills_found = 0;
863  /* Need to have a pointer and use strdup for qsort to work properly */
864  char skills[MAX_SKILLS][MAX_BUF];
865  const char *search = parms;
866  bool long_format = false;
867 
868  if ( parms && strncmp(parms,"-l",2) == 0 ) {
869  long_format = true;
870  search += 2;
871  while ( *search && *search != ' ' ) ++search; /* move past other parameters */
872  while ( *search == ' ' ) ++search;
873  }
875  if (tmp->type == SKILL) {
876  if (search && strstr(tmp->name, search) == NULL)
877  continue;
878 
880  if ( long_format ) {
881  int perm_level = exp_level(tmp->total_exp * settings.permanent_exp_ratio / 100);
882  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 ")%-*sperm lvl:%3d (xp:%" FMT64 "/%" FMT64 ") ",
883  tmp->name, tmp->level,
884  tmp->stats.exp,
885  level_exp(tmp->level+1, op->expmul),
886  1+2*digits_in_long(op->stats.exp)-digits_in_long(tmp->stats.exp)-digits_in_long(level_exp(tmp->level+1, op->expmul)),
887  "",
888  perm_level,
889  tmp->total_exp * settings.permanent_exp_ratio / 100,
890  level_exp(perm_level+1, op->expmul));
891  } else {
892  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 "/%d%%)",
893  tmp->name, tmp->level,
894  tmp->stats.exp,
895  level_exp(tmp->level+1, op->expmul),
896  clipped_percent(PERM_EXP(tmp->total_exp), tmp->stats.exp));
897  }
898  } else {
899  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 ")",
900  tmp->name, tmp->level,
901  tmp->stats.exp,
902  level_exp(tmp->level+1, op->expmul));
903  }
904  /* I don't know why some characters get a bunch of skills, but
905  * it sometimes happens (maybe a leftover from buggier earlier code
906  * and those character are still about). In any case, lets handle
907  * it so it doesn't crash the server - otherwise, one character may
908  * crash the server numerous times.
909  */
910  if (num_skills_found >= MAX_SKILLS) {
912  "Your character has too many skills.\n"
913  "Something isn't right - contact the server admin");
914  break;
915  }
916  }
917  } FOR_INV_FINISH();
918 
919  if ( search && *search ) {
921  "Player skills%s: (matching '%s')", long_format ? " (long format)":"",search);
922  } else {
924  "Player skills%s:", long_format ? " (long format)":"");
925  }
926 
927  if (num_skills_found > 1)
928  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
929 
930  for (i = 0; i < num_skills_found; i++) {
932  skills[i]);
933  }
934 
935  cp = determine_god(op);
936  if (strcmp(cp, "none") == 0)
937  cp = NULL;
938 
940  "You can handle %d weapon improvements.\n"
941  "You worship %s.\n"
942  "Your equipped item power is %d out of %d\n",
943  op->level/5+5,
944  cp ? cp : "no god at current time",
945  op->contr->item_power, op->level);
946 }
947 
964 int use_skill(object *op, const char *string) {
965  object *skop;
966  size_t len;
967 
968  if (!string)
969  return 0;
970 
971  skop = NULL;
973  if (tmp->type == SKILL
975  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
976  skop = tmp;
977  break;
978  } else if (tmp->type == SKILL_TOOL
979  && !QUERY_FLAG(tmp, FLAG_UNPAID) /* Holy symbols could be used unpaid w/o this */
980  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
981  skop = tmp;
982  break;
983  }
984  } FOR_INV_FINISH();
985  if (!skop) {
987  "Unable to find skill %s",
988  string);
989  return 0;
990  }
991 
992  len = strlen(skop->skill);
993 
994  /* All this logic goes and skips over the skill name to find any
995  * options given to the skill. Its pretty simple - if there
996  * are extra parameters (as determined by string length), we
997  * want to skip over any leading spaces.
998  */
999  if (len >= strlen(string)) {
1000  string = NULL;
1001  } else {
1002  string += len;
1003  while (*string == 0x20)
1004  string++;
1005  if (strlen(string) == 0)
1006  string = NULL;
1007  }
1008 
1009 #ifdef SKILL_UTIL_DEBUG
1010  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
1011 #endif
1012 
1013  /* Change to the new skill, then execute it. */
1014  if (do_skill(op, op, skop, op->facing, string))
1015  return 1;
1016 
1017  return 0;
1018 }
1019 
1042 static object *find_best_player_hth_skill(object *op) {
1043  object *best_skill = NULL;
1044  int last_skill;
1045 
1046  if (op->contr->unarmed_skill) {
1047  /* command_unarmed_skill() already does these checks, and right
1048  * now I do not think there is any way to lose unarmed skills.
1049  * But maybe in the future there will be (polymorph?) so handle
1050  * it appropriately. MSW 2009-07-03
1051  *
1052  * Note that the error messages should only print out once when
1053  * the initial failure to switch skills happens, so the player
1054  * should not get spammed with tons of messages unless they have
1055  * no valid unarmed skill
1056  */
1057 
1058  best_skill = find_skill_by_name(op, op->contr->unarmed_skill);
1059 
1060  if (!best_skill) {
1062  "Unable to find skill %s - using default unarmed skill",
1063  op->contr->unarmed_skill);
1064  } else {
1065  size_t i;
1066 
1067  for (i = 0; i < sizeof(unarmed_skills); i++)
1068  if (best_skill->subtype == unarmed_skills[i])
1069  break;
1070  if (i < sizeof(unarmed_skills))
1071  return(best_skill);
1072  }
1073  /* If for some reason the unarmed_skill is not valid, we fall
1074  * through the processing below.
1075  */
1076  }
1077 
1078 
1079  /* Dragons are a special case - gros 25th July 2006 */
1080  /* Perhaps this special case should be removed and unarmed_skill
1081  * set to clawing for dragon characters? MSW 2009-07-03
1082  */
1083  if (is_dragon_pl(op)) {
1084  object *tmp;
1085 
1087  if (tmp) /* I suppose it should always be true - but maybe there's
1088  * draconic toothache ? :) */
1089  return tmp;
1090  }
1091 
1092  last_skill = sizeof(unarmed_skills);
1093  FOR_INV_PREPARE(op, tmp) {
1094  if (tmp->type == SKILL) {
1095  int i;
1096 
1097  /* The order in the array is preferred order. So basically,
1098  * we just cut down the number to search - eg, if we find a skill
1099  * early on in flame touch, then we only need to look into the unarmed_array
1100  * to the entry before flame touch - don't care about the entries afterward,
1101  * because they are inferior skills.
1102  * if we end up finding the best skill (i==0) might as well return
1103  * right away - can't get any better than that.
1104  */
1105  for (i = 0; i < last_skill; i++) {
1106  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
1107  best_skill = tmp;
1108  last_skill = i;
1109  if (i == 0)
1110  return best_skill;
1111  }
1112  }
1113  }
1114  } FOR_INV_FINISH();
1115  return best_skill;
1116 }
1117 
1131 static void do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
1132  int success;
1133 
1134  /* For Players only: if there is no ready weapon, and no "attack" skill
1135  * is readied either then try to find a skill for the player to use.
1136  * it is presumed that if skill is set, it is a valid attack skill (eg,
1137  * the caller should have set it appropriately). We still want to pass
1138  * through that code if skill is set to change to the skill.
1139  */
1140  if (op->type == PLAYER) {
1141  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1142  size_t i;
1143 
1144  if (!skill) {
1145  /* See if the players chosen skill is a combat skill, and use
1146  * it if appropriate.
1147  */
1148  if (op->chosen_skill) {
1149  /* the list is 0-terminated, and talismans, which can be in chosen_skill,
1150  * have a subtype of 0, therefore don't check the 0 */
1151  for (i = 0; unarmed_skills[i] != 0; i++)
1152  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1153  skill = op->chosen_skill;
1154  break;
1155  }
1156  }
1157  /* If we didn't find a skill above, look harder for a good skill */
1158  if (!skill) {
1159  skill = find_best_player_hth_skill(op);
1160 
1161  if (!skill) {
1164  "You have no unarmed combat skills!");
1165  return;
1166  }
1167  }
1168  }
1169  if (skill != op->chosen_skill) {
1170  /* now try to ready the new skill */
1171  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1174  "Couldn't change to skill %s",
1175  skill->name);
1176  return;
1177  }
1178  }
1179  } else {
1180  /* Seen some crashes below where current_weapon is not set,
1181  * even though the flag says it is. So if current weapon isn't set,
1182  * do some work in trying to find the object to use.
1183  */
1184  if (!op->current_weapon) {
1185  object *tmp;
1186 
1187  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1189  if (!tmp) {
1190  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1191  op->current_weapon = NULL;
1192  return;
1193  } else {
1194  char weapon[MAX_BUF];
1195 
1196  query_name(tmp, weapon, MAX_BUF);
1197  op->current_weapon = tmp;
1198  }
1199  }
1200 
1201  /* Has ready weapon - make sure chosen_skill is set up properly */
1202  if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
1203  object *found_skill = find_skill_by_name(op, op->current_weapon->skill);
1204  assert(found_skill != NULL);
1205  change_skill(op, found_skill, 1);
1206  }
1207  }
1208  }
1209 
1210  /* lose invisibility/hiding status for running attacks */
1211 
1212  if (op->type == PLAYER && op->contr->tmp_invis) {
1213  op->contr->tmp_invis = 0;
1214  op->invisible = 0;
1215  op->hide = 0;
1217  }
1218 
1219  success = attack_ob(tmp, op);
1220 
1221  if (tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1222  char op_name[MAX_BUF];
1223  if (!success) { // In case of miss, attack_ob didn't print anything
1224  query_name(tmp, op_name, MAX_BUF);
1227  "You miss %s!",
1228  op_name);
1229  } else if (string != NULL) { // If string is NULL, message was already displayed
1230  if (op->type == PLAYER) {
1231  query_name(tmp, op_name, MAX_BUF);
1234  "You %s %s!",
1235  string, op_name);
1236  } else if (tmp->type == PLAYER) {
1237  query_name(op, op_name, MAX_BUF);
1240  "%s %s you!",
1241  op_name, string);
1242  }
1243  }
1244  }
1245 }
1246 
1270 void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1271  int16_t tx, ty;
1272  mapstruct *m;
1273  int mflags;
1274 
1275  if (!dir)
1276  dir = pl->facing;
1277  tx = freearr_x[dir];
1278  ty = freearr_y[dir];
1279 
1280  /* If we don't yet have an opponent, find if one exists, and attack.
1281  * Legal opponents are the same as outlined in move_player_attack()
1282  */
1283 
1284  if (tmp == NULL) {
1285  m = pl->map;
1286  tx = pl->x+freearr_x[dir];
1287  ty = pl->y+freearr_y[dir];
1288 
1289  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1290  if (mflags&P_OUT_OF_MAP)
1291  return;
1292 
1293  /* space must be blocked for there to be anything interesting to do */
1294  if (!(mflags&P_IS_ALIVE)
1295  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1296  return;
1297  }
1298 
1299  FOR_MAP_PREPARE(m, tx, ty, tmp2)
1300  if ((QUERY_FLAG(tmp2, FLAG_ALIVE) && tmp2->stats.hp >= 0)
1301  || QUERY_FLAG(tmp2, FLAG_CAN_ROLL)
1302  || tmp2->type == LOCKED_DOOR) {
1303  /* Don't attack party members */
1304  if ((pl->type == PLAYER && tmp2->type == PLAYER)
1305  && (pl->contr->party != NULL && pl->contr->party == tmp2->contr->party))
1306  return;
1307  tmp = tmp2;
1308  break;
1309  }
1310  FOR_MAP_FINISH();
1311  }
1312  if (!tmp) {
1313  if (pl->type == PLAYER)
1315  "There is nothing to attack!");
1316  return;
1317  }
1318 
1319  do_skill_attack(tmp, pl, string, skill);
1320 }
1321 
1340 static void attack_hth(object *pl, int dir, const char *string, object *skill) {
1341  object *weapon;
1342 
1345  if (weapon != NULL) {
1346  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1347  char weaponname[MAX_BUF];
1348 
1349  query_name(weapon, weaponname, MAX_BUF);
1352  "You are unable to unwield %s in order to attack with %s.",
1353  weaponname, skill->name);
1354  return;
1355  } else {
1357  "You unwield your weapon in order to attack.");
1358  }
1359  }
1360  }
1361  skill_attack(NULL, pl, dir, string, skill);
1362 }
1363 
1384 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1385  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1386  if (op->type == PLAYER)
1388  "You have no ready weapon to attack with!");
1389  return;
1390  }
1391  skill_attack(NULL, op, dir, string, skill);
1392 }
Face
Definition: face.h:14
living::exp
int64_t exp
Definition: living.h:47
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:533
PLAYER
@ PLAYER
Definition: object.h:112
global.h
settings
struct Settings settings
Definition: init.cpp:139
Settings::simple_exp
uint8_t simple_exp
Definition: global.h:263
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
SK_FIRE_MAGIC
@ SK_FIRE_MAGIC
Definition: skills.h:62
object_find_by_type_applied
object * object_find_by_type_applied(const object *who, int type)
Definition: object.cpp:4083
MSG_TYPE_ATTACK_MISS
#define MSG_TYPE_ATTACK_MISS
Definition: newclient.h:623
AP_APPLY
#define AP_APPLY
Definition: define.h:574
llevError
@ llevError
Definition: logger.h:11
learn_skill
int learn_skill(object *pl, object *scroll)
Definition: skill_util.cpp:759
SK_INSCRIPTION
@ SK_INSCRIPTION
Definition: skills.h:41
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
use_alchemy
int use_alchemy(object *op)
Definition: alchemy.cpp:1057
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
do_skill_attack
static void do_skill_attack(object *tmp, object *op, const char *string, object *skill)
Definition: skill_util.cpp:1131
SK_ALCHEMY
@ SK_ALCHEMY
Definition: skills.h:25
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Definition: newclient.h:410
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
archetypes_for_each
void archetypes_for_each(arch_op op)
Definition: assets.cpp:301
SK_CLAWING
@ SK_CLAWING
Definition: skills.h:50
Settings::permanent_exp_ratio
uint8_t permanent_exp_ratio
Definition: global.h:258
object::arch
struct archetype * arch
Definition: object.h:424
disinfect.a
a
Definition: disinfect.py:13
MSG_TYPE_ATTACK_DID_HIT
#define MSG_TYPE_ATTACK_DID_HIT
Definition: newclient.h:615
SK_DET_MAGIC
@ SK_DET_MAGIC
Definition: skills.h:30
TRAP
@ TRAP
Definition: object.h:246
SK_DISARM_TRAPS
@ SK_DISARM_TRAPS
Definition: skills.h:46
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
give_skill_by_name
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.cpp:1784
object::anim_suffix
sstring anim_suffix
Definition: object.h:324
WEAPON
@ WEAPON
Definition: object.h:124
SK_PRAYING
@ SK_PRAYING
Definition: skills.h:49
write_on_item
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.cpp:1762
range_none
@ range_none
Definition: player.h:30
commongive.inv
inv
Definition: commongive.py:29
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
use_oratory
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.cpp:996
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Definition: object.h:148
singing
int singing(object *pl, int dir, object *skill)
Definition: skills.cpp:1149
RUNE
@ RUNE
Definition: object.h:245
skill_ident
int skill_ident(object *pl, object *skill)
Definition: skills.cpp:927
fix_object
void fix_object(object *op)
Definition: living.cpp:1132
object_find_by_type_and_skill
object * object_find_by_type_and_skill(const object *who, int type, const char *skill)
Definition: object.cpp:4183
SK_KARATE
@ SK_KARATE
Definition: skills.h:38
Ice.tmp
int tmp
Definition: Ice.py:207
NDI_RED
#define NDI_RED
Definition: newclient.h:248
SEE_LAST_ERROR
@ SEE_LAST_ERROR
Definition: define.h:52
find_traps
int find_traps(object *pl, object *skill)
Definition: skills.cpp:1240
SK_MEDITATION
@ SK_MEDITATION
Definition: skills.h:35
SK_SET_TRAP
@ SK_SET_TRAP
Definition: skills.h:47
skills.h
give.parms
parms
Definition: give.py:28
find_skill_by_number
object * find_skill_by_number(object *who, int skillno)
Definition: skill_util.cpp:314
skill_messages
sstring skill_messages[MAX_SKILLS]
Definition: skill_util.cpp:69
P_IS_ALIVE
#define P_IS_ALIVE
Definition: map.h:235
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
object::level
int16_t level
Definition: object.h:361
SK_SINGING
@ SK_SINGING
Definition: skills.h:32
SK_SORCERY
@ SK_SORCERY
Definition: skills.h:55
digits_in_long
static int digits_in_long(int64_t num)
Definition: skill_util.cpp:839
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Definition: newclient.h:418
MAX
#define MAX(x, y)
Definition: compat.h:24
free_skill_index
static int free_skill_index()
Definition: skill_util.cpp:71
SK_EARTH_MAGIC
@ SK_EARTH_MAGIC
Definition: skills.h:60
FLAG_ALIVE
#define FLAG_ALIVE
Definition: define.h:230
m
static event_registration m
Definition: citylife.cpp:425
skill_names
const char * skill_names[MAX_SKILLS]
Definition: skill_util.cpp:59
autojail.who
who
Definition: autojail.py:3
object_update
void object_update(object *op, int action)
Definition: object.cpp:1434
FMT64
#define FMT64
Definition: compat.h:16
pick_lock
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.cpp:391
object::subtype
uint8_t subtype
Definition: object.h:349
SK_HARVESTING
@ SK_HARVESTING
Definition: skills.h:58
change_skill
int change_skill(object *who, object *new_skill, int flag)
Definition: skill_util.cpp:359
add_refcount
sstring add_refcount(sstring str)
Definition: shstr.cpp:210
SK_TWO_HANDED_WEAPON
@ SK_TWO_HANDED_WEAPON
Definition: skills.h:56
determine_god
const char * determine_god(object *op)
Definition: gods.cpp:55
freearr_y
short freearr_y[SIZEOFFREE]
Definition: object.cpp:305
SK_JEWELER
@ SK_JEWELER
Definition: skills.h:24
attack_hth
static void attack_hth(object *pl, int dir, const char *string, object *skill)
Definition: skill_util.cpp:1340
clipped_percent
static int clipped_percent(int64_t a, int64_t b)
Definition: skill_util.cpp:814
query_name
void query_name(const object *op, char *buf, size_t size)
Definition: item.cpp:588
shop_describe
int shop_describe(const object *op)
Definition: shop.cpp:1194
SK_LITERACY
@ SK_LITERACY
Definition: skills.h:27
remove_trap
int remove_trap(object *op, object *skill)
Definition: skills.cpp:1311
SK_DET_CURSE
@ SK_DET_CURSE
Definition: skills.h:33
archetype::clone
object clone
Definition: object.h:487
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
apply_anim_suffix
void apply_anim_suffix(object *who, const char *suffix)
Definition: anim.cpp:150
SK_BOWYER
@ SK_BOWYER
Definition: skills.h:23
FLAG_FREED
#define FLAG_FREED
Definition: define.h:233
LOCKED_DOOR
@ LOCKED_DOOR
Definition: object.h:128
do_skill
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Definition: skill_util.cpp:431
skill_faces
const Face * skill_faces[MAX_SKILLS]
Definition: skill_util.cpp:63
object::face
const Face * face
Definition: object.h:341
exp_level
int exp_level(int64_t exp)
Definition: living.cpp:1893
MSG_TYPE_ATTACK
#define MSG_TYPE_ATTACK
Definition: newclient.h:412
SK_EVOCATION
@ SK_EVOCATION
Definition: skills.h:54
Ice.b
b
Definition: Ice.py:48
object::type
uint8_t type
Definition: object.h:348
GET_MAP_MOVE_BLOCK
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:190
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
FLAG_CAN_ROLL
#define FLAG_CAN_ROLL
Definition: define.h:254
MSG_TYPE_SKILL_MISSING
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:590
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.cpp:2175
rangetype
rangetype
Definition: player.h:28
SK_BARGAINING
@ SK_BARGAINING
Definition: skills.h:28
level_exp
int64_t level_exp(int level, double expmul)
Definition: living.cpp:1881
archetype
Definition: object.h:483
pray
int pray(object *pl, object *skill)
Definition: skills.cpp:1384
SK_SUMMONING
@ SK_SUMMONING
Definition: skills.h:52
sproto.h
SK_AIR_MAGIC
@ SK_AIR_MAGIC
Definition: skills.h:59
skill_attack
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.cpp:1270
do_each_skill
static void do_each_skill(archetype *at)
Definition: skill_util.cpp:80
MAX_SKILLS
#define MAX_SKILLS
Definition: skills.h:70
FLAG_CAN_USE_SKILL
#define FLAG_CAN_USE_SKILL
Definition: define.h:321
MSG_TYPE_VICTIM_WAS_HIT
#define MSG_TYPE_VICTIM_WAS_HIT
Definition: newclient.h:654
skill_throw
int skill_throw(object *op, object *part, int dir, object *skill)
Definition: skills.cpp:2277
get_skill_client_code
int get_skill_client_code(const char *skill_name)
Definition: skill_util.cpp:116
clear_skill
void clear_skill(object *who)
Definition: skill_util.cpp:398
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
attack_melee_weapon
static void attack_melee_weapon(object *op, int dir, const char *string, object *skill)
Definition: skill_util.cpp:1384
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:245
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:590
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:247
MAX_BUF
#define MAX_BUF
Definition: define.h:35
find_best_player_hth_skill
static object * find_best_player_hth_skill(object *op)
Definition: skill_util.cpp:1042
adjust_skill_tool
static object * adjust_skill_tool(object *who, object *skill, object *skill_tool)
Definition: skill_util.cpp:149
SK_FLAME_TOUCH
@ SK_FLAME_TOUCH
Definition: skills.h:37
SK_JUMPING
@ SK_JUMPING
Definition: skills.h:29
SK_HIDING
@ SK_HIDING
Definition: skills.h:21
SK_LEVITATION
@ SK_LEVITATION
Definition: skills.h:51
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
FLAG_READY_SKILL
#define FLAG_READY_SKILL
Definition: define.h:333
PERM_EXP
#define PERM_EXP(exptotal)
Definition: global.h:230
SK_LOCKPICKING
@ SK_LOCKPICKING
Definition: skills.h:20
MSG_TYPE_SKILL_ERROR
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:591
SK_WOODSMAN
@ SK_WOODSMAN
Definition: skills.h:40
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:265
spells.h
object::name
sstring name
Definition: object.h:319
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:593
use_skill
int use_skill(object *op, const char *string)
Definition: skill_util.cpp:964
is_dragon_pl
int is_dragon_pl(const object *op)
Definition: player.cpp:122
get_learn_spell
int get_learn_spell(int stat)
Definition: living.cpp:2373
SK_PUNCHING
@ SK_PUNCHING
Definition: skills.h:36
MSG_TYPE_SKILL_LIST
#define MSG_TYPE_SKILL_LIST
Definition: newclient.h:595
SK_WRAITH_FEED
@ SK_WRAITH_FEED
Definition: skills.h:57
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.cpp:300
mapstruct
Definition: map.h:313
sstring
const typedef char * sstring
Definition: sstring.h:2
give.op
op
Definition: give.py:33
autojail.value
value
Definition: autojail.py:6
object::skill
sstring skill
Definition: object.h:329
SK_STEALING
@ SK_STEALING
Definition: skills.h:26
shop.h
object::msg
sstring msg
Definition: object.h:330
SKILL_TOOL
@ SKILL_TOOL
Definition: object.h:194
assets.h
SK_MISSILE_WEAPON
@ SK_MISSILE_WEAPON
Definition: skills.h:43
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
npc_dialog.index
int index
Definition: npc_dialog.py:102
do_harvest
void do_harvest(object *pl, int dir, object *skill)
Definition: c_misc.cpp:2263
SK_SUBTRACT_SKILL_EXP
#define SK_SUBTRACT_SKILL_EXP
Definition: skills.h:81
apply_special
int apply_special(object *who, object *op, int aflags)
Definition: apply.cpp:1156
AP_NOPRINT
#define AP_NOPRINT
Definition: define.h:585
SK_ONE_HANDED_WEAPON
@ SK_ONE_HANDED_WEAPON
Definition: skills.h:42
show_skills
void show_skills(object *op, const char *parms)
Definition: skill_util.cpp:860
meditate
void meditate(object *pl, object *skill)
Definition: skills.cpp:1432
steal
int steal(object *op, int dir, object *skill)
Definition: skills.cpp:279
make_face_from_files.int
int
Definition: make_face_from_files.py:32
SK_PYROMANCY
@ SK_PYROMANCY
Definition: skills.h:53
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.cpp:667
init_skills
void init_skills(void)
Definition: skill_util.cpp:99
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.cpp:308
SK_SMITHERY
@ SK_SMITHERY
Definition: skills.h:22
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
SK_THAUMATURGY
@ SK_THAUMATURGY
Definition: skills.h:48
OB_TYPE_MOVE_BLOCK
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:432
object::stats
living stats
Definition: object.h:378
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:592
AP_UNAPPLY
#define AP_UNAPPLY
Definition: define.h:575
range_skill
@ range_skill
Definition: player.h:35
attack_ob
int attack_ob(object *op, object *hitter)
Definition: attack.cpp:937
SK_FIND_TRAPS
@ SK_FIND_TRAPS
Definition: skills.h:34
SK_USE_MAGIC_ITEM
@ SK_USE_MAGIC_ITEM
Definition: skills.h:45
freearr_x
short freearr_x[SIZEOFFREE]
Definition: object.cpp:299
hide
int hide(object *op, object *skill)
Definition: skills.cpp:496
link_player_skills
void link_player_skills(object *op)
Definition: player.cpp:287
FLAG_READY_WEAPON
#define FLAG_READY_WEAPON
Definition: define.h:334
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.cpp:211
SK_CLIMBING
@ SK_CLIMBING
Definition: skills.h:39
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
living.h
SK_THROWING
@ SK_THROWING
Definition: skills.h:44
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
object.h
SK_ORATORY
@ SK_ORATORY
Definition: skills.h:31
SK_WATER_MAGIC
@ SK_WATER_MAGIC
Definition: skills.h:61
llevDebug
@ llevDebug
Definition: logger.h:13
give.name
name
Definition: give.py:27
jump
int jump(object *pl, int dir, object *skill)
Definition: skills.cpp:666