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)) {
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  }
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)
457  apply_anim_suffix(op, 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)) {
469  "You come to earth.");
470  } else {
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) {
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 }
UP_OBJ_FACE
#define UP_OBJ_FACE
Definition: object.h:524
skills
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other skills
Definition: stats.txt:126
PLAYER
@ PLAYER
Definition: object.h:112
global.h
settings
struct Settings settings
Definition: init.cpp:139
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:4074
MSG_TYPE_ATTACK_MISS
#define MSG_TYPE_ATTACK_MISS
Definition: newclient.h:609
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:51
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:396
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
MSG_TYPE_ATTACK_DID_HIT
#define MSG_TYPE_ATTACK_DID_HIT
Definition: newclient.h:601
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:1777
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
Ice.tmp
int tmp
Definition: Ice.py:207
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:1125
object_find_by_type_and_skill
object * object_find_by_type_and_skill(const object *who, int type, const char *skill)
Definition: object.cpp:4174
SK_KARATE
@ SK_KARATE
Definition: skills.h:38
NDI_RED
#define NDI_RED
Definition: newclient.h:234
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:236
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
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:404
MAX
#define MAX(x, y)
Definition: compat.h:24
Settings::simple_exp
uint8_t simple_exp
Definition: global.h:263
free_skill_index
static int free_skill_index()
Definition: skill_util.cpp:71
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
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:1429
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:592
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:478
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:1886
MSG_TYPE_ATTACK
#define MSG_TYPE_ATTACK
Definition: newclient.h:398
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:191
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:576
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.cpp:2168
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:1874
archetype
Definition: object.h:474
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:640
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
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:231
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:248
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:577
SK_WOODSMAN
@ SK_WOODSMAN
Definition: skills.h:40
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:570
spells.h
object::name
sstring name
Definition: object.h:319
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:579
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:2366
SK_PUNCHING
@ SK_PUNCHING
Definition: skills.h:36
MSG_TYPE_SKILL_LIST
#define MSG_TYPE_SKILL_LIST
Definition: newclient.h:581
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:299
mapstruct
Definition: map.h:314
sstring
const typedef char * sstring
Definition: sstring.h:2
floor
Magical Runes Runes are magical inscriptions on the dungeon floor
Definition: runes-guide.txt:3
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
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
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
guild_entry.x1
int x1
Definition: guild_entry.py:33
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
Face
Definition: face.h:14
init_skills
void init_skills(void)
Definition: skill_util.cpp:99
SK_SMITHERY
@ SK_SMITHERY
Definition: skills.h:22
skill
skill
Definition: arch-handbook.txt:585
a
Magical Runes Runes are magical inscriptions on the dungeon which cast a spell or detonate when something steps on them Flying objects don t detonate runes Beware ! Runes are invisible most of the time They are only visible occasionally ! There are several runes which are there are some special runes which may only be called with the invoke and people may apply it to read it Maybe useful for mazes ! This rune will not nor is it ordinarily invisible Partial Visibility of they ll be visible only part of the time They have a(your level/2) chance of being visible in any given round
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
Settings::permanent_exp_ratio
uint8_t permanent_exp_ratio
Definition: global.h:258
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:578
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
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
jump
int jump(object *pl, int dir, object *skill)
Definition: skills.cpp:666
hide
uint32 hide
Definition: arch-handbook.txt:572