Crossfire Server, Trunk  R22047
skill_util.c
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 
65 static int free_skill_index() {
66  int i;
67  for (i = 0; i < MAX_SKILLS; i++) {
68  if (skill_names[i] == NULL && skill_faces[i] == NULL) {
69  break;
70  }
71  }
72  return i;
73 }
74 static void do_each_skill(archetype *at) {
75  if (at->clone.type == SKILL) {
76  int i = free_skill_index();
77  if (i == MAX_SKILLS) {
78  LOG(llevError, "init_skills: too many skills, increase MAX_SKILLS and rebuild server!");
80  }
81  skill_names[i] = add_refcount(at->clone.skill);
82  if (at->clone.face != NULL)
83  skill_faces[i] = at->clone.face;
84  }
85 }
86 
91 void init_skills(void) {
92  int i;
93 
94  for (i = 0; i < MAX_SKILLS; i++) {
95  skill_names[i] = NULL;
96  skill_faces[i] = NULL;
97  }
98 
100 }
101 
107 int get_skill_client_code(const char *skill_name)
108 {
109  int index;
110  for (index = 0; index < MAX_SKILLS && skill_names[index] != NULL; index++)
111  if (strcmp(skill_names[index], skill_name) == 0)
112  return index;
113 
114  assert("invalid skill!");
115  return 0;
116 }
117 
140 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
141  if (!skill && !skill_tool)
142  return NULL;
143 
144  /* If this is a skill that can be used without a tool and no tool found, return it */
145  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
146  return skill;
147 
148  /* Player has a tool to use the skill. If not applied, apply it -
149  * if not successful, return skill if can be used. If they do have the skill tool
150  * but not the skill itself, give it to them.
151  */
152  if (skill_tool) {
153  if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
154  if (apply_special(who, skill_tool, 0)) {
155  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
156  return skill;
157  else
158  return NULL;
159  }
160  }
161  if (!skill) {
162  skill = give_skill_by_name(who, skill_tool->skill);
163  link_player_skills(who);
164  }
165  return skill;
166  }
167  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
168  return skill;
169  else
170  return NULL;
171 }
172 
202 object *find_skill_by_name(object *who, const char *name) {
203  object *skill = NULL, *skills[MAX_SKILLS], *skill_tools[MAX_SKILLS];
204  const char *skill_names[MAX_SKILLS];
205  char *ourname=NULL;
206  int num_names, highest_level_skill=0, i;
207 
208  if (!name)
209  return NULL;
210 
211  /* Simple case - no commas in past in name, so don't need to tokenize */
212  if (!strchr(name, ',')) {
213  skill_names[0] = name;
214  skill_tools[0] = NULL;
215  skills[0] = NULL;
216  num_names = 1;
217  } else {
218  /* strtok_r is destructive, so we need our own copy */
219  char *lasts;
220  ourname = strdup(name);
221 
222  if ((skill_names[0] = strtok_r(ourname, ",", &lasts)) == NULL) {
223  /* This should really never happen */
224  LOG(llevError, "find_skill_by_name: strtok_r returned null, but strchr did not?\n");
225  free(ourname);
226  return NULL;
227  } else {
228  skill_tools[0] = NULL;
229  skills[0] = NULL;
230  /* we already have the first name from the strtok_r above */
231  num_names=1;
232  while ((skill_names[num_names] = strtok_r(NULL, ",", &lasts)) != NULL) {
233  /* Clean out any leading spacing. typical string would be
234  * skill1, skill2, skill3, ...
235  */
236  while (isspace(*skill_names[num_names]))
237  skill_names[num_names]++;
238  skills[num_names] = NULL;
239  skill_tools[num_names] = NULL;
240  num_names++;
241  }
242  }
243  /* While we don't use ourname below this point, the skill_names[] points into
244  * it, so we can't free it yet.
245  */
246  }
247 
248  FOR_INV_PREPARE(who, tmp) {
249  /* We make sure the length of the string in the object is greater
250  * in length than the passed string. Eg, if we have a skill called
251  * 'hi', we don't want to match if the user passed 'high'
252  */
253  if (tmp->type == SKILL || (tmp->type == SKILL_TOOL && !QUERY_FLAG(tmp, FLAG_UNPAID))) {
254  for (i = 0; i<num_names; i++) {
255  if (!strncasecmp(skill_names[i], tmp->skill, strlen(skill_names[i])) &&
256  strlen(tmp->skill) >= strlen(skill_names[i])) {
257  if (tmp->type == SKILL) {
258  skills[i] = tmp;
259  if (!skill || tmp->level > skill->level) {
260  skill = tmp;
261  highest_level_skill=i;
262  }
263  }
264  else {
265  /* Skill tools don't have levels, so we basically find the
266  * 'best' skill tool for this skill.
267  */
268  if (QUERY_FLAG(tmp, FLAG_APPLIED) || !skill_tools[i] ||
269  !QUERY_FLAG(skill_tools[i], FLAG_APPLIED)) {
270  skill_tools[i] = tmp;
271  }
272  }
273  /* Got a matching name - no reason to look through rest of names */
274  break;
275  }
276  }
277  }
278  } FOR_INV_FINISH();
279  free(ourname);
280  return adjust_skill_tool(who, skills[highest_level_skill], skill_tools[highest_level_skill]);
281 }
282 
305 object *find_skill_by_number(object *who, int skillno) {
306  object *skill = NULL, *skill_tool = NULL;
307 
308  if (skillno <= 0 || skillno > MAX_SKILLS) {
309  return NULL;
310  }
311 
312  FOR_INV_PREPARE(who, tmp) {
313  if (tmp->type == SKILL && tmp->subtype == skillno)
314  skill = tmp;
315 
316  /* Try to find appropriate skilltool. If the player has one already
317  * applied, we try to keep using that one.
318  */
319  else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
320  if (QUERY_FLAG(tmp, FLAG_APPLIED))
321  skill_tool = tmp;
322  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
323  skill_tool = tmp;
324  }
325  } FOR_INV_FINISH();
326 
327  return adjust_skill_tool(who, skill, skill_tool);
328 }
329 
350 int change_skill(object *who, object *new_skill, int flag) {
351  rangetype old_range;
352 
353  if (who->type != PLAYER)
354  return 0;
355 
356  old_range = who->contr->shoottype;
357 
358  /* The readied skill can be a skill tool, so check on actual skill instead of object. */
359  if (who->chosen_skill && who->chosen_skill->skill == new_skill->skill) {
360  /* optimization for changing skill to current skill */
361  if (!(flag&0x1))
362  who->contr->shoottype = range_skill;
363  return 1;
364  }
365 
366  if (who->chosen_skill)
367  apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
368 
369  /* Only goal in this case was to unapply a skill */
370  if (!new_skill)
371  return 0;
372 
373  if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
374  return 0;
375  }
376  if (flag&0x1)
377  who->contr->shoottype = old_range;
378 
379  return 1;
380 }
381 
389 void clear_skill(object *who) {
390  who->chosen_skill = NULL;
392  if (who->type == PLAYER) {
393  who->contr->ranges[range_skill] = NULL;
394  if (who->contr->shoottype == range_skill)
395  who->contr->shoottype = range_none;
396  }
397 }
398 
422 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
423  int success = 0, exp = 0;
424 
425  if (!skill)
426  return 0;
427 
428  /* The code below presumes that the skill points to the object that
429  * holds the exp, level, etc of the skill. So if this is a player
430  * go and try to find the actual real skill pointer, and if the
431  * the player doesn't have a bucket for that, create one.
432  */
433  if (skill->type != SKILL && op->type == PLAYER) {
434  object *tmp;
435 
436  tmp = object_find_by_type_and_skill(op, SKILL, skill->skill);
437  if (!tmp) {
438  tmp = give_skill_by_name(op, skill->skill);
439  if (!tmp) {
440  LOG(llevError, "do_skill: asked for skill %s but couldn't find matching SKILL archetype.\n", skill->skill);
441  return 0;
442  }
443  }
444  skill = tmp;
445  }
446 
447  if (skill->anim_suffix)
448  apply_anim_suffix(op, skill->anim_suffix);
449 
450  switch (skill->subtype) {
451  case SK_LEVITATION:
452  /* Not 100% sure if this will work with new movement code -
453  * the levitation skill has move_type for flying, so when
454  * equipped, that should transfer to player, when not,
455  * shouldn't.
456  */
457  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
458  CLEAR_FLAG(skill, FLAG_APPLIED);
460  "You come to earth.");
461  } else {
462  SET_FLAG(skill, FLAG_APPLIED);
464  "You rise into the air!.");
465  }
466  fix_object(op);
467  success = 1;
468  break;
469 
470  case SK_STEALING:
471  exp = success = steal(op, dir, skill);
472  break;
473 
474  case SK_LOCKPICKING:
475  exp = success = pick_lock(op, dir, skill);
476  break;
477 
478  case SK_HIDING:
479  exp = success = hide(op, skill);
480  break;
481 
482  case SK_JUMPING:
483  success = jump(op, dir, skill);
484  break;
485 
486  case SK_INSCRIPTION:
487  exp = success = write_on_item(op, string, skill);
488  break;
489 
490  case SK_MEDITATION:
491  meditate(op, skill);
492  success = 1;
493  break;
494  /* note that the following 'attack' skills gain exp through hit_player() */
495 
496  case SK_KARATE:
497  attack_hth(op, dir, "karate-chopped", skill);
498  break;
499 
500  case SK_PUNCHING:
501  attack_hth(op, dir, "punched", skill);
502  break;
503 
504  case SK_FLAME_TOUCH:
505  attack_hth(op, dir, "flamed", skill);
506  break;
507 
508  case SK_CLAWING:
509  attack_hth(op, dir, "clawed", skill);
510  break;
511 
512  case SK_WRAITH_FEED:
513  attack_hth(op, dir, "fed upon", skill);
514  break;
515 
518  (void)attack_melee_weapon(op, dir, NULL, skill);
519  break;
520 
521  case SK_FIND_TRAPS:
522  exp = success = find_traps(op, skill);
523  break;
524 
525  case SK_SINGING:
526  exp = success = singing(op, dir, skill);
527  break;
528 
529  case SK_ORATORY:
530  exp = success = use_oratory(op, dir, skill);
531  break;
532 
533  case SK_SMITHERY:
534  case SK_BOWYER:
535  case SK_JEWELER:
536  case SK_ALCHEMY:
537  case SK_THAUMATURGY:
538  case SK_LITERACY:
539  case SK_WOODSMAN:
540  if (use_alchemy(op) == 0)
541  exp = success = skill_ident(op, skill);
542  break;
543 
544  case SK_DET_MAGIC:
545  case SK_DET_CURSE:
546  exp = success = skill_ident(op, skill);
547  break;
548 
549  case SK_DISARM_TRAPS:
550  exp = success = remove_trap(op, skill);
551  break;
552 
553  case SK_THROWING:
554  success = skill_throw(op, part, dir, skill);
555  break;
556 
557  case SK_SET_TRAP:
559  "This skill is not currently implemented.");
560  break;
561 
562  case SK_USE_MAGIC_ITEM:
563  case SK_MISSILE_WEAPON:
565  "There is no special attack for this skill.");
566  break;
567 
568  case SK_PRAYING:
569  success = pray(op, skill);
570  break;
571 
572  case SK_BARGAINING:
573  success = shop_describe(op);
574  break;
575 
576  case SK_SORCERY:
577  case SK_EVOCATION:
578  case SK_PYROMANCY:
579  case SK_SUMMONING:
580  case SK_CLIMBING:
581  case SK_EARTH_MAGIC:
582  case SK_AIR_MAGIC:
583  case SK_FIRE_MAGIC:
584  case SK_WATER_MAGIC:
586  "This skill is already in effect.");
587  break;
588 
589  case SK_HARVESTING:
590  do_harvest(op, dir, skill);
591  success = 0;
592  break;
593 
594  default: {
595  char name[MAX_BUF];
596 
597  query_name(op, name, MAX_BUF);
598  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
599  break;
600  }
601  }
602 
603  /* For players we now update the speed_left from using the skill.
604  * Monsters have no skill use time because of the random nature in
605  * which use_monster_skill is called already simulates this.
606  * If certain skills should take more/less time, that should be
607  * in the code for the skill itself.
608  */
609 
610  if (op->type == PLAYER)
611  op->speed_left -= 1.0;
612 
613  /* this is a good place to add experience for successful use of skills.
614  * Note that add_exp() will figure out player/monster experience
615  * gain problems.
616  */
617 
618  if (success && exp)
619  change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
620 
621  return success;
622 }
623 
658 int64_t calc_skill_exp(const object *who, const object *op, const object *skill) {
659  int64_t op_exp;
660  int op_lvl;
661  float base, value, lvl_mult;
662 
663  if (!skill)
664  skill = who;
665 
666  /* Oct 95 - where we have an object, I expanded our treatment
667  * to 3 cases:
668  * non-living magic obj, runes and everything else.
669  *
670  * If an object is not alive and magical we set the base exp higher to
671  * help out exp awards for skill_ident skills. Also, if
672  * an item is type RUNE, we give out exp based on stats.Cha
673  * and level (this was the old system) -b.t.
674  */
675 
676  if (!op) { /* no item/creature */
677  op_lvl = MAX(who->map->difficulty, 1);
678  op_exp = 0;
679  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
680  * for the amount of experience */
681  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
682  op_lvl = op->level;
683  } else { /* all other items/living creatures */
684  op_exp = op->stats.exp;
685  op_lvl = op->level;
686  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
687  op_lvl += 5*abs(op->magic);
688  }
689  }
690 
691  if (op_lvl < 1)
692  op_lvl = 1;
693 
694  if (who->type != PLAYER) { /* for monsters only */
695  return ((int64_t)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
696  }
697 
698  /* for players */
699  base = op_exp;
700  /* if skill really is a skill, then we can look at the skill archetype for
701  * base reward value (exp) and level multiplier factor.
702  */
703  if (skill->type == SKILL) {
704  base += skill->arch->clone.stats.exp;
705  if (settings.simple_exp) {
706  if (skill->arch->clone.level)
707  lvl_mult = (float)skill->arch->clone.level/100.0;
708  else
709  lvl_mult = 1.0; /* no adjustment */
710  } else {
711  if (skill->level)
712  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
713  else
714  lvl_mult = 1.0;
715  }
716  } else {
717  /* Don't divide by zero here! */
718  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
719  }
720 
721  /* assemble the exp total, and return value */
722 
723  value = base*lvl_mult;
724  if (value < 1)
725  value = 1; /* Always give at least 1 exp point */
726 
727 #ifdef SKILL_UTIL_DEBUG
728  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
729 #endif
730  return ((int64_t)value);
731 }
732 
750 int learn_skill(object *pl, object *scroll) {
751  object *tmp;
752 
753  if (!scroll->skill) {
754  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
755  return 2;
756  }
757 
758  /* can't use find_skill_by_name because we want skills the player knows
759  * but can't use natively.
760  */
761 
762  tmp = NULL;
763  FOR_INV_PREPARE(pl, inv)
764  if (inv->type == SKILL && !strncasecmp(scroll->skill, inv->skill, strlen(scroll->skill))) {
765  tmp = inv;
766  break;
767  }
768  FOR_INV_FINISH();
769 
770  /* player already knows it */
771  if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
772  return 0;
773 
774  /* now a random change to learn, based on player Int.
775  * give bonus based on level - otherwise stupid characters
776  * might never be able to learn anything.
777  */
778  if (random_roll(0, 99, pl, PREFER_LOW) > (get_learn_spell(pl->stats.Int)+(pl->level/5)))
779  return 2; /* failure :< */
780 
781  if (!tmp)
782  tmp = give_skill_by_name(pl, scroll->skill);
783 
784  if (!tmp) {
785  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
786  return 2;
787  }
788 
790  link_player_skills(pl);
791  return 1;
792 }
793 
805 static int clipped_percent(int64_t a, int64_t b) {
806  int rv;
807 
808  if (b <= 0)
809  return 0;
810 
811  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
812 
813  if (rv < 0)
814  return 0;
815  else if (rv > 100)
816  return 100;
817 
818  return rv;
819 }
820 
821 
830 static int digits_in_long(int64_t num)
831 {
832  return num == 0 ? 1 : floor( log10( labs(num) ) ) + 1;
833 }
834 
851 void show_skills(object *op, const char *parms) {
852  const char *cp;
853  int i, num_skills_found = 0;
854  /* Need to have a pointer and use strdup for qsort to work properly */
855  char skills[MAX_SKILLS][MAX_BUF];
856  const char *search = parms;
857  bool long_format = false;
858 
859  if ( parms && strncmp(parms,"-l",2) == 0 ) {
860  long_format = true;
861  search += 2;
862  while ( *search && *search != ' ' ) ++search; /* move past other parameters */
863  while ( *search == ' ' ) ++search;
864  }
865  FOR_INV_PREPARE(op, tmp) {
866  if (tmp->type == SKILL) {
867  if (search && strstr(tmp->name, search) == NULL)
868  continue;
869 
871  if ( long_format ) {
872  int perm_level = exp_level(tmp->total_exp * settings.permanent_exp_ratio / 100);
873  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%"FMT64"/%"FMT64")%-*sperm lvl:%3d (xp:%"FMT64"/%"FMT64") ",
874  tmp->name, tmp->level,
875  tmp->stats.exp,
876  level_exp(tmp->level+1, op->expmul),
877  1+2*digits_in_long(op->stats.exp)-digits_in_long(tmp->stats.exp)-digits_in_long(level_exp(tmp->level+1, op->expmul)),
878  "",
879  perm_level,
880  tmp->total_exp * settings.permanent_exp_ratio / 100,
881  level_exp(perm_level+1, op->expmul));
882  } else {
883  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%"FMT64"/%"FMT64"/%d%%)",
884  tmp->name, tmp->level,
885  tmp->stats.exp,
886  level_exp(tmp->level+1, op->expmul),
887  clipped_percent(PERM_EXP(tmp->total_exp), tmp->stats.exp));
888  }
889  } else {
890  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%"FMT64"/%"FMT64")",
891  tmp->name, tmp->level,
892  tmp->stats.exp,
893  level_exp(tmp->level+1, op->expmul));
894  }
895  /* I don't know why some characters get a bunch of skills, but
896  * it sometimes happens (maybe a leftover from buggier earlier code
897  * and those character are still about). In any case, lets handle
898  * it so it doesn't crash the server - otherwise, one character may
899  * crash the server numerous times.
900  */
901  if (num_skills_found >= MAX_SKILLS) {
903  "Your character has too many skills.\n"
904  "Something isn't right - contact the server admin");
905  break;
906  }
907  }
908  } FOR_INV_FINISH();
909 
910  if ( search && *search ) {
912  "Player skills%s: (matching '%s')", long_format ? " (long format)":"",search);
913  } else {
915  "Player skills%s:", long_format ? " (long format)":"");
916  }
917 
918  if (num_skills_found > 1)
919  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
920 
921  for (i = 0; i < num_skills_found; i++) {
923  skills[i]);
924  }
925 
926  cp = determine_god(op);
927  if (strcmp(cp, "none") == 0)
928  cp = NULL;
929 
931  "You can handle %d weapon improvements.\n"
932  "You worship %s.\n"
933  "Your equipped item power is %d out of %d\n",
934  op->level/5+5,
935  cp ? cp : "no god at current time",
936  op->contr->item_power, op->level);
937 }
938 
955 int use_skill(object *op, const char *string) {
956  object *skop;
957  size_t len;
958 
959  if (!string)
960  return 0;
961 
962  skop = NULL;
963  FOR_INV_PREPARE(op, tmp) {
964  if (tmp->type == SKILL
966  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
967  skop = tmp;
968  break;
969  } else if (tmp->type == SKILL_TOOL
970  && !QUERY_FLAG(tmp, FLAG_UNPAID) /* Holy symbols could be used unpaid w/o this */
971  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
972  skop = tmp;
973  break;
974  }
975  } FOR_INV_FINISH();
976  if (!skop) {
978  "Unable to find skill %s",
979  string);
980  return 0;
981  }
982 
983  len = strlen(skop->skill);
984 
985  /* All this logic goes and skips over the skill name to find any
986  * options given to the skill. Its pretty simple - if there
987  * are extra parameters (as determined by string length), we
988  * want to skip over any leading spaces.
989  */
990  if (len >= strlen(string)) {
991  string = NULL;
992  } else {
993  string += len;
994  while (*string == 0x20)
995  string++;
996  if (strlen(string) == 0)
997  string = NULL;
998  }
999 
1000 #ifdef SKILL_UTIL_DEBUG
1001  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
1002 #endif
1003 
1004  /* Change to the new skill, then execute it. */
1005  if (do_skill(op, op, skop, op->facing, string))
1006  return 1;
1007 
1008  return 0;
1009 }
1010 
1033 static object *find_best_player_hth_skill(object *op) {
1034  object *best_skill = NULL;
1035  int last_skill;
1036 
1037  if (op->contr->unarmed_skill) {
1038  /* command_unarmed_skill() already does these checks, and right
1039  * now I do not think there is any way to lose unarmed skills.
1040  * But maybe in the future there will be (polymorph?) so handle
1041  * it appropriately. MSW 2009-07-03
1042  *
1043  * Note that the error messages should only print out once when
1044  * the initial failure to switch skills happens, so the player
1045  * should not get spammed with tons of messages unless they have
1046  * no valid unarmed skill
1047  */
1048 
1049  best_skill = find_skill_by_name(op, op->contr->unarmed_skill);
1050 
1051  if (!best_skill) {
1053  "Unable to find skill %s - using default unarmed skill",
1054  op->contr->unarmed_skill);
1055  } else {
1056  size_t i;
1057 
1058  for (i = 0; i < sizeof(unarmed_skills); i++)
1059  if (best_skill->subtype == unarmed_skills[i])
1060  break;
1061  if (i < sizeof(unarmed_skills))
1062  return(best_skill);
1063  }
1064  /* If for some reason the unarmed_skill is not valid, we fall
1065  * through the processing below.
1066  */
1067  }
1068 
1069 
1070  /* Dragons are a special case - gros 25th July 2006 */
1071  /* Perhaps this special case should be removed and unarmed_skill
1072  * set to clawing for dragon characters? MSW 2009-07-03
1073  */
1074  if (is_dragon_pl(op)) {
1075  object *tmp;
1076 
1077  tmp = find_skill_by_number(op, SK_CLAWING);
1078  if (tmp) /* I suppose it should always be true - but maybe there's
1079  * draconic toothache ? :) */
1080  return tmp;
1081  }
1082 
1083  last_skill = sizeof(unarmed_skills);
1084  FOR_INV_PREPARE(op, tmp) {
1085  if (tmp->type == SKILL) {
1086  int i;
1087 
1088  /* The order in the array is preferred order. So basically,
1089  * we just cut down the number to search - eg, if we find a skill
1090  * early on in flame touch, then we only need to look into the unarmed_array
1091  * to the entry before flame touch - don't care about the entries afterward,
1092  * because they are inferior skills.
1093  * if we end up finding the best skill (i==0) might as well return
1094  * right away - can't get any better than that.
1095  */
1096  for (i = 0; i < last_skill; i++) {
1097  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
1098  best_skill = tmp;
1099  last_skill = i;
1100  if (i == 0)
1101  return best_skill;
1102  }
1103  }
1104  }
1105  } FOR_INV_FINISH();
1106  return best_skill;
1107 }
1108 
1122 static void do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
1123  int success;
1124 
1125  /* For Players only: if there is no ready weapon, and no "attack" skill
1126  * is readied either then try to find a skill for the player to use.
1127  * it is presumed that if skill is set, it is a valid attack skill (eg,
1128  * the caller should have set it appropriately). We still want to pass
1129  * through that code if skill is set to change to the skill.
1130  */
1131  if (op->type == PLAYER) {
1132  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1133  size_t i;
1134 
1135  if (!skill) {
1136  /* See if the players chosen skill is a combat skill, and use
1137  * it if appropriate.
1138  */
1139  if (op->chosen_skill) {
1140  /* the list is 0-terminated, and talismans, which can be in chosen_skill,
1141  * have a subtype of 0, therefore don't check the 0 */
1142  for (i = 0; unarmed_skills[i] != 0; i++)
1143  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1144  skill = op->chosen_skill;
1145  break;
1146  }
1147  }
1148  /* If we didn't find a skill above, look harder for a good skill */
1149  if (!skill) {
1150  skill = find_best_player_hth_skill(op);
1151 
1152  if (!skill) {
1153  draw_ext_info(NDI_BLACK, 0, op,
1155  "You have no unarmed combat skills!");
1156  return;
1157  }
1158  }
1159  }
1160  if (skill != op->chosen_skill) {
1161  /* now try to ready the new skill */
1162  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1165  "Couldn't change to skill %s",
1166  skill->name);
1167  return;
1168  }
1169  }
1170  } else {
1171  /* Seen some crashes below where current_weapon is not set,
1172  * even though the flag says it is. So if current weapon isn't set,
1173  * do some work in trying to find the object to use.
1174  */
1175  if (!op->current_weapon) {
1176  object *tmp;
1177 
1178  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1179  tmp = object_find_by_type_applied(op, WEAPON);
1180  if (!tmp) {
1181  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1182  op->current_weapon = NULL;
1183  return;
1184  } else {
1185  char weapon[MAX_BUF];
1186 
1187  query_name(tmp, weapon, MAX_BUF);
1188  op->current_weapon = tmp;
1189  }
1190  }
1191 
1192  /* Has ready weapon - make sure chosen_skill is set up properly */
1193  if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
1194  object *found_skill = find_skill_by_name(op, op->current_weapon->skill);
1195  assert(found_skill != NULL);
1196  change_skill(op, found_skill, 1);
1197  }
1198  }
1199  }
1200 
1201  /* lose invisibility/hiding status for running attacks */
1202 
1203  if (op->type == PLAYER && op->contr->tmp_invis) {
1204  op->contr->tmp_invis = 0;
1205  op->invisible = 0;
1206  op->hide = 0;
1207  object_update(op, UP_OBJ_FACE);
1208  }
1209 
1210  success = attack_ob(tmp, op);
1211 
1212  /*
1213  * print appropriate messages to the player
1214  *
1215  * If no string, then we aren't dealing with the
1216  * skill attack directly, so the message is printed elsewhere.
1217  *
1218  * Only print hit/miss if we've got a Fire+Attack.
1219  * Otherwise, don't print here at all.
1220  */
1221 
1222  if (string != NULL && tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1223  char op_name[MAX_BUF];
1224  if (success){
1225  if (op->type == PLAYER) {
1226  query_name(tmp, op_name, MAX_BUF);
1229  "You %s %s!",
1230  string, op_name);
1231  } else if (tmp->type == PLAYER) {
1232  query_name(op, op_name, MAX_BUF);
1235  "%s %s you!",
1236  op_name, string);
1237  }
1238  }
1239  else{
1240  query_name(tmp, op_name, MAX_BUF);
1243  "You miss %s!",
1244  op_name);
1245  }
1246  }
1247 }
1248 
1272 void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1273  int16_t tx, ty;
1274  mapstruct *m;
1275  int mflags;
1276 
1277  if (!dir)
1278  dir = pl->facing;
1279  tx = freearr_x[dir];
1280  ty = freearr_y[dir];
1281 
1282  /* If we don't yet have an opponent, find if one exists, and attack.
1283  * Legal opponents are the same as outlined in move_player_attack()
1284  */
1285 
1286  if (tmp == NULL) {
1287  m = pl->map;
1288  tx = pl->x+freearr_x[dir];
1289  ty = pl->y+freearr_y[dir];
1290 
1291  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1292  if (mflags&P_OUT_OF_MAP)
1293  return;
1294 
1295  /* space must be blocked for there to be anything interesting to do */
1296  if (!(mflags&P_IS_ALIVE)
1297  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1298  return;
1299  }
1300 
1301  FOR_MAP_PREPARE(m, tx, ty, tmp2)
1302  if ((QUERY_FLAG(tmp2, FLAG_ALIVE) && tmp2->stats.hp >= 0)
1303  || QUERY_FLAG(tmp2, FLAG_CAN_ROLL)
1304  || tmp2->type == LOCKED_DOOR) {
1305  /* Don't attack party members */
1306  if ((pl->type == PLAYER && tmp2->type == PLAYER)
1307  && (pl->contr->party != NULL && pl->contr->party == tmp2->contr->party))
1308  return;
1309  tmp = tmp2;
1310  break;
1311  }
1312  FOR_MAP_FINISH();
1313  }
1314  if (!tmp) {
1315  if (pl->type == PLAYER)
1317  "There is nothing to attack!");
1318  return;
1319  }
1320 
1321  do_skill_attack(tmp, pl, string, skill);
1322 }
1323 
1342 static void attack_hth(object *pl, int dir, const char *string, object *skill) {
1343  object *weapon;
1344 
1345  if (QUERY_FLAG(pl, FLAG_READY_WEAPON)) {
1346  weapon = object_find_by_type_applied(pl, WEAPON);
1347  if (weapon != NULL) {
1348  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1349  char weaponname[MAX_BUF];
1350 
1351  query_name(weapon, weaponname, MAX_BUF);
1354  "You are unable to unwield %s in order to attack with %s.",
1355  weaponname, skill->name);
1356  return;
1357  } else {
1359  "You unwield your weapon in order to attack.");
1360  }
1361  }
1362  }
1363  skill_attack(NULL, pl, dir, string, skill);
1364 }
1365 
1386 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1387  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1388  if (op->type == PLAYER)
1390  "You have no ready weapon to attack with!");
1391  return;
1392  }
1393  skill_attack(NULL, op, dir, string, skill);
1394 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:313
#define AP_UNAPPLY
Definition: define.h:612
Definition: player.h:92
int apply_special(object *who, object *op, int aflags)
Definition: apply.c:1156
const char * determine_god(object *op)
Definition: gods.c:106
#define FLAG_UNPAID
Definition: define.h:236
int skill_ident(object *pl, object *skill)
Definition: skills.c:929
int64_t level_exp(int level, double expmul)
Definition: living.c:1872
static void attack_melee_weapon(object *op, int dir, const char *string, object *skill)
Definition: skill_util.c:1386
void apply_anim_suffix(object *who, const char *suffix)
Definition: anim.c:149
#define SET_FLAG(xyz, p)
Definition: define.h:223
#define MSG_TYPE_SKILL_LIST
Definition: newclient.h:587
sstring add_refcount(sstring str)
Definition: shstr.c:210
object * object_find_by_type_applied(const object *who, int type)
Definition: object.c:3873
static int digits_in_long(int64_t num)
Definition: skill_util.c:830
void link_player_skills(object *op)
Definition: player.c:279
static int clipped_percent(int64_t a, int64_t b)
Definition: skill_util.c:805
int attack_ob(object *op, object *hitter)
Definition: attack.c:912
int exp_level(int64_t exp)
Definition: living.c:1884
const char * skill_names[MAX_SKILLS]
Definition: skill_util.c:59
void fatal(enum fatal_error err)
Definition: utils.c:597
void archetypes_for_each(arch_op op)
Definition: assets.cpp:300
Definition: face.h:14
int shop_describe(const object *op)
Definition: shop.c:1189
int use_skill(object *op, const char *string)
Definition: skill_util.c:955
short freearr_x[SIZEOFFREE]
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:601
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:389
static object * adjust_skill_tool(object *who, object *skill, object *skill_tool)
Definition: skill_util.c:140
int change_skill(object *who, object *new_skill, int flag)
Definition: skill_util.c:350
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:582
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Definition: skill_util.c:422
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:583
int jump(object *pl, int dir, object *skill)
Definition: skills.c:664
int remove_trap(object *op, object *skill)
Definition: skills.c:1317
#define MAX(x, y)
Definition: compat.h:20
void object_update(object *op, int action)
Definition: object.c:1190
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:658
#define NDI_BLACK
Definition: newclient.h:221
#define AP_APPLY
Definition: define.h:611
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:308
#define MIN(x, y)
Definition: compat.h:17
#define FLAG_CAN_ROLL
Definition: define.h:254
short freearr_y[SIZEOFFREE]
Definition: object.c:71
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1777
int use_alchemy(object *op)
Definition: alchemy.c:1049
#define FLAG_READY_SKILL
Definition: define.h:334
#define NDI_RED
Definition: newclient.h:224
int skill_throw(object *op, object *part, int dir, object *skill)
Definition: skills.c:2276
#define strtok_r(x, y, z)
Definition: win32.h:62
#define FLAG_ALIVE
Definition: define.h:230
#define MSG_TYPE_VICTIM
Definition: newclient.h:392
signed short int16_t
Definition: win32.h:160
#define FLAG_CAN_USE_SKILL
Definition: define.h:322
int hide(object *op, object *skill)
Definition: skills.c:494
int is_dragon_pl(const object *op)
Definition: player.c:114
#define snprintf
Definition: win32.h:46
#define PERM_EXP(exptotal)
Definition: global.h:230
static void attack_hth(object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1342
#define FOR_INV_FINISH()
Definition: define.h:714
void show_skills(object *op, const char *parms)
Definition: skill_util.c:851
#define FMT64
Definition: compat.h:12
const Face * skill_faces[MAX_SKILLS]
Definition: skill_util.c:63
#define MSG_TYPE_VICTIM_WAS_HIT
Definition: newclient.h:647
int steal(object *op, int dir, object *skill)
Definition: skills.c:277
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:447
#define AP_NOPRINT
Definition: define.h:623
#define P_OUT_OF_MAP
Definition: map.h:252
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:193
#define MSG_TYPE_ATTACK_MISS
Definition: newclient.h:615
static object * find_best_player_hth_skill(object *op)
Definition: skill_util.c:1033
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:585
uint8_t simple_exp
Definition: global.h:262
void do_harvest(object *pl, int dir, object *skill)
Definition: c_misc.c:2275
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
static int free_skill_index()
Definition: skill_util.c:65
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:2166
#define MAX_BUF
Definition: define.h:35
#define MSG_TYPE_ATTACK
Definition: newclient.h:385
#define MSG_TYPE_ATTACK_DID_HIT
Definition: newclient.h:607
int strncasecmp(const char *s1, const char *s2, int n)
void init_skills(void)
Definition: skill_util.c:91
int learn_skill(object *pl, object *scroll)
Definition: skill_util.c:750
signed __int64 int64_t
Definition: win32.h:168
#define FLAG_READY_WEAPON
Definition: define.h:335
#define FOR_MAP_FINISH()
Definition: define.h:767
uint8_t permanent_exp_ratio
Definition: global.h:257
int get_skill_client_code(const char *skill_name)
Definition: skill_util.c:107
int pray(object *pl, object *skill)
Definition: skills.c:1390
object * object_find_by_type_and_skill(const object *who, int type, const char *skill)
Definition: object.c:3973
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1153
struct Settings settings
Definition: init.c:39
rangetype
Definition: player.h:15
#define FLAG_APPLIED
Definition: define.h:235
int find_traps(object *pl, object *skill)
Definition: skills.c:1246
#define MSG_TYPE_SKILL
Definition: newclient.h:383
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:311
#define NDI_UNIQUE
Definition: newclient.h:245
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.c:999
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
int get_learn_spell(int stat)
Definition: living.c:2364
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:202
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:760
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:584
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:583
#define MAX_SKILLS
Definition: skills.h:70
#define SK_SUBTRACT_SKILL_EXP
Definition: skills.h:81
Definition: map.h:326
#define P_IS_ALIVE
Definition: map.h:238
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
void clear_skill(object *who)
Definition: skill_util.c:389
object * find_skill_by_number(object *who, int skillno)
Definition: skill_util.c:305
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1768
void fix_object(object *op)
Definition: living.c:1124
void meditate(object *pl, object *skill)
Definition: skills.c:1438
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1272
static void do_each_skill(archetype *at)
Definition: skill_util.c:74
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:707
static void do_skill_attack(object *tmp, object *op, const char *string, object *skill)
Definition: skill_util.c:1122
#define FLAG_FREED
Definition: define.h:233