Crossfire Server, Trunk  R21226
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 
44 #include "living.h"
45 #include "object.h"
46 #include "shop.h"
47 #include "skills.h"
48 #include "spells.h"
49 #include "sproto.h"
50 
51 static void attack_hth(object *pl, int dir, const char *string, object *skill);
52 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill);
53 
57 const char *skill_names[MAX_SKILLS];
62 
67 void init_skills(void) {
68  int i;
69  archetype *at;
70 
71  for (i = 0; i < MAX_SKILLS; i++) {
72  skill_names[i] = NULL;
73  skill_faces[i] = -1;
74  }
75  i = 0;
76 
77  for (at = first_archetype; at != NULL; at = at->next) {
78  if (at->clone.type == SKILL) {
79  if (i == MAX_SKILLS) {
80  LOG(llevError, "init_skills: too many skills, increase MAX_SKILLS and rebuild server!");
82  }
84  if (at->clone.face != NULL)
85  skill_faces[i] = at->clone.face->number;
86  i++;
87  }
88  }
89 }
90 
96 int get_skill_client_code(const char *skill_name)
97 {
98  int index;
99  for (index = 0; index < MAX_SKILLS && skill_names[index] != NULL; index++)
100  if (strcmp(skill_names[index], skill_name) == 0)
101  return index;
102 
103  assert("invalid skill!");
104  return 0;
105 }
106 
129 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
130  if (!skill && !skill_tool)
131  return NULL;
132 
133  /* If this is a skill that can be used without a tool and no tool found, return it */
134  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
135  return skill;
136 
137  /* Player has a tool to use the skill. If not applied, apply it -
138  * if not successful, return skill if can be used. If they do have the skill tool
139  * but not the skill itself, give it to them.
140  */
141  if (skill_tool) {
142  if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
143  if (apply_special(who, skill_tool, 0)) {
144  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
145  return skill;
146  else
147  return NULL;
148  }
149  }
150  if (!skill) {
151  skill = give_skill_by_name(who, skill_tool->skill);
152  link_player_skills(who);
153  }
154  return skill;
155  }
156  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
157  return skill;
158  else
159  return NULL;
160 }
161 
191 object *find_skill_by_name(object *who, const char *name) {
192  object *skill = NULL, *skills[MAX_SKILLS], *skill_tools[MAX_SKILLS];
193  const char *skill_names[MAX_SKILLS];
194  char *ourname=NULL;
195  int num_names, highest_level_skill=0, i;
196 
197  if (!name)
198  return NULL;
199 
200  /* Simple case - no commas in past in name, so don't need to tokenize */
201  if (!strchr(name, ',')) {
202  skill_names[0] = name;
203  skill_tools[0] = NULL;
204  skills[0] = NULL;
205  num_names = 1;
206  } else {
207  /* strtok_r is destructive, so we need our own copy */
208  char *lasts;
209  ourname = strdup(name);
210 
211  if ((skill_names[0] = strtok_r(ourname, ",", &lasts)) == NULL) {
212  /* This should really never happen */
213  LOG(llevError, "find_skill_by_name: strtok_r returned null, but strchr did not?\n");
214  free(ourname);
215  return NULL;
216  } else {
217  skill_tools[0] = NULL;
218  skills[0] = NULL;
219  /* we already have the first name from the strtok_r above */
220  num_names=1;
221  while ((skill_names[num_names] = strtok_r(NULL, ",", &lasts)) != NULL) {
222  /* Clean out any leading spacing. typical string would be
223  * skill1, skill2, skill3, ...
224  */
225  while (isspace(*skill_names[num_names]))
226  skill_names[num_names]++;
227  skills[num_names] = NULL;
228  skill_tools[num_names] = NULL;
229  num_names++;
230  }
231  }
232  /* While we don't use ourname below this point, the skill_names[] points into
233  * it, so we can't free it yet.
234  */
235  }
236 
237  FOR_INV_PREPARE(who, tmp) {
238  /* We make sure the length of the string in the object is greater
239  * in length than the passed string. Eg, if we have a skill called
240  * 'hi', we don't want to match if the user passed 'high'
241  */
242  if (tmp->type == SKILL || tmp->type == SKILL_TOOL) {
243  for (i = 0; i<num_names; i++) {
244  if (!strncasecmp(skill_names[i], tmp->skill, strlen(skill_names[i])) &&
245  strlen(tmp->skill) >= strlen(skill_names[i])) {
246  if (tmp->type == SKILL) {
247  skills[i] = tmp;
248  if (!skill || tmp->level > skill->level) {
249  skill = tmp;
250  highest_level_skill=i;
251  }
252  }
253  else {
254  /* Skill tools don't have levels, so we basically find the
255  * 'best' skill tool for this skill.
256  */
257  if (QUERY_FLAG(tmp, FLAG_APPLIED) || !skill_tools[i] ||
258  !QUERY_FLAG(skill_tools[i], FLAG_APPLIED)) {
259  skill_tools[i] = tmp;
260  }
261  }
262  /* Got a matching name - no reason to look through rest of names */
263  break;
264  }
265  }
266  }
267  } FOR_INV_FINISH();
268  free(ourname);
269  return adjust_skill_tool(who, skills[highest_level_skill], skill_tools[highest_level_skill]);
270 }
271 
294 object *find_skill_by_number(object *who, int skillno) {
295  object *skill = NULL, *skill_tool = NULL;
296 
297  if (skillno <= 0 || skillno > MAX_SKILLS) {
298  return NULL;
299  }
300 
301  FOR_INV_PREPARE(who, tmp) {
302  if (tmp->type == SKILL && tmp->subtype == skillno)
303  skill = tmp;
304 
305  /* Try to find appropriate skilltool. If the player has one already
306  * applied, we try to keep using that one.
307  */
308  else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
309  if (QUERY_FLAG(tmp, FLAG_APPLIED))
310  skill_tool = tmp;
311  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
312  skill_tool = tmp;
313  }
314  } FOR_INV_FINISH();
315 
316  return adjust_skill_tool(who, skill, skill_tool);
317 }
318 
339 int change_skill(object *who, object *new_skill, int flag) {
340  rangetype old_range;
341 
342  if (who->type != PLAYER)
343  return 0;
344 
345  old_range = who->contr->shoottype;
346 
347  /* The readied skill can be a skill tool, so check on actual skill instead of object. */
348  if (who->chosen_skill && who->chosen_skill->skill == new_skill->skill) {
349  /* optimization for changing skill to current skill */
350  if (!(flag&0x1))
351  who->contr->shoottype = range_skill;
352  return 1;
353  }
354 
355  if (who->chosen_skill)
356  apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
357 
358  /* Only goal in this case was to unapply a skill */
359  if (!new_skill)
360  return 0;
361 
362  if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
363  return 0;
364  }
365  if (flag&0x1)
366  who->contr->shoottype = old_range;
367 
368  return 1;
369 }
370 
378 void clear_skill(object *who) {
379  who->chosen_skill = NULL;
381  if (who->type == PLAYER) {
382  who->contr->ranges[range_skill] = NULL;
383  if (who->contr->shoottype == range_skill)
384  who->contr->shoottype = range_none;
385  }
386 }
387 
411 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
412  int success = 0, exp = 0;
413 
414  if (!skill)
415  return 0;
416 
417  /* The code below presumes that the skill points to the object that
418  * holds the exp, level, etc of the skill. So if this is a player
419  * go and try to find the actual real skill pointer, and if the
420  * the player doesn't have a bucket for that, create one.
421  */
422  if (skill->type != SKILL && op->type == PLAYER) {
423  object *tmp;
424 
425  tmp = object_find_by_type_and_skill(op, SKILL, skill->skill);
426  if (!tmp) {
427  tmp = give_skill_by_name(op, skill->skill);
428  if (!tmp) {
429  LOG(llevError, "do_skill: asked for skill %s but couldn't find matching SKILL archetype.\n", skill->skill);
430  return 0;
431  }
432  }
433  skill = tmp;
434  }
435 
436  if (skill->anim_suffix)
437  apply_anim_suffix(op, skill->anim_suffix);
438 
439  switch (skill->subtype) {
440  case SK_LEVITATION:
441  /* Not 100% sure if this will work with new movement code -
442  * the levitation skill has move_type for flying, so when
443  * equipped, that should transfer to player, when not,
444  * shouldn't.
445  */
446  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
447  CLEAR_FLAG(skill, FLAG_APPLIED);
449  "You come to earth.");
450  } else {
451  SET_FLAG(skill, FLAG_APPLIED);
453  "You rise into the air!.");
454  }
455  fix_object(op);
456  success = 1;
457  break;
458 
459  case SK_STEALING:
460  exp = success = steal(op, dir, skill);
461  break;
462 
463  case SK_LOCKPICKING:
464  exp = success = pick_lock(op, dir, skill);
465  break;
466 
467  case SK_HIDING:
468  exp = success = hide(op, skill);
469  break;
470 
471  case SK_JUMPING:
472  success = jump(op, dir, skill);
473  break;
474 
475  case SK_INSCRIPTION:
476  exp = success = write_on_item(op, string, skill);
477  break;
478 
479  case SK_MEDITATION:
480  meditate(op, skill);
481  success = 1;
482  break;
483  /* note that the following 'attack' skills gain exp through hit_player() */
484 
485  case SK_KARATE:
486  attack_hth(op, dir, "karate-chopped", skill);
487  break;
488 
489  case SK_PUNCHING:
490  attack_hth(op, dir, "punched", skill);
491  break;
492 
493  case SK_FLAME_TOUCH:
494  attack_hth(op, dir, "flamed", skill);
495  break;
496 
497  case SK_CLAWING:
498  attack_hth(op, dir, "clawed", skill);
499  break;
500 
501  case SK_WRAITH_FEED:
502  attack_hth(op, dir, "fed upon", skill);
503  break;
504 
507  (void)attack_melee_weapon(op, dir, NULL, skill);
508  break;
509 
510  case SK_FIND_TRAPS:
511  exp = success = find_traps(op, skill);
512  break;
513 
514  case SK_SINGING:
515  exp = success = singing(op, dir, skill);
516  break;
517 
518  case SK_ORATORY:
519  exp = success = use_oratory(op, dir, skill);
520  break;
521 
522  case SK_SMITHERY:
523  case SK_BOWYER:
524  case SK_JEWELER:
525  case SK_ALCHEMY:
526  case SK_THAUMATURGY:
527  case SK_LITERACY:
528  case SK_WOODSMAN:
529  if (use_alchemy(op) == 0)
530  exp = success = skill_ident(op, skill);
531  break;
532 
533  case SK_DET_MAGIC:
534  case SK_DET_CURSE:
535  exp = success = skill_ident(op, skill);
536  break;
537 
538  case SK_DISARM_TRAPS:
539  exp = success = remove_trap(op, skill);
540  break;
541 
542  case SK_THROWING:
543  success = skill_throw(op, part, dir, skill);
544  break;
545 
546  case SK_SET_TRAP:
548  "This skill is not currently implemented.");
549  break;
550 
551  case SK_USE_MAGIC_ITEM:
552  case SK_MISSILE_WEAPON:
554  "There is no special attack for this skill.");
555  break;
556 
557  case SK_PRAYING:
558  success = pray(op, skill);
559  break;
560 
561  case SK_BARGAINING:
562  success = shop_describe(op);
563  break;
564 
565  case SK_SORCERY:
566  case SK_EVOCATION:
567  case SK_PYROMANCY:
568  case SK_SUMMONING:
569  case SK_CLIMBING:
570  case SK_EARTH_MAGIC:
571  case SK_AIR_MAGIC:
572  case SK_FIRE_MAGIC:
573  case SK_WATER_MAGIC:
575  "This skill is already in effect.");
576  break;
577 
578  case SK_HARVESTING:
579  do_harvest(op, dir, skill);
580  success = 0;
581  break;
582 
583  default: {
584  char name[MAX_BUF];
585 
586  query_name(op, name, MAX_BUF);
587  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
588  break;
589  }
590  }
591 
592  /* For players we now update the speed_left from using the skill.
593  * Monsters have no skill use time because of the random nature in
594  * which use_monster_skill is called already simulates this.
595  * If certain skills should take more/less time, that should be
596  * in the code for the skill itself.
597  */
598 
599  if (op->type == PLAYER)
600  op->speed_left -= 1.0;
601 
602  /* this is a good place to add experience for successful use of skills.
603  * Note that add_exp() will figure out player/monster experience
604  * gain problems.
605  */
606 
607  if (success && exp)
608  change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
609 
610  return success;
611 }
612 
645 int64_t calc_skill_exp(const object *who, const object *op, const object *skill) {
646  int64_t op_exp;
647  int op_lvl;
648  float base, value, lvl_mult;
649 
650  if (!skill)
651  skill = who;
652 
653  /* Oct 95 - where we have an object, I expanded our treatment
654  * to 3 cases:
655  * non-living magic obj, runes and everything else.
656  *
657  * If an object is not alive and magical we set the base exp higher to
658  * help out exp awards for skill_ident skills. Also, if
659  * an item is type RUNE, we give out exp based on stats.Cha
660  * and level (this was the old system) -b.t.
661  */
662 
663  if (!op) { /* no item/creature */
664  op_lvl = MAX(who->map->difficulty, 1);
665  op_exp = 0;
666  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
667  * for the amount of experience */
668  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
669  op_lvl = op->level;
670  } else { /* all other items/living creatures */
671  op_exp = op->stats.exp;
672  op_lvl = op->level;
673  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
674  op_lvl += 5*abs(op->magic);
675  }
676  }
677 
678  if (op_lvl < 1)
679  op_lvl = 1;
680 
681  if (who->type != PLAYER) { /* for monsters only */
682  return ((int64_t)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
683  }
684 
685  /* for players */
686  base = op_exp;
687  /* if skill really is a skill, then we can look at the skill archetype for
688  * base reward value (exp) and level multiplier factor.
689  */
690  if (skill->type == SKILL) {
691  base += skill->arch->clone.stats.exp;
692  if (settings.simple_exp) {
693  if (skill->arch->clone.level)
694  lvl_mult = (float)skill->arch->clone.level/100.0;
695  else
696  lvl_mult = 1.0; /* no adjustment */
697  } else {
698  if (skill->level)
699  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
700  else
701  lvl_mult = 1.0;
702  }
703  } else {
704  /* Don't divide by zero here! */
705  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
706  }
707 
708  /* assemble the exp total, and return value */
709 
710  value = base*lvl_mult;
711  if (value < 1)
712  value = 1; /* Always give at least 1 exp point */
713 
714 #ifdef SKILL_UTIL_DEBUG
715  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
716 #endif
717  return ((int64_t)value);
718 }
719 
737 int learn_skill(object *pl, object *scroll) {
738  object *tmp;
739 
740  if (!scroll->skill) {
741  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
742  return 2;
743  }
744 
745  /* can't use find_skill_by_name because we want skills the player knows
746  * but can't use natively.
747  */
748 
749  tmp = NULL;
750  FOR_INV_PREPARE(pl, inv)
751  if (inv->type == SKILL && !strncasecmp(scroll->skill, inv->skill, strlen(scroll->skill))) {
752  tmp = inv;
753  break;
754  }
755  FOR_INV_FINISH();
756 
757  /* player already knows it */
758  if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
759  return 0;
760 
761  /* now a random change to learn, based on player Int.
762  * give bonus based on level - otherwise stupid characters
763  * might never be able to learn anything.
764  */
765  if (random_roll(0, 99, pl, PREFER_LOW) > (get_learn_spell(pl->stats.Int)+(pl->level/5)))
766  return 2; /* failure :< */
767 
768  if (!tmp)
769  tmp = give_skill_by_name(pl, scroll->skill);
770 
771  if (!tmp) {
772  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
773  return 2;
774  }
775 
777  link_player_skills(pl);
778  return 1;
779 }
780 
792 static int clipped_percent(int64_t a, int64_t b) {
793  int rv;
794 
795  if (b <= 0)
796  return 0;
797 
798  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
799 
800  if (rv < 0)
801  return 0;
802  else if (rv > 100)
803  return 100;
804 
805  return rv;
806 }
807 
824 void show_skills(object *op, const char *search) {
825  const char *cp;
826  int i, num_skills_found = 0;
827  static const char *const periods = "........................................";
828  /* Need to have a pointer and use strdup for qsort to work properly */
829  char skills[MAX_SKILLS][MAX_BUF];
830 
831  FOR_INV_PREPARE(op, tmp) {
832  if (tmp->type == SKILL) {
833  char buf[MAX_BUF];
834 
835  if (search && strstr(tmp->name, search) == NULL)
836  continue;
837  /* Basically want to fill this out to 40 spaces with periods */
838  snprintf(buf, sizeof(buf), "%s%s", tmp->name, periods);
839  buf[40] = 0;
840 
842  snprintf(skills[num_skills_found++], MAX_BUF, "%slvl:%3d (xp:%"FMT64"/%"FMT64"/%d%%)",
843  buf, tmp->level,
844  tmp->stats.exp,
845  level_exp(tmp->level+1, op->expmul),
846  clipped_percent(tmp->perm_exp, tmp->stats.exp));
847  } else {
848  snprintf(skills[num_skills_found++], MAX_BUF, "%slvl:%3d (xp:%"FMT64"/%"FMT64")",
849  buf, tmp->level,
850  tmp->stats.exp,
851  level_exp(tmp->level+1, op->expmul));
852  }
853  /* I don't know why some characters get a bunch of skills, but
854  * it sometimes happens (maybe a leftover from buggier earlier code
855  * and those character are still about). In any case, lets handle
856  * it so it doesn't crash the server - otherwise, one character may
857  * crash the server numerous times.
858  */
859  if (num_skills_found >= MAX_SKILLS) {
861  "Your character has too many skills. "
862  "Something isn't right - contact the server admin");
863  break;
864  }
865  }
866  } FOR_INV_FINISH();
867 
869  "Player skills:");
870 
871  if (num_skills_found > 1)
872  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
873 
874  for (i = 0; i < num_skills_found; i++) {
876  skills[i]);
877  }
878 
880  "You can handle %d weapon improvements.",
881  op->level/5+5);
882 
883  cp = determine_god(op);
884  if (strcmp(cp, "none") == 0)
885  cp = NULL;
887  "You worship %s.",
888  cp ? cp : "no god at current time");
889 
891  "Your equipped item power is %d out of %d\n",
892  op->contr->item_power, op->level);
893 }
894 
911 int use_skill(object *op, const char *string) {
912  object *skop;
913  size_t len;
914 
915  if (!string)
916  return 0;
917 
918  skop = NULL;
919  FOR_INV_PREPARE(op, tmp) {
920  if (tmp->type == SKILL
922  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
923  skop = tmp;
924  break;
925  } else if (tmp->type == SKILL_TOOL
926  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
927  skop = tmp;
928  break;
929  }
930  } FOR_INV_FINISH();
931  if (!skop) {
933  "Unable to find skill %s",
934  string);
935  return 0;
936  }
937 
938  len = strlen(skop->skill);
939 
940  /* All this logic goes and skips over the skill name to find any
941  * options given to the skill. Its pretty simple - if there
942  * are extra parameters (as determined by string length), we
943  * want to skip over any leading spaces.
944  */
945  if (len >= strlen(string)) {
946  string = NULL;
947  } else {
948  string += len;
949  while (*string == 0x20)
950  string++;
951  if (strlen(string) == 0)
952  string = NULL;
953  }
954 
955 #ifdef SKILL_UTIL_DEBUG
956  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
957 #endif
958 
959  /* Change to the new skill, then execute it. */
960  if (do_skill(op, op, skop, op->facing, string))
961  return 1;
962 
963  return 0;
964 }
965 
988 static object *find_best_player_hth_skill(object *op) {
989  object *best_skill = NULL;
990  int last_skill;
991 
992  if (op->contr->unarmed_skill) {
993  /* command_unarmed_skill() already does these checks, and right
994  * now I do not think there is any way to lose unarmed skills.
995  * But maybe in the future there will be (polymorph?) so handle
996  * it appropriately. MSW 2009-07-03
997  *
998  * Note that the error messages should only print out once when
999  * the initial failure to switch skills happens, so the player
1000  * should not get spammed with tons of messages unless they have
1001  * no valid unarmed skill
1002  */
1003 
1004  best_skill = find_skill_by_name(op, op->contr->unarmed_skill);
1005 
1006  if (!best_skill) {
1008  "Unable to find skill %s - using default unarmed skill",
1009  op->contr->unarmed_skill);
1010  } else {
1011  size_t i;
1012 
1013  for (i = 0; i < sizeof(unarmed_skills); i++)
1014  if (best_skill->subtype == unarmed_skills[i])
1015  break;
1016  if (i < sizeof(unarmed_skills))
1017  return(best_skill);
1018  }
1019  /* If for some reason the unarmed_skill is not valid, we fall
1020  * through the processing below.
1021  */
1022  }
1023 
1024 
1025  /* Dragons are a special case - gros 25th July 2006 */
1026  /* Perhaps this special case should be removed and unarmed_skill
1027  * set to clawing for dragon characters? MSW 2009-07-03
1028  */
1029  if (is_dragon_pl(op)) {
1030  object *tmp;
1031 
1032  tmp = find_skill_by_number(op, SK_CLAWING);
1033  if (tmp) /* I suppose it should always be true - but maybe there's
1034  * draconic toothache ? :) */
1035  return tmp;
1036  }
1037 
1038  last_skill = sizeof(unarmed_skills);
1039  FOR_INV_PREPARE(op, tmp) {
1040  if (tmp->type == SKILL) {
1041  int i;
1042 
1043  /* The order in the array is preferred order. So basically,
1044  * we just cut down the number to search - eg, if we find a skill
1045  * early on in flame touch, then we only need to look into the unarmed_array
1046  * to the entry before flame touch - don't care about the entries afterward,
1047  * because they are inferior skills.
1048  * if we end up finding the best skill (i==0) might as well return
1049  * right away - can't get any better than that.
1050  */
1051  for (i = 0; i < last_skill; i++) {
1052  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
1053  best_skill = tmp;
1054  last_skill = i;
1055  if (i == 0)
1056  return best_skill;
1057  }
1058  }
1059  }
1060  } FOR_INV_FINISH();
1061  return best_skill;
1062 }
1063 
1077 static void do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
1078  int success;
1079 
1080  /* For Players only: if there is no ready weapon, and no "attack" skill
1081  * is readied either then try to find a skill for the player to use.
1082  * it is presumed that if skill is set, it is a valid attack skill (eg,
1083  * the caller should have set it appropriately). We still want to pass
1084  * through that code if skill is set to change to the skill.
1085  */
1086  if (op->type == PLAYER) {
1087  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1088  size_t i;
1089 
1090  if (!skill) {
1091  /* See if the players chosen skill is a combat skill, and use
1092  * it if appropriate.
1093  */
1094  if (op->chosen_skill) {
1095  /* the list is 0-terminated, and talismans, which can be in chosen_skill,
1096  * have a subtype of 0, therefore don't check the 0 */
1097  for (i = 0; unarmed_skills[i] != 0; i++)
1098  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1099  skill = op->chosen_skill;
1100  break;
1101  }
1102  }
1103  /* If we didn't find a skill above, look harder for a good skill */
1104  if (!skill) {
1105  skill = find_best_player_hth_skill(op);
1106 
1107  if (!skill) {
1108  draw_ext_info(NDI_BLACK, 0, op,
1110  "You have no unarmed combat skills!");
1111  return;
1112  }
1113  }
1114  }
1115  if (skill != op->chosen_skill) {
1116  /* now try to ready the new skill */
1117  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1120  "Couldn't change to skill %s",
1121  skill->name);
1122  return;
1123  }
1124  }
1125  } else {
1126  /* Seen some crashes below where current_weapon is not set,
1127  * even though the flag says it is. So if current weapon isn't set,
1128  * do some work in trying to find the object to use.
1129  */
1130  if (!op->current_weapon) {
1131  object *tmp;
1132 
1133  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1135  if (!tmp) {
1136  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1137  op->current_weapon = NULL;
1138  return;
1139  } else {
1140  char weapon[MAX_BUF];
1141 
1142  query_name(tmp, weapon, MAX_BUF);
1143  op->current_weapon = tmp;
1144  }
1145  }
1146 
1147  /* Has ready weapon - make sure chosen_skill is set up properly */
1148  if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
1149  object *found_skill = find_skill_by_name(op, op->current_weapon->skill);
1150  assert(found_skill != NULL);
1151  change_skill(op, found_skill, 1);
1152  }
1153  }
1154  }
1155 
1156  /* lose invisibility/hiding status for running attacks */
1157 
1158  if (op->type == PLAYER && op->contr->tmp_invis) {
1159  op->contr->tmp_invis = 0;
1160  op->invisible = 0;
1161  op->hide = 0;
1163  }
1164 
1165  success = attack_ob(tmp, op);
1166 
1167  /*
1168  * print appropriate messages to the player
1169  *
1170  * If no string, then we aren't dealing with the
1171  * skill attack directly, so the message is printed elsewhere.
1172  *
1173  * Only print hit/miss if we've got a Fire+Attack.
1174  * Otherwise, don't print here at all.
1175  */
1176 
1177  if (string != NULL && tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1178  char op_name[MAX_BUF];
1179  if (success){
1180  if (op->type == PLAYER) {
1181  query_name(tmp, op_name, MAX_BUF);
1184  "You %s %s!",
1185  string, op_name);
1186  } else if (tmp->type == PLAYER) {
1187  query_name(op, op_name, MAX_BUF);
1190  "%s %s you!",
1191  op_name, string);
1192  }
1193  }
1194  else{
1195  query_name(tmp, op_name, MAX_BUF);
1198  "You miss %s!",
1199  op_name);
1200  }
1201  }
1202 }
1203 
1227 void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1228  int16_t tx, ty;
1229  mapstruct *m;
1230  int mflags;
1231 
1232  if (!dir)
1233  dir = pl->facing;
1234  tx = freearr_x[dir];
1235  ty = freearr_y[dir];
1236 
1237  /* If we don't yet have an opponent, find if one exists, and attack.
1238  * Legal opponents are the same as outlined in move_player_attack()
1239  */
1240 
1241  if (tmp == NULL) {
1242  m = pl->map;
1243  tx = pl->x+freearr_x[dir];
1244  ty = pl->y+freearr_y[dir];
1245 
1246  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1247  if (mflags&P_OUT_OF_MAP)
1248  return;
1249 
1250  /* space must be blocked for there to be anything interesting to do */
1251  if (!(mflags&P_IS_ALIVE)
1252  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1253  return;
1254  }
1255 
1256  FOR_MAP_PREPARE(m, tx, ty, tmp2)
1257  if ((QUERY_FLAG(tmp2, FLAG_ALIVE) && tmp2->stats.hp >= 0)
1258  || QUERY_FLAG(tmp2, FLAG_CAN_ROLL)
1259  || tmp2->type == LOCKED_DOOR) {
1260  /* Don't attack party members */
1261  if ((pl->type == PLAYER && tmp2->type == PLAYER)
1262  && (pl->contr->party != NULL && pl->contr->party == tmp2->contr->party))
1263  return;
1264  tmp = tmp2;
1265  break;
1266  }
1267  FOR_MAP_FINISH();
1268  }
1269  if (!tmp) {
1270  if (pl->type == PLAYER)
1272  "There is nothing to attack!");
1273  return;
1274  }
1275 
1276  do_skill_attack(tmp, pl, string, skill);
1277 }
1278 
1297 static void attack_hth(object *pl, int dir, const char *string, object *skill) {
1298  object *weapon;
1299 
1300  if (QUERY_FLAG(pl, FLAG_READY_WEAPON)) {
1301  weapon = object_find_by_type_applied(pl, WEAPON);
1302  if (weapon != NULL) {
1303  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1304  char weaponname[MAX_BUF];
1305 
1306  query_name(weapon, weaponname, MAX_BUF);
1309  "You are unable to unwield %s in order to attack with %s.",
1310  weaponname, skill->name);
1311  return;
1312  } else {
1314  "You unwield your weapon in order to attack.");
1315  }
1316  }
1317  }
1318  skill_attack(NULL, pl, dir, string, skill);
1319 }
1320 
1341 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1342  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1343  if (op->type == PLAYER)
1345  "You have no ready weapon to attack with!");
1346  return;
1347  }
1348  skill_attack(NULL, op, dir, string, skill);
1349 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:315
#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:517
int skill_ident(object *pl, object *skill)
Definition: skills.c:895
int64_t level_exp(int level, double expmul)
Definition: living.c:1847
static void attack_melee_weapon(object *op, int dir, const char *string, object *skill)
Definition: skill_util.c:1341
#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:3994
void link_player_skills(object *op)
Definition: player.c:279
static int clipped_percent(int64_t a, int64_t b)
Definition: skill_util.c:792
int attack_ob(object *op, object *hitter)
Definition: attack.c:905
const char * skill_names[MAX_SKILLS]
Definition: skill_util.c:57
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:911
object clone
Definition: object.h:470
int16_t invisible
Definition: object.h:360
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:386
static object * adjust_skill_tool(object *who, object *skill, object *skill_tool)
Definition: skill_util.c:129
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:339
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:582
object * ranges[range_size]
Definition: player.h:103
uint8_t subtype
Definition: object.h:339
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Definition: skill_util.c:411
uint8_t hide
Definition: object.h:387
int64_t exp
Definition: living.h:47
double expmul
Definition: object.h:395
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:583
int jump(object *pl, int dir, object *skill)
Definition: skills.c:658
int remove_trap(object *op, object *skill)
Definition: skills.c:1283
#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:61
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Definition: skill_util.c:645
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:310
Definition: object.h:465
#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:1752
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:386
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:2244
#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:491
struct mapdef * map
Definition: object.h:297
int is_dragon_pl(const object *op)
Definition: player.c:114
#define snprintf
Definition: win32.h:46
static void attack_hth(object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1297
#define FOR_INV_FINISH()
Definition: define.h:714
#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:276
struct obj * current_weapon
Definition: object.h:370
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:988
struct pl * contr
Definition: object.h:276
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:585
uint8_t simple_exp
Definition: global.h:259
void do_harvest(object *pl, int dir, object *skill)
Definition: c_misc.c:2246
#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:2126
#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:67
int learn_skill(object *pl, object *scroll)
Definition: skill_util.c:737
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:254
Definition: object.h:107
int get_skill_client_code(const char *skill_name)
Definition: skill_util.c:96
int pray(object *pl, object *skill)
Definition: skills.c:1356
object * object_find_by_type_and_skill(const object *who, int type, const char *skill)
Definition: object.c:4094
Definition: object.h:143
void show_skills(object *op, const char *search)
Definition: skill_util.c:824
living stats
Definition: object.h:368
struct archt * arch
Definition: object.h:412
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1119
uint8_t type
Definition: object.h:338
struct Settings settings
Definition: init.c:40
struct archt * next
Definition: object.h:467
rangetype
Definition: player.h:15
#define FLAG_APPLIED
Definition: define.h:235
int find_traps(object *pl, object *skill)
Definition: skills.c:1212
#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:965
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:2309
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:191
#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:378
object * find_skill_by_number(object *who, int skillno)
Definition: skill_util.c:294
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1735
const char * unarmed_skill
Definition: player.h:202
int16_t level
Definition: object.h:351
int8_t facing
Definition: object.h:335
void fix_object(object *op)
Definition: living.c:1119
void meditate(object *pl, object *skill)
Definition: skills.c:1404
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:1227
int8_t magic
Definition: object.h:348
#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:1077
#define FLAG_FREED
Definition: define.h:233