Crossfire Server, Trunk  R213250
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 
52 static void attack_hth(object *pl, int dir, const char *string, object *skill);
53 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill);
54 
58 const char *skill_names[MAX_SKILLS];
63 
68 void init_skills(void) {
69  int i;
70  archetype *at;
71 
72  for (i = 0; i < MAX_SKILLS; i++) {
73  skill_names[i] = NULL;
74  skill_faces[i] = -1;
75  }
76  i = 0;
77 
78  for (at = first_archetype; at != NULL; at = at->next) {
79  if (at->clone.type == SKILL) {
80  if (i == MAX_SKILLS) {
81  LOG(llevError, "init_skills: too many skills, increase MAX_SKILLS and rebuild server!");
83  }
85  if (at->clone.face != NULL)
86  skill_faces[i] = at->clone.face->number;
87  i++;
88  }
89  }
90 }
91 
97 int get_skill_client_code(const char *skill_name)
98 {
99  int index;
100  for (index = 0; index < MAX_SKILLS && skill_names[index] != NULL; index++)
101  if (strcmp(skill_names[index], skill_name) == 0)
102  return index;
103 
104  assert("invalid skill!");
105  return 0;
106 }
107 
130 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
131  if (!skill && !skill_tool)
132  return NULL;
133 
134  /* If this is a skill that can be used without a tool and no tool found, return it */
135  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
136  return skill;
137 
138  /* Player has a tool to use the skill. If not applied, apply it -
139  * if not successful, return skill if can be used. If they do have the skill tool
140  * but not the skill itself, give it to them.
141  */
142  if (skill_tool) {
143  if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
144  if (apply_special(who, skill_tool, 0)) {
145  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
146  return skill;
147  else
148  return NULL;
149  }
150  }
151  if (!skill) {
152  skill = give_skill_by_name(who, skill_tool->skill);
153  link_player_skills(who);
154  }
155  return skill;
156  }
157  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
158  return skill;
159  else
160  return NULL;
161 }
162 
192 object *find_skill_by_name(object *who, const char *name) {
193  object *skill = NULL, *skills[MAX_SKILLS], *skill_tools[MAX_SKILLS];
194  const char *skill_names[MAX_SKILLS];
195  char *ourname=NULL;
196  int num_names, highest_level_skill=0, i;
197 
198  if (!name)
199  return NULL;
200 
201  /* Simple case - no commas in past in name, so don't need to tokenize */
202  if (!strchr(name, ',')) {
203  skill_names[0] = name;
204  skill_tools[0] = NULL;
205  skills[0] = NULL;
206  num_names = 1;
207  } else {
208  /* strtok_r is destructive, so we need our own copy */
209  char *lasts;
210  ourname = strdup(name);
211 
212  if ((skill_names[0] = strtok_r(ourname, ",", &lasts)) == NULL) {
213  /* This should really never happen */
214  LOG(llevError, "find_skill_by_name: strtok_r returned null, but strchr did not?\n");
215  free(ourname);
216  return NULL;
217  } else {
218  skill_tools[0] = NULL;
219  skills[0] = NULL;
220  /* we already have the first name from the strtok_r above */
221  num_names=1;
222  while ((skill_names[num_names] = strtok_r(NULL, ",", &lasts)) != NULL) {
223  /* Clean out any leading spacing. typical string would be
224  * skill1, skill2, skill3, ...
225  */
226  while (isspace(*skill_names[num_names]))
227  skill_names[num_names]++;
228  skills[num_names] = NULL;
229  skill_tools[num_names] = NULL;
230  num_names++;
231  }
232  }
233  /* While we don't use ourname below this point, the skill_names[] points into
234  * it, so we can't free it yet.
235  */
236  }
237 
238  FOR_INV_PREPARE(who, tmp) {
239  /* We make sure the length of the string in the object is greater
240  * in length than the passed string. Eg, if we have a skill called
241  * 'hi', we don't want to match if the user passed 'high'
242  */
243  if (tmp->type == SKILL || tmp->type == SKILL_TOOL) {
244  for (i = 0; i<num_names; i++) {
245  if (!strncasecmp(skill_names[i], tmp->skill, strlen(skill_names[i])) &&
246  strlen(tmp->skill) >= strlen(skill_names[i])) {
247  if (tmp->type == SKILL) {
248  skills[i] = tmp;
249  if (!skill || tmp->level > skill->level) {
250  skill = tmp;
251  highest_level_skill=i;
252  }
253  }
254  else {
255  /* Skill tools don't have levels, so we basically find the
256  * 'best' skill tool for this skill.
257  */
258  if (QUERY_FLAG(tmp, FLAG_APPLIED) || !skill_tools[i] ||
259  !QUERY_FLAG(skill_tools[i], FLAG_APPLIED)) {
260  skill_tools[i] = tmp;
261  }
262  }
263  /* Got a matching name - no reason to look through rest of names */
264  break;
265  }
266  }
267  }
268  } FOR_INV_FINISH();
269  free(ourname);
270  return adjust_skill_tool(who, skills[highest_level_skill], skill_tools[highest_level_skill]);
271 }
272 
295 object *find_skill_by_number(object *who, int skillno) {
296  object *skill = NULL, *skill_tool = NULL;
297 
298  if (skillno <= 0 || skillno > MAX_SKILLS) {
299  return NULL;
300  }
301 
302  FOR_INV_PREPARE(who, tmp) {
303  if (tmp->type == SKILL && tmp->subtype == skillno)
304  skill = tmp;
305 
306  /* Try to find appropriate skilltool. If the player has one already
307  * applied, we try to keep using that one.
308  */
309  else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
310  if (QUERY_FLAG(tmp, FLAG_APPLIED))
311  skill_tool = tmp;
312  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
313  skill_tool = tmp;
314  }
315  } FOR_INV_FINISH();
316 
317  return adjust_skill_tool(who, skill, skill_tool);
318 }
319 
340 int change_skill(object *who, object *new_skill, int flag) {
341  rangetype old_range;
342 
343  if (who->type != PLAYER)
344  return 0;
345 
346  old_range = who->contr->shoottype;
347 
348  /* The readied skill can be a skill tool, so check on actual skill instead of object. */
349  if (who->chosen_skill && who->chosen_skill->skill == new_skill->skill) {
350  /* optimization for changing skill to current skill */
351  if (!(flag&0x1))
352  who->contr->shoottype = range_skill;
353  return 1;
354  }
355 
356  if (who->chosen_skill)
357  apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
358 
359  /* Only goal in this case was to unapply a skill */
360  if (!new_skill)
361  return 0;
362 
363  if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
364  return 0;
365  }
366  if (flag&0x1)
367  who->contr->shoottype = old_range;
368 
369  return 1;
370 }
371 
379 void clear_skill(object *who) {
380  who->chosen_skill = NULL;
382  if (who->type == PLAYER) {
383  who->contr->ranges[range_skill] = NULL;
384  if (who->contr->shoottype == range_skill)
385  who->contr->shoottype = range_none;
386  }
387 }
388 
412 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
413  int success = 0, exp = 0;
414 
415  if (!skill)
416  return 0;
417 
418  /* The code below presumes that the skill points to the object that
419  * holds the exp, level, etc of the skill. So if this is a player
420  * go and try to find the actual real skill pointer, and if the
421  * the player doesn't have a bucket for that, create one.
422  */
423  if (skill->type != SKILL && op->type == PLAYER) {
424  object *tmp;
425 
426  tmp = object_find_by_type_and_skill(op, SKILL, skill->skill);
427  if (!tmp) {
428  tmp = give_skill_by_name(op, skill->skill);
429  if (!tmp) {
430  LOG(llevError, "do_skill: asked for skill %s but couldn't find matching SKILL archetype.\n", skill->skill);
431  return 0;
432  }
433  }
434  skill = tmp;
435  }
436 
437  if (skill->anim_suffix)
438  apply_anim_suffix(op, skill->anim_suffix);
439 
440  switch (skill->subtype) {
441  case SK_LEVITATION:
442  /* Not 100% sure if this will work with new movement code -
443  * the levitation skill has move_type for flying, so when
444  * equipped, that should transfer to player, when not,
445  * shouldn't.
446  */
447  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
448  CLEAR_FLAG(skill, FLAG_APPLIED);
450  "You come to earth.");
451  } else {
452  SET_FLAG(skill, FLAG_APPLIED);
454  "You rise into the air!.");
455  }
456  fix_object(op);
457  success = 1;
458  break;
459 
460  case SK_STEALING:
461  exp = success = steal(op, dir, skill);
462  break;
463 
464  case SK_LOCKPICKING:
465  exp = success = pick_lock(op, dir, skill);
466  break;
467 
468  case SK_HIDING:
469  exp = success = hide(op, skill);
470  break;
471 
472  case SK_JUMPING:
473  success = jump(op, dir, skill);
474  break;
475 
476  case SK_INSCRIPTION:
477  exp = success = write_on_item(op, string, skill);
478  break;
479 
480  case SK_MEDITATION:
481  meditate(op, skill);
482  success = 1;
483  break;
484  /* note that the following 'attack' skills gain exp through hit_player() */
485 
486  case SK_KARATE:
487  attack_hth(op, dir, "karate-chopped", skill);
488  break;
489 
490  case SK_PUNCHING:
491  attack_hth(op, dir, "punched", skill);
492  break;
493 
494  case SK_FLAME_TOUCH:
495  attack_hth(op, dir, "flamed", skill);
496  break;
497 
498  case SK_CLAWING:
499  attack_hth(op, dir, "clawed", skill);
500  break;
501 
502  case SK_WRAITH_FEED:
503  attack_hth(op, dir, "fed upon", skill);
504  break;
505 
508  (void)attack_melee_weapon(op, dir, NULL, skill);
509  break;
510 
511  case SK_FIND_TRAPS:
512  exp = success = find_traps(op, skill);
513  break;
514 
515  case SK_SINGING:
516  exp = success = singing(op, dir, skill);
517  break;
518 
519  case SK_ORATORY:
520  exp = success = use_oratory(op, dir, skill);
521  break;
522 
523  case SK_SMITHERY:
524  case SK_BOWYER:
525  case SK_JEWELER:
526  case SK_ALCHEMY:
527  case SK_THAUMATURGY:
528  case SK_LITERACY:
529  case SK_WOODSMAN:
530  if (use_alchemy(op) == 0)
531  exp = success = skill_ident(op, skill);
532  break;
533 
534  case SK_DET_MAGIC:
535  case SK_DET_CURSE:
536  exp = success = skill_ident(op, skill);
537  break;
538 
539  case SK_DISARM_TRAPS:
540  exp = success = remove_trap(op, skill);
541  break;
542 
543  case SK_THROWING:
544  success = skill_throw(op, part, dir, skill);
545  break;
546 
547  case SK_SET_TRAP:
549  "This skill is not currently implemented.");
550  break;
551 
552  case SK_USE_MAGIC_ITEM:
553  case SK_MISSILE_WEAPON:
555  "There is no special attack for this skill.");
556  break;
557 
558  case SK_PRAYING:
559  success = pray(op, skill);
560  break;
561 
562  case SK_BARGAINING:
563  success = shop_describe(op);
564  break;
565 
566  case SK_SORCERY:
567  case SK_EVOCATION:
568  case SK_PYROMANCY:
569  case SK_SUMMONING:
570  case SK_CLIMBING:
571  case SK_EARTH_MAGIC:
572  case SK_AIR_MAGIC:
573  case SK_FIRE_MAGIC:
574  case SK_WATER_MAGIC:
576  "This skill is already in effect.");
577  break;
578 
579  case SK_HARVESTING:
580  do_harvest(op, dir, skill);
581  success = 0;
582  break;
583 
584  default: {
585  char name[MAX_BUF];
586 
587  query_name(op, name, MAX_BUF);
588  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
589  break;
590  }
591  }
592 
593  /* For players we now update the speed_left from using the skill.
594  * Monsters have no skill use time because of the random nature in
595  * which use_monster_skill is called already simulates this.
596  * If certain skills should take more/less time, that should be
597  * in the code for the skill itself.
598  */
599 
600  if (op->type == PLAYER)
601  op->speed_left -= 1.0;
602 
603  /* this is a good place to add experience for successful use of skills.
604  * Note that add_exp() will figure out player/monster experience
605  * gain problems.
606  */
607 
608  if (success && exp)
609  change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
610 
611  return success;
612 }
613 
646 int64_t calc_skill_exp(const object *who, const object *op, const object *skill) {
647  int64_t op_exp;
648  int op_lvl;
649  float base, value, lvl_mult;
650 
651  if (!skill)
652  skill = who;
653 
654  /* Oct 95 - where we have an object, I expanded our treatment
655  * to 3 cases:
656  * non-living magic obj, runes and everything else.
657  *
658  * If an object is not alive and magical we set the base exp higher to
659  * help out exp awards for skill_ident skills. Also, if
660  * an item is type RUNE, we give out exp based on stats.Cha
661  * and level (this was the old system) -b.t.
662  */
663 
664  if (!op) { /* no item/creature */
665  op_lvl = MAX(who->map->difficulty, 1);
666  op_exp = 0;
667  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
668  * for the amount of experience */
669  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
670  op_lvl = op->level;
671  } else { /* all other items/living creatures */
672  op_exp = op->stats.exp;
673  op_lvl = op->level;
674  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
675  op_lvl += 5*abs(op->magic);
676  }
677  }
678 
679  if (op_lvl < 1)
680  op_lvl = 1;
681 
682  if (who->type != PLAYER) { /* for monsters only */
683  return ((int64_t)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
684  }
685 
686  /* for players */
687  base = op_exp;
688  /* if skill really is a skill, then we can look at the skill archetype for
689  * base reward value (exp) and level multiplier factor.
690  */
691  if (skill->type == SKILL) {
692  base += skill->arch->clone.stats.exp;
693  if (settings.simple_exp) {
694  if (skill->arch->clone.level)
695  lvl_mult = (float)skill->arch->clone.level/100.0;
696  else
697  lvl_mult = 1.0; /* no adjustment */
698  } else {
699  if (skill->level)
700  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
701  else
702  lvl_mult = 1.0;
703  }
704  } else {
705  /* Don't divide by zero here! */
706  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
707  }
708 
709  /* assemble the exp total, and return value */
710 
711  value = base*lvl_mult;
712  if (value < 1)
713  value = 1; /* Always give at least 1 exp point */
714 
715 #ifdef SKILL_UTIL_DEBUG
716  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
717 #endif
718  return ((int64_t)value);
719 }
720 
738 int learn_skill(object *pl, object *scroll) {
739  object *tmp;
740 
741  if (!scroll->skill) {
742  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
743  return 2;
744  }
745 
746  /* can't use find_skill_by_name because we want skills the player knows
747  * but can't use natively.
748  */
749 
750  tmp = NULL;
751  FOR_INV_PREPARE(pl, inv)
752  if (inv->type == SKILL && !strncasecmp(scroll->skill, inv->skill, strlen(scroll->skill))) {
753  tmp = inv;
754  break;
755  }
756  FOR_INV_FINISH();
757 
758  /* player already knows it */
759  if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
760  return 0;
761 
762  /* now a random change to learn, based on player Int.
763  * give bonus based on level - otherwise stupid characters
764  * might never be able to learn anything.
765  */
766  if (random_roll(0, 99, pl, PREFER_LOW) > (get_learn_spell(pl->stats.Int)+(pl->level/5)))
767  return 2; /* failure :< */
768 
769  if (!tmp)
770  tmp = give_skill_by_name(pl, scroll->skill);
771 
772  if (!tmp) {
773  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
774  return 2;
775  }
776 
778  link_player_skills(pl);
779  return 1;
780 }
781 
793 static int clipped_percent(int64_t a, int64_t b) {
794  int rv;
795 
796  if (b <= 0)
797  return 0;
798 
799  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
800 
801  if (rv < 0)
802  return 0;
803  else if (rv > 100)
804  return 100;
805 
806  return rv;
807 }
808 
809 
818 static int digits_in_long(int64_t num)
819 {
820  return num == 0 ? 1 : floor( log10( labs(num) ) ) + 1;
821 }
822 
839 void show_skills(object *op, const char *parms) {
840  const char *cp;
841  int i, num_skills_found = 0;
842  /* Need to have a pointer and use strdup for qsort to work properly */
843  char skills[MAX_SKILLS][MAX_BUF];
844  const char *search = parms;
845  bool long_format = false;
846 
847  if ( parms && strncmp(parms,"-l",2) == 0 ) {
848  long_format = true;
849  search += 2;
850  while ( *search && *search != ' ' ) ++search; /* move past other parameters */
851  while ( *search == ' ' ) ++search;
852  }
853  FOR_INV_PREPARE(op, tmp) {
854  if (tmp->type == SKILL) {
855  if (search && strstr(tmp->name, search) == NULL)
856  continue;
857 
859  if ( long_format ) {
860  int perm_level = exp_level(tmp->total_exp * settings.permanent_exp_ratio / 100);
861  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%"FMT64"/%"FMT64")%-*sperm lvl:%3d (xp:%"FMT64"/%"FMT64") ",
862  tmp->name, tmp->level,
863  tmp->stats.exp,
864  level_exp(tmp->level+1, op->expmul),
865  1+2*digits_in_long(op->stats.exp)-digits_in_long(tmp->stats.exp)-digits_in_long(level_exp(tmp->level+1, op->expmul)),
866  "",
867  perm_level,
868  tmp->total_exp * settings.permanent_exp_ratio / 100,
869  level_exp(perm_level+1, op->expmul));
870  } else {
871  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%"FMT64"/%"FMT64"/%d%%)",
872  tmp->name, tmp->level,
873  tmp->stats.exp,
874  level_exp(tmp->level+1, op->expmul),
875  clipped_percent(PERM_EXP(tmp->total_exp), tmp->stats.exp));
876  }
877  } else {
878  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%"FMT64"/%"FMT64")",
879  tmp->name, tmp->level,
880  tmp->stats.exp,
881  level_exp(tmp->level+1, op->expmul));
882  }
883  /* I don't know why some characters get a bunch of skills, but
884  * it sometimes happens (maybe a leftover from buggier earlier code
885  * and those character are still about). In any case, lets handle
886  * it so it doesn't crash the server - otherwise, one character may
887  * crash the server numerous times.
888  */
889  if (num_skills_found >= MAX_SKILLS) {
891  "Your character has too many skills.\n"
892  "Something isn't right - contact the server admin");
893  break;
894  }
895  }
896  } FOR_INV_FINISH();
897 
898  if ( search && *search ) {
900  "Player skills%s: (matching '%s')", long_format ? " (long format)":"",search);
901  } else {
903  "Player skills%s:", long_format ? " (long format)":"");
904  }
905 
906  if (num_skills_found > 1)
907  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
908 
909  for (i = 0; i < num_skills_found; i++) {
911  skills[i]);
912  }
913 
914  cp = determine_god(op);
915  if (strcmp(cp, "none") == 0)
916  cp = NULL;
917 
919  "You can handle %d weapon improvements.\n"
920  "You worship %s.\n"
921  "Your equipped item power is %d out of %d\n",
922  op->level/5+5,
923  cp ? cp : "no god at current time",
924  op->contr->item_power, op->level);
925 }
926 
943 int use_skill(object *op, const char *string) {
944  object *skop;
945  size_t len;
946 
947  if (!string)
948  return 0;
949 
950  skop = NULL;
951  FOR_INV_PREPARE(op, tmp) {
952  if (tmp->type == SKILL
954  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
955  skop = tmp;
956  break;
957  } else if (tmp->type == SKILL_TOOL
958  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
959  skop = tmp;
960  break;
961  }
962  } FOR_INV_FINISH();
963  if (!skop) {
965  "Unable to find skill %s",
966  string);
967  return 0;
968  }
969 
970  len = strlen(skop->skill);
971 
972  /* All this logic goes and skips over the skill name to find any
973  * options given to the skill. Its pretty simple - if there
974  * are extra parameters (as determined by string length), we
975  * want to skip over any leading spaces.
976  */
977  if (len >= strlen(string)) {
978  string = NULL;
979  } else {
980  string += len;
981  while (*string == 0x20)
982  string++;
983  if (strlen(string) == 0)
984  string = NULL;
985  }
986 
987 #ifdef SKILL_UTIL_DEBUG
988  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
989 #endif
990 
991  /* Change to the new skill, then execute it. */
992  if (do_skill(op, op, skop, op->facing, string))
993  return 1;
994 
995  return 0;
996 }
997 
1020 static object *find_best_player_hth_skill(object *op) {
1021  object *best_skill = NULL;
1022  int last_skill;
1023 
1024  if (op->contr->unarmed_skill) {
1025  /* command_unarmed_skill() already does these checks, and right
1026  * now I do not think there is any way to lose unarmed skills.
1027  * But maybe in the future there will be (polymorph?) so handle
1028  * it appropriately. MSW 2009-07-03
1029  *
1030  * Note that the error messages should only print out once when
1031  * the initial failure to switch skills happens, so the player
1032  * should not get spammed with tons of messages unless they have
1033  * no valid unarmed skill
1034  */
1035 
1036  best_skill = find_skill_by_name(op, op->contr->unarmed_skill);
1037 
1038  if (!best_skill) {
1040  "Unable to find skill %s - using default unarmed skill",
1041  op->contr->unarmed_skill);
1042  } else {
1043  size_t i;
1044 
1045  for (i = 0; i < sizeof(unarmed_skills); i++)
1046  if (best_skill->subtype == unarmed_skills[i])
1047  break;
1048  if (i < sizeof(unarmed_skills))
1049  return(best_skill);
1050  }
1051  /* If for some reason the unarmed_skill is not valid, we fall
1052  * through the processing below.
1053  */
1054  }
1055 
1056 
1057  /* Dragons are a special case - gros 25th July 2006 */
1058  /* Perhaps this special case should be removed and unarmed_skill
1059  * set to clawing for dragon characters? MSW 2009-07-03
1060  */
1061  if (is_dragon_pl(op)) {
1062  object *tmp;
1063 
1064  tmp = find_skill_by_number(op, SK_CLAWING);
1065  if (tmp) /* I suppose it should always be true - but maybe there's
1066  * draconic toothache ? :) */
1067  return tmp;
1068  }
1069 
1070  last_skill = sizeof(unarmed_skills);
1071  FOR_INV_PREPARE(op, tmp) {
1072  if (tmp->type == SKILL) {
1073  int i;
1074 
1075  /* The order in the array is preferred order. So basically,
1076  * we just cut down the number to search - eg, if we find a skill
1077  * early on in flame touch, then we only need to look into the unarmed_array
1078  * to the entry before flame touch - don't care about the entries afterward,
1079  * because they are inferior skills.
1080  * if we end up finding the best skill (i==0) might as well return
1081  * right away - can't get any better than that.
1082  */
1083  for (i = 0; i < last_skill; i++) {
1084  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
1085  best_skill = tmp;
1086  last_skill = i;
1087  if (i == 0)
1088  return best_skill;
1089  }
1090  }
1091  }
1092  } FOR_INV_FINISH();
1093  return best_skill;
1094 }
1095 
1109 static void do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
1110  int success;
1111 
1112  /* For Players only: if there is no ready weapon, and no "attack" skill
1113  * is readied either then try to find a skill for the player to use.
1114  * it is presumed that if skill is set, it is a valid attack skill (eg,
1115  * the caller should have set it appropriately). We still want to pass
1116  * through that code if skill is set to change to the skill.
1117  */
1118  if (op->type == PLAYER) {
1119  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1120  size_t i;
1121 
1122  if (!skill) {
1123  /* See if the players chosen skill is a combat skill, and use
1124  * it if appropriate.
1125  */
1126  if (op->chosen_skill) {
1127  /* the list is 0-terminated, and talismans, which can be in chosen_skill,
1128  * have a subtype of 0, therefore don't check the 0 */
1129  for (i = 0; unarmed_skills[i] != 0; i++)
1130  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1131  skill = op->chosen_skill;
1132  break;
1133  }
1134  }
1135  /* If we didn't find a skill above, look harder for a good skill */
1136  if (!skill) {
1137  skill = find_best_player_hth_skill(op);
1138 
1139  if (!skill) {
1140  draw_ext_info(NDI_BLACK, 0, op,
1142  "You have no unarmed combat skills!");
1143  return;
1144  }
1145  }
1146  }
1147  if (skill != op->chosen_skill) {
1148  /* now try to ready the new skill */
1149  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1152  "Couldn't change to skill %s",
1153  skill->name);
1154  return;
1155  }
1156  }
1157  } else {
1158  /* Seen some crashes below where current_weapon is not set,
1159  * even though the flag says it is. So if current weapon isn't set,
1160  * do some work in trying to find the object to use.
1161  */
1162  if (!op->current_weapon) {
1163  object *tmp;
1164 
1165  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1167  if (!tmp) {
1168  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1169  op->current_weapon = NULL;
1170  return;
1171  } else {
1172  char weapon[MAX_BUF];
1173 
1174  query_name(tmp, weapon, MAX_BUF);
1175  op->current_weapon = tmp;
1176  }
1177  }
1178 
1179  /* Has ready weapon - make sure chosen_skill is set up properly */
1180  if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
1181  object *found_skill = find_skill_by_name(op, op->current_weapon->skill);
1182  assert(found_skill != NULL);
1183  change_skill(op, found_skill, 1);
1184  }
1185  }
1186  }
1187 
1188  /* lose invisibility/hiding status for running attacks */
1189 
1190  if (op->type == PLAYER && op->contr->tmp_invis) {
1191  op->contr->tmp_invis = 0;
1192  op->invisible = 0;
1193  op->hide = 0;
1195  }
1196 
1197  success = attack_ob(tmp, op);
1198 
1199  /*
1200  * print appropriate messages to the player
1201  *
1202  * If no string, then we aren't dealing with the
1203  * skill attack directly, so the message is printed elsewhere.
1204  *
1205  * Only print hit/miss if we've got a Fire+Attack.
1206  * Otherwise, don't print here at all.
1207  */
1208 
1209  if (string != NULL && tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1210  char op_name[MAX_BUF];
1211  if (success){
1212  if (op->type == PLAYER) {
1213  query_name(tmp, op_name, MAX_BUF);
1216  "You %s %s!",
1217  string, op_name);
1218  } else if (tmp->type == PLAYER) {
1219  query_name(op, op_name, MAX_BUF);
1222  "%s %s you!",
1223  op_name, string);
1224  }
1225  }
1226  else{
1227  query_name(tmp, op_name, MAX_BUF);
1230  "You miss %s!",
1231  op_name);
1232  }
1233  }
1234 }
1235 
1259 void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1260  int16_t tx, ty;
1261  mapstruct *m;
1262  int mflags;
1263 
1264  if (!dir)
1265  dir = pl->facing;
1266  tx = freearr_x[dir];
1267  ty = freearr_y[dir];
1268 
1269  /* If we don't yet have an opponent, find if one exists, and attack.
1270  * Legal opponents are the same as outlined in move_player_attack()
1271  */
1272 
1273  if (tmp == NULL) {
1274  m = pl->map;
1275  tx = pl->x+freearr_x[dir];
1276  ty = pl->y+freearr_y[dir];
1277 
1278  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1279  if (mflags&P_OUT_OF_MAP)
1280  return;
1281 
1282  /* space must be blocked for there to be anything interesting to do */
1283  if (!(mflags&P_IS_ALIVE)
1284  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1285  return;
1286  }
1287 
1288  FOR_MAP_PREPARE(m, tx, ty, tmp2)
1289  if ((QUERY_FLAG(tmp2, FLAG_ALIVE) && tmp2->stats.hp >= 0)
1290  || QUERY_FLAG(tmp2, FLAG_CAN_ROLL)
1291  || tmp2->type == LOCKED_DOOR) {
1292  /* Don't attack party members */
1293  if ((pl->type == PLAYER && tmp2->type == PLAYER)
1294  && (pl->contr->party != NULL && pl->contr->party == tmp2->contr->party))
1295  return;
1296  tmp = tmp2;
1297  break;
1298  }
1299  FOR_MAP_FINISH();
1300  }
1301  if (!tmp) {
1302  if (pl->type == PLAYER)
1304  "There is nothing to attack!");
1305  return;
1306  }
1307 
1308  do_skill_attack(tmp, pl, string, skill);
1309 }
1310 
1329 static void attack_hth(object *pl, int dir, const char *string, object *skill) {
1330  object *weapon;
1331 
1332  if (QUERY_FLAG(pl, FLAG_READY_WEAPON)) {
1333  weapon = object_find_by_type_applied(pl, WEAPON);
1334  if (weapon != NULL) {
1335  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1336  char weaponname[MAX_BUF];
1337 
1338  query_name(weapon, weaponname, MAX_BUF);
1341  "You are unable to unwield %s in order to attack with %s.",
1342  weaponname, skill->name);
1343  return;
1344  } else {
1346  "You unwield your weapon in order to attack.");
1347  }
1348  }
1349  }
1350  skill_attack(NULL, pl, dir, string, skill);
1351 }
1352 
1373 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1374  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1375  if (op->type == PLAYER)
1377  "You have no ready weapon to attack with!");
1378  return;
1379  }
1380  skill_attack(NULL, op, dir, string, skill);
1381 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:316
#define AP_UNAPPLY
Definition: define.h:613
int8_t Int
Definition: living.h:36
Definition: player.h:92
int apply_special(object *who, object *op, int aflags)
Definition: apply.c:1078
const char * determine_god(object *op)
Definition: gods.c:106
#define UP_OBJ_FACE
Definition: object.h:519
int skill_ident(object *pl, object *skill)
Definition: skills.c:896
int64_t level_exp(int level, double expmul)
Definition: living.c:1868
static void attack_melee_weapon(object *op, int dir, const char *string, object *skill)
Definition: skill_util.c:1373
#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:3993
static int digits_in_long(int64_t num)
Definition: skill_util.c:818
void link_player_skills(object *op)
Definition: player.c:279
static int clipped_percent(int64_t a, int64_t b)
Definition: skill_util.c:793
int attack_ob(object *op, object *hitter)
Definition: attack.c:905
int exp_level(int64_t exp)
Definition: living.c:1880
const char * skill_names[MAX_SKILLS]
Definition: skill_util.c:58
void fatal(enum fatal_error err)
Definition: utils.c:597
int shop_describe(const object *op)
Definition: shop.c:1169
int use_skill(object *op, const char *string)
Definition: skill_util.c:943
object clone
Definition: object.h:472
int16_t invisible
Definition: object.h:361
short freearr_x[SIZEOFFREE]
Definition: object.c:65
#define PREFER_LOW
Definition: define.h:602
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:387
static object * adjust_skill_tool(object *who, object *skill, object *skill_tool)
Definition: skill_util.c:130
rangetype shoottype
Definition: player.h:99
Definition: object.h:240
Definition: object.h:119
int change_skill(object *who, object *new_skill, int flag)
Definition: skill_util.c:340
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:582
object * ranges[range_size]
Definition: player.h:103
uint8_t subtype
Definition: object.h:340
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Definition: skill_util.c:412
uint8_t hide
Definition: object.h:389
int64_t exp
Definition: living.h:47
double expmul
Definition: object.h:397
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:583
int jump(object *pl, int dir, object *skill)
Definition: skills.c:659
int remove_trap(object *op, object *skill)
Definition: skills.c:1284
#define MAX(x, y)
Definition: compat.h:20
void object_update(object *op, int action)
Definition: object.c:1260
int skill_faces[MAX_SKILLS]
Definition: skill_util.c:62
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:646
int16_t sp
Definition: living.h:42
#define NDI_BLACK
Definition: newclient.h:221
#define AP_APPLY
Definition: define.h:612
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:311
Definition: object.h:467
#define MIN(x, y)
Definition: compat.h:17
#define FLAG_CAN_ROLL
Definition: define.h:254
short freearr_y[SIZEOFFREE]
Definition: object.c:71
partylist * party
Definition: player.h:186
uint16_t number
Definition: face.h:15
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1773
int use_alchemy(object *op)
Definition: alchemy.c:1034
#define FLAG_READY_SKILL
Definition: define.h:334
Definition: object.h:241
struct obj * chosen_skill
Definition: object.h:388
int16_t y
Definition: object.h:326
#define NDI_RED
Definition: newclient.h:224
int skill_throw(object *op, object *part, int dir, object *skill)
Definition: skills.c:2245
#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
float speed_left
Definition: object.h:329
signed short int16_t
Definition: win32.h:160
#define FLAG_CAN_USE_SKILL
Definition: define.h:322
const char * anim_suffix
Definition: object.h:316
uint32_t tmp_invis
Definition: player.h:125
int hide(object *op, object *skill)
Definition: skills.c:492
struct mapdef * map
Definition: object.h:297
int is_dragon_pl(const object *op)
Definition: player.c:114
#define snprintf
Definition: win32.h:46
#define PERM_EXP(exptotal)
Definition: global.h:235
static void attack_hth(object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1329
#define FOR_INV_FINISH()
Definition: define.h:714
void show_skills(object *op, const char *parms)
Definition: skill_util.c:839
#define FMT64
Definition: compat.h:12
const char * name
Definition: object.h:311
#define MSG_TYPE_VICTIM_WAS_HIT
Definition: newclient.h:647
int steal(object *op, int dir, object *skill)
Definition: skills.c:277
struct obj * current_weapon
Definition: object.h:372
int8_t Cha
Definition: living.h:36
#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:251
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:192
#define MSG_TYPE_ATTACK_MISS
Definition: newclient.h:615
static object * find_best_player_hth_skill(object *op)
Definition: skill_util.c:1020
struct pl * contr
Definition: object.h:276
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:585
uint8_t simple_exp
Definition: global.h:265
void do_harvest(object *pl, int dir, object *skill)
Definition: c_misc.c:2306
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:2162
#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
int16_t x
Definition: object.h:326
int strncasecmp(const char *s1, const char *s2, int n)
Definition: porting.c:224
const char * skill
Definition: object.h:321
void init_skills(void)
Definition: skill_util.c:68
int learn_skill(object *pl, object *scroll)
Definition: skill_util.c:738
uint16_t difficulty
Definition: map.h:343
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:260
Definition: object.h:107
int get_skill_client_code(const char *skill_name)
Definition: skill_util.c:97
int pray(object *pl, object *skill)
Definition: skills.c:1357
object * object_find_by_type_and_skill(const object *who, int type, const char *skill)
Definition: object.c:4093
Definition: object.h:143
living stats
Definition: object.h:369
struct archt * arch
Definition: object.h:414
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1120
uint8_t type
Definition: object.h:339
struct Settings settings
Definition: init.c:39
struct archt * next
Definition: object.h:469
rangetype
Definition: player.h:15
#define FLAG_APPLIED
Definition: define.h:235
int find_traps(object *pl, object *skill)
Definition: skills.c:1213
#define MSG_TYPE_SKILL
Definition: newclient.h:383
char * strdup(const char *str)
Definition: porting.c:200
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.c:310
#define NDI_UNIQUE
Definition: newclient.h:245
void apply_anim_suffix(object *who, sstring suffix)
Definition: anim.c:319
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.c:966
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
int16_t item_power
Definition: player.h:117
int get_learn_spell(int stat)
Definition: living.c:2359
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:192
#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:623
#define MAX_SKILLS
Definition: skills.h:70
#define SK_SUBTRACT_SKILL_EXP
Definition: skills.h:81
Definition: map.h:325
#define P_IS_ALIVE
Definition: map.h:237
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
const New_Face * face
Definition: object.h:332
void clear_skill(object *who)
Definition: skill_util.c:379
object * find_skill_by_number(object *who, int skillno)
Definition: skill_util.c:295
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1736
const char * unarmed_skill
Definition: player.h:202
int16_t level
Definition: object.h:352
int8_t facing
Definition: object.h:336
void fix_object(object *op)
Definition: living.c:1120
void meditate(object *pl, object *skill)
Definition: skills.c:1405
EXTERN archetype * first_archetype
Definition: global.h:122
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1259
int8_t magic
Definition: object.h:349
#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:1109
#define FLAG_FREED
Definition: define.h:233