Crossfire Server, Branches 1.12  R18729
skill_util.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_skill_util_c =
3  * "$Id: skill_util.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 /*
6  CrossFire, A Multiplayer game for X-windows
7 
8  Copyright (C) 2006 Mark Wedel & Crossfire Development Team
9  Copyright (C) 1992 Frank Tore Johansen
10 
11  This program is free software; you can redistribute it and/or modify
12  it under the terms of the GNU General Public License as published by
13  the Free Software Foundation; either version 2 of the License, or
14  (at your option) any later version.
15 
16  This program is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  GNU General Public License for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with this program; if not, write to the Free Software
23  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 
25  The author can be reached via e-mail to crossfire-devel@real-time.com
26 */
27 
46 /* define the following for skills utility debuging */
47 /* #define SKILL_UTIL_DEBUG */
48 
49 #define WANT_UNARMED_SKILLS
50 
51 #include <global.h>
52 #include <object.h>
53 #ifndef __CEXTRACT__
54 #include <sproto.h>
55 #endif
56 #include <living.h> /* for defs of STR,CON,DEX,etc. -b.t.*/
57 #include <spells.h>
58 
59 static int attack_hth(object *pl, int dir, const char *string, object *skill);
60 static int attack_melee_weapon(object *op, int dir, const char *string, object *skill);
61 
65 const char *skill_names[NUM_SKILLS];
66 
71 void init_skills(void) {
72  int i;
73  archetype *at;
74 
75  for (i = 0; i < NUM_SKILLS; i++)
76  skill_names[i] = NULL;
77 
78  for (at = first_archetype; at != NULL; at = at->next) {
79  if (at->clone.type == SKILL) {
80  if (at->clone.subtype >= sizeof(skill_names)/sizeof(*skill_names)) {
81  LOG(llevError, "init_skills: invalid skill subtype %d for skill %s\n", at->clone.subtype, at->clone.skill);
82  } else if (skill_names[at->clone.subtype] != NULL) {
83  LOG(llevError, "init_skills: multiple skill using same subtype %d, %s, %s\n",
85  } else {
87  }
88  }
89  }
90 
91  /* This isn't really an error if there is no skill subtype set, but
92  * checking for this may catch some user errors.
93  * On the other hand, it'll crash later on, which is not nice. Thus giving a dummy name.
94  */
95  for (i = 1; i < NUM_SKILLS; i++) {
96  if (!skill_names[i]) {
97  LOG(llevError, "init_skills: skill subtype %d doesn't have a name?\n", i);
98  skill_names[i] = add_string("dummy skill");
99  }
100  }
101 }
102 
112 void link_player_skills(object *op) {
113  object *tmp;
114 
115  for (tmp = op->inv; tmp; tmp = tmp->below) {
116  if (tmp->type == SKILL) {
117  /* This is really a warning, hence no else below */
118  if (op->contr->last_skill_ob[tmp->subtype] && op->contr->last_skill_ob[tmp->subtype] != tmp) {
119  LOG(llevError, "Multiple skills with the same subtype? %s, %s\n", op->contr->last_skill_ob[tmp->subtype]->skill, tmp->skill);
120  }
121  if (tmp->subtype >= NUM_SKILLS) {
122  LOG(llevError, "Invalid subtype number %d (range 0-%d)\n", tmp->subtype, NUM_SKILLS);
123  } else {
124  op->contr->last_skill_ob[tmp->subtype] = tmp;
125  op->contr->last_skill_exp[tmp->subtype] = -1;
126  }
127  }
128  }
129 }
130 
153 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
154  if (!skill && !skill_tool)
155  return NULL;
156 
157  /* If this is a skill that can be used without a tool and no tool found, return it */
158  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
159  return skill;
160 
161  /* Player has a tool to use the skill. If not applied, apply it -
162  * if not successful, return skill if can be used. If they do have the skill tool
163  * but not the skill itself, give it to them.
164  */
165  if (skill_tool) {
166  if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
167  if (apply_special(who, skill_tool, 0)) {
168  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
169  return skill;
170  else
171  return NULL;
172  }
173  }
174  if (!skill) {
175  skill = give_skill_by_name(who, skill_tool->skill);
176  link_player_skills(who);
177  }
178  return skill;
179  }
180  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
181  return skill;
182  else
183  return NULL;
184 }
185 
207 object *find_skill_by_name(object *who, const char *name) {
208  object *skill = NULL, *skill_tool = NULL, *tmp;
209 
210  if (!name)
211  return NULL;
212 
213  /* We make sure the length of the string in the object is greater
214  * in length than the passed string. Eg, if we have a skill called
215  * 'hi', we don't want to match if the user passed 'high'
216  */
217  for (tmp = who->inv; tmp != NULL; tmp = tmp->below) {
218  if (tmp->type == SKILL
219  && !strncasecmp(name, tmp->skill, strlen(name))
220  && strlen(tmp->skill) >= strlen(name))
221  skill = tmp;
222 
223  /* Try to find appropriate skilltool. If the player has one already
224  * applied, we try to keep using that one.
225  */
226  else if (tmp->type == SKILL_TOOL
227  && !strncasecmp(name, tmp->skill, strlen(name))
228  && strlen(tmp->skill) >= strlen(name)) {
229  if (QUERY_FLAG(tmp, FLAG_APPLIED))
230  skill_tool = tmp;
231  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
232  skill_tool = tmp;
233  }
234  }
235 
236  return adjust_skill_tool(who, skill, skill_tool);
237 }
238 
257 object *find_skill_by_number(object *who, int skillno) {
258  object *skill = NULL, *skill_tool = NULL, *tmp;
259 
260  if (skillno < 1 || skillno >= NUM_SKILLS)
261  return NULL;
262 
263  for (tmp = who->inv; tmp != NULL; tmp = tmp->below) {
264  if (tmp->type == SKILL && tmp->subtype == skillno)
265  skill = tmp;
266 
267  /* Try to find appropriate skilltool. If the player has one already
268  * applied, we try to keep using that one.
269  */
270  else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
271  if (QUERY_FLAG(tmp, FLAG_APPLIED))
272  skill_tool = tmp;
273  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
274  skill_tool = tmp;
275  }
276  }
277 
278  return adjust_skill_tool(who, skill, skill_tool);
279 }
280 
301 int change_skill(object *who, object *new_skill, int flag) {
302  int old_range;
303 
304  if (who->type != PLAYER)
305  return 0;
306 
307  old_range = who->contr->shoottype;
308 
309  if (who->chosen_skill && who->chosen_skill == new_skill) {
310  /* optimization for changing skill to current skill */
311  if (who->type == PLAYER && !(flag&0x1))
312  who->contr->shoottype = range_skill;
313  return 1;
314  }
315 
316  if (!new_skill || who->chosen_skill)
317  if (who->chosen_skill)
318  apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
319 
320  /* Only goal in this case was to unapply a skill */
321  if (!new_skill)
322  return 0;
323 
324  if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
325  return 0;
326  }
327  if (flag&0x1)
328  who->contr->shoottype = old_range;
329 
330  return 1;
331 }
332 
340 void clear_skill(object *who) {
341  who->chosen_skill = NULL;
343  if (who->type == PLAYER) {
344  who->contr->ranges[range_skill] = NULL;
345  if (who->contr->shoottype == range_skill)
346  who->contr->shoottype = range_none;
347  }
348 }
349 
373 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
374  int success = 0, exp = 0;
375  object *tmp;
376 
377  if (!skill)
378  return 0;
379 
380  /* The code below presumes that the skill points to the object that
381  * holds the exp, level, etc of the skill. So if this is a player
382  * go and try to find the actual real skill pointer, and if the
383  * the player doesn't have a bucket for that, create one.
384  */
385  if (skill->type != SKILL && op->type == PLAYER) {
386  for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
387  if (tmp->type == SKILL && tmp->skill == skill->skill)
388  break;
389  }
390  if (!tmp)
391  tmp = give_skill_by_name(op, skill->skill);
392  skill = tmp;
393  }
394 
395  if (skill->anim_suffix)
396  apply_anim_suffix(op, skill->anim_suffix);
397 
398  switch (skill->subtype) {
399  case SK_LEVITATION:
400  /* Not 100% sure if this will work with new movement code -
401  * the levitation skill has move_type for flying, so when
402  * equipped, that should transfer to player, when not,
403  * shouldn't.
404  */
405  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
406  CLEAR_FLAG(skill, FLAG_APPLIED);
408  "You come to earth.", NULL);
409  } else {
410  SET_FLAG(skill, FLAG_APPLIED);
412  "You rise into the air!.", NULL);
413  }
414  fix_object(op);
415  success = 1;
416  break;
417 
418  case SK_STEALING:
419  exp = success = steal(op, dir, skill);
420  break;
421 
422  case SK_LOCKPICKING:
423  exp = success = pick_lock(op, dir, skill);
424  break;
425 
426  case SK_HIDING:
427  exp = success = hide(op, skill);
428  break;
429 
430  case SK_JUMPING:
431  success = jump(op, dir, skill);
432  break;
433 
434  case SK_INSCRIPTION:
435  exp = success = write_on_item(op, string, skill);
436  break;
437 
438  case SK_MEDITATION:
439  meditate(op, skill);
440  success = 1;
441  break;
442  /* note that the following 'attack' skills gain exp through hit_player() */
443 
444  case SK_KARATE:
445  (void)attack_hth(op, dir, "karate-chopped", skill);
446  break;
447 
448  case SK_PUNCHING:
449  (void)attack_hth(op, dir, "punched", skill);
450  break;
451 
452  case SK_FLAME_TOUCH:
453  (void)attack_hth(op, dir, "flamed", skill);
454  break;
455 
456  case SK_CLAWING:
457  (void)attack_hth(op, dir, "clawed", skill);
458  break;
459 
460  case SK_WRAITH_FEED:
461  (void)attack_hth(op, dir, "fed upon", skill);
462  break;
463 
466  (void)attack_melee_weapon(op, dir, NULL, skill);
467  break;
468 
469  case SK_FIND_TRAPS:
470  exp = success = find_traps(op, skill);
471  break;
472 
473  case SK_SINGING:
474  exp = success = singing(op, dir, skill);
475  break;
476 
477  case SK_ORATORY:
478  exp = success = use_oratory(op, dir, skill);
479  break;
480 
481  case SK_SMITHERY:
482  case SK_BOWYER:
483  case SK_JEWELER:
484  case SK_ALCHEMY:
485  case SK_THAUMATURGY:
486  case SK_LITERACY:
487  case SK_WOODSMAN:
488  if (use_alchemy(op) == 0)
489  exp = success = skill_ident(op, skill);
490  break;
491 
492  case SK_DET_MAGIC:
493  case SK_DET_CURSE:
494  exp = success = skill_ident(op, skill);
495  break;
496 
497  case SK_DISARM_TRAPS:
498  exp = success = remove_trap(op, skill);
499  break;
500 
501  case SK_THROWING:
502  success = skill_throw(op, part, dir, string, skill);
503  break;
504 
505  case SK_SET_TRAP:
507  "This skill is not currently implemented.", NULL);
508  break;
509 
510  case SK_USE_MAGIC_ITEM:
511  case SK_MISSILE_WEAPON:
513  "There is no special attack for this skill.", NULL);
514  break;
515 
516  case SK_PRAYING:
517  success = pray(op, skill);
518  break;
519 
520  case SK_BARGAINING:
521  success = describe_shop(op);
522  break;
523 
524  case SK_SORCERY:
525  case SK_EVOCATION:
526  case SK_PYROMANCY:
527  case SK_SUMMONING:
528  case SK_CLIMBING:
530  "This skill is already in effect.", NULL);
531  break;
532 
533  case SK_HARVESTING:
534  success = do_harvest(op, dir, skill);
535  break;
536 
537  default: {
538  char name[MAX_BUF];
539 
540  query_name(op, name, MAX_BUF);
541  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
542  break;
543  }
544  }
545 
546  /* For players we now update the speed_left from using the skill.
547  * Monsters have no skill use time because of the random nature in
548  * which use_monster_skill is called already simulates this.
549  * If certain skills should take more/less time, that should be
550  * in the code for the skill itself.
551  */
552 
553  if (op->type == PLAYER)
554  op->speed_left -= 1.0;
555 
556  /* this is a good place to add experience for successfull use of skills.
557  * Note that add_exp() will figure out player/monster experience
558  * gain problems.
559  */
560 
561  if (success && exp)
562  change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
563 
564  return success;
565 }
566 
599 sint64 calc_skill_exp(object *who, object *op, object *skill) {
600  sint64 op_exp = 0;
601  int op_lvl = 0;
602  float base, value, lvl_mult = 0.0;
603 
604  if (!skill)
605  skill = who;
606 
607  /* Oct 95 - where we have an object, I expanded our treatment
608  * to 3 cases:
609  * non-living magic obj, runes and everything else.
610  *
611  * If an object is not alive and magical we set the base exp higher to
612  * help out exp awards for skill_ident skills. Also, if
613  * an item is type RUNE, we give out exp based on stats.Cha
614  * and level (this was the old system) -b.t.
615  */
616 
617  if (!op) { /* no item/creature */
618  op_lvl = MAX(who->map->difficulty, 1);
619  op_exp = 0;
620  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
621  * for the amount of experience */
622  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
623  op_lvl = op->level;
624  } else { /* all other items/living creatures */
625  op_exp = op->stats.exp;
626  op_lvl = op->level;
627  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
628  op_lvl += 5*abs(op->magic);
629  }
630  }
631 
632  if (op_lvl < 1)
633  op_lvl = 1;
634 
635  if (who->type != PLAYER) { /* for monsters only */
636  return ((sint64)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
637  } else { /* for players */
638  base = op_exp;
639  /* if skill really is a skill, then we can look at the skill archetype for
640  * bse reward value (exp) and level multiplier factor.
641  */
642  if (skill->type == SKILL) {
643  base += skill->arch->clone.stats.exp;
644  if (settings.simple_exp) {
645  if (skill->arch->clone.level)
646  lvl_mult = (float)skill->arch->clone.level/100.0;
647  else
648  lvl_mult = 1.0; /* no adjustment */
649  } else {
650  if (skill->level)
651  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
652  else
653  lvl_mult = 1.0;
654  }
655  } else {
656  /* Don't divide by zero here! */
657  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
658  }
659  }
660 
661  /* assemble the exp total, and return value */
662 
663  value = base*lvl_mult;
664  if (value < 1)
665  value = 1; /* Always give at least 1 exp point */
666 
667 #ifdef SKILL_UTIL_DEBUG
668  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
669 #endif
670  return ((sint64)value);
671 }
672 
690 int learn_skill(object *pl, object *scroll) {
691  object *tmp;
692 
693  if (!scroll->skill) {
694  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
695  return 2;
696  }
697 
698  /* can't use find_skill_by_name because we want skills the player knows
699  * but can't use natively.
700  */
701 
702  for (tmp = pl->inv; tmp != NULL; tmp = tmp->below)
703  if (tmp->type == SKILL && !strncasecmp(scroll->skill, tmp->skill, strlen(scroll->skill)))
704  break;
705 
706  /* player already knows it */
707  if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
708  return 0;
709 
710  /* now a random change to learn, based on player Int.
711  * give bonus based on level - otherwise stupid characters
712  * might never be able to learn anything.
713  */
714  if (random_roll(0, 99, pl, PREFER_LOW) > (learn_spell[pl->stats.Int]+(pl->level/5)))
715  return 2; /* failure :< */
716 
717  if (!tmp)
718  tmp = give_skill_by_name(pl, scroll->skill);
719 
720  if (!tmp) {
721  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
722  return 2;
723  }
724 
726  link_player_skills(pl);
727  return 1;
728 }
729 
741 static int clipped_percent(sint64 a, sint64 b) {
742  int rv;
743 
744  if (b <= 0)
745  return 0;
746 
747  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
748 
749  if (rv < 0)
750  return 0;
751  else if (rv > 100)
752  return 100;
753 
754  return rv;
755 }
756 
773 void show_skills(object *op, const char *search) {
774  object *tmp = NULL;
775  char buf[MAX_BUF];
776  const char *cp;
777  int i, num_skills_found = 0;
778  static const char *const periods = "........................................";
779  /* Need to have a pointer and use strdup for qsort to work properly */
780  char skills[NUM_SKILLS][MAX_BUF];
781 
782  for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
783  if (tmp->type == SKILL) {
784  if (search && strstr(tmp->name, search) == NULL)
785  continue;
786  /* Basically want to fill this out to 40 spaces with periods */
787  snprintf(buf, sizeof(buf), "%s%s", tmp->name, periods);
788  buf[40] = 0;
789 
791  snprintf(skills[num_skills_found++], MAX_BUF, "%slvl:%3d (xp:%"FMT64"/%"FMT64"/%d%%)",
792  buf, tmp->level,
793  tmp->stats.exp,
794  level_exp(tmp->level+1, op->expmul),
795  clipped_percent(tmp->perm_exp, tmp->stats.exp));
796  } else {
797  snprintf(skills[num_skills_found++], MAX_BUF, "%slvl:%3d (xp:%"FMT64"/%"FMT64")",
798  buf, tmp->level,
799  tmp->stats.exp,
800  level_exp(tmp->level+1, op->expmul));
801  }
802  /* I don't know why some characters get a bunch of skills, but
803  * it sometimes happens (maybe a leftover from bugier earlier code
804  * and those character are still about). In any case, lets handle
805  * it so it doesn't crash the server - otherwise, one character may
806  * crash the server numerous times.
807  */
808  if (num_skills_found >= NUM_SKILLS) {
810  "Your character has too many skills. "
811  "Something isn't right - contact the server admin", NULL);
812  break;
813  }
814  }
815  }
816 
818  "Player skills:", NULL);
819 
820  if (num_skills_found > 1)
821  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
822 
823  for (i = 0; i < num_skills_found; i++) {
825  skills[i], skills[i]);
826  }
827 
829  "You can handle %d weapon improvements.",
830  "You can handle %d weapon improvements.",
831  op->level/5+5);
832 
833  cp = determine_god(op);
834  if (strcmp(cp, "none") == 0)
835  cp = NULL;
837  "You worship %s.",
838  "You worship %s.",
839  cp ? cp : "no god at current time");
840 
842  "Your equipped item power is %d out of %d\n",
843  "Your equipped item power is %d out of %d\n",
844  op->contr->item_power, op->level);
845 }
846 
863 int use_skill(object *op, const char *string) {
864  object *skop;
865  size_t len;
866 
867  if (!string)
868  return 0;
869 
870  for (skop = op->inv; skop != NULL; skop = skop->below) {
871  if (skop->type == SKILL
873  && !strncasecmp(string, skop->skill, MIN(strlen(string), strlen(skop->skill))))
874  break;
875  else if (skop->type == SKILL_TOOL
876  && !strncasecmp(string, skop->skill, MIN(strlen(string), strlen(skop->skill))))
877  break;
878  }
879  if (!skop) {
881  "Unable to find skill %s",
882  "Unable to find skill %s",
883  string);
884  return 0;
885  }
886 
887  len = strlen(skop->skill);
888 
889  /* All this logic goes and skips over the skill name to find any
890  * options given to the skill. Its pretty simple - if there
891  * are extra parameters (as deteremined by string length), we
892  * want to skip over any leading spaces.
893  */
894  if (len >= strlen(string)) {
895  string = NULL;
896  } else {
897  string += len;
898  while (*string == 0x20)
899  string++;
900  if (strlen(string) == 0)
901  string = NULL;
902  }
903 
904 #ifdef SKILL_UTIL_DEBUG
905  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
906 #endif
907 
908  /* Change to the new skill, then execute it. */
909  if (do_skill(op, op, skop, op->facing, string))
910  return 1;
911 
912  return 0;
913 }
914 
937 static object *find_best_player_hth_skill(object *op) {
938  object *tmp, *best_skill = NULL;
939  int dragon = is_dragon_pl(op), last_skill = sizeof(unarmed_skills), i;
940 
941  /* Dragons are a special case - gros 25th July 2006 */
942  if (dragon) {
943  tmp = find_skill_by_number(op, SK_CLAWING);
944  if (tmp) /* I suppose it should always be true - but maybe there's
945  * draconic toothache ? :) */
946  return tmp;
947  }
948  for (tmp = op->inv; tmp; tmp = tmp->below) {
949  if (tmp->type == SKILL) {
950 
951  /* The order in the array is preferred order. So basically,
952  * we just cut down the number to search - eg, if we find a skill
953  * early on in flame touch, then we only need to look into the unarmed_array
954  * to the entry before flame touch - don't care about the entries afterward,
955  * because they are infrerior skills.
956  * if we end up finding the best skill (i==0) might as well return
957  * right away - can't get any better than that.
958  */
959  for (i = 0; i < last_skill; i++) {
960  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
961  best_skill = tmp;
962  last_skill = i;
963  if (i == 0)
964  return best_skill;
965  }
966  }
967  }
968  }
969  return best_skill;
970 }
971 
987 static int do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
988  int success;
989 
990  /* For Players only: if there is no ready weapon, and no "attack" skill
991  * is readied either then try to find a skill for the player to use.
992  * it is presumed that if skill is set, it is a valid attack skill (eg,
993  * the caller should have set it appropriately). We still want to pass
994  * through that code if skill is set to change to the skill.
995  */
996  if (op->type == PLAYER) {
997  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
998  size_t i;
999 
1000  if (!skill) {
1001  /* See if the players chosen skill is a combat skill, and use
1002  * it if appropriate.
1003  */
1004  if (op->chosen_skill) {
1005  for (i = 0; i < sizeof(unarmed_skills); i++)
1006  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1007  skill = op->chosen_skill;
1008  break;
1009  }
1010  }
1011  /* If we didn't find a skill above, look harder for a good skill */
1012  if (!skill) {
1013  skill = find_best_player_hth_skill(op);
1014 
1015  if (!skill) {
1016  draw_ext_info(NDI_BLACK, 0, op,
1018  "You have no unarmed combat skills!", NULL);
1019  return 0;
1020  }
1021  }
1022  }
1023  if (skill != op->chosen_skill) {
1024  /* now try to ready the new skill */
1025  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1028  "Couldn't change to skill %s",
1029  "Couldn't change to skill %s",
1030  skill->name);
1031  return 0;
1032  }
1033  }
1034  } else {
1035  /* Seen some crashes below where current_weapon is not set,
1036  * even though the flag says it is. So if current weapon isn't set,
1037  * do some work in trying to find the object to use.
1038  */
1039  if (!op->current_weapon) {
1040  object *tmp;
1041 
1042  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1043  for (tmp = op->inv; tmp; tmp = tmp->below)
1044  if (tmp->type == WEAPON && QUERY_FLAG(tmp, FLAG_APPLIED))
1045  break;
1046 
1047  if (!tmp) {
1048  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1049  op->current_weapon = NULL;
1050  return 0;
1051  } else {
1052  char weapon[MAX_BUF];
1053 
1054  query_name(tmp, weapon, MAX_BUF);
1055  op->current_weapon = tmp;
1056  }
1057  }
1058 
1059  /* Has ready weapon - make sure chosen_skill is set up properly */
1060  if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
1062  }
1063  }
1064  }
1065 
1066  /* lose invisiblity/hiding status for running attacks */
1067 
1068  if (op->type == PLAYER && op->contr->tmp_invis) {
1069  op->contr->tmp_invis = 0;
1070  op->invisible = 0;
1071  op->hide = 0;
1073  }
1074 
1075  success = attack_ob(tmp, op);
1076 
1077  /* print appropriate messages to the player */
1078 
1079  if (success && string != NULL && tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1080  char op_name[MAX_BUF];
1081  if(op->type==PLAYER) {
1082  query_name(tmp, op_name, MAX_BUF);
1085  "You %s %s!",
1086  "You %s %s!",
1087  string,op_name);
1088  }
1089  else if(tmp->type==PLAYER) {
1090  query_name(op, op_name, MAX_BUF);
1093  "%s %s you!",
1094  "%s %s you!",
1095  op_name,string);
1096  }
1097  }
1098  return success;
1099 }
1100 
1126 int skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1127  sint16 tx, ty;
1128  mapstruct *m;
1129  int mflags;
1130 
1131  if (!dir)
1132  dir = pl->facing;
1133  tx = freearr_x[dir];
1134  ty = freearr_y[dir];
1135 
1136  /* If we don't yet have an opponent, find if one exists, and attack.
1137  * Legal opponents are the same as outlined in move_player_attack()
1138  */
1139 
1140  if (tmp == NULL) {
1141  m = pl->map;
1142  tx = pl->x+freearr_x[dir];
1143  ty = pl->y+freearr_y[dir];
1144 
1145  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1146  if (mflags&P_OUT_OF_MAP)
1147  return 0;
1148 
1149  /* space must be blocked for there to be anything interesting to do */
1150  if (!(mflags&P_IS_ALIVE)
1151  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1152  return 0;
1153  }
1154 
1155  for (tmp = GET_MAP_OB(m, tx, ty); tmp; tmp = tmp->above)
1156  if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->stats.hp >= 0)
1157  || QUERY_FLAG(tmp, FLAG_CAN_ROLL)
1158  || tmp->type == LOCKED_DOOR) {
1159  /* Don't attack party members */
1160  if ((pl->type == PLAYER && tmp->type == PLAYER)
1161  && (pl->contr->party != NULL && pl->contr->party == tmp->contr->party))
1162  return 0;
1163  break;
1164  }
1165  }
1166  if (!tmp) {
1167  if (pl->type == PLAYER)
1169  "There is nothing to attack!", NULL);
1170  return 0;
1171  }
1172 
1173  return do_skill_attack(tmp, pl, string, skill);
1174 }
1175 
1196 static int attack_hth(object *pl, int dir, const char *string, object *skill) {
1197  object *enemy = NULL, *weapon;
1198 
1199  if (QUERY_FLAG(pl, FLAG_READY_WEAPON))
1200  for (weapon = pl->inv; weapon; weapon = weapon->below) {
1201  if (weapon->type == WEAPON && QUERY_FLAG(weapon, FLAG_APPLIED)) {
1202  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1203  char weaponname[MAX_BUF];
1204 
1205  query_name(weapon, weaponname, MAX_BUF);
1208  "You are unable to unwield %s in order to attack with %s.",
1209  "You are unable to unwield %s in order to attack with %s.",
1210  weaponname, skill->name);
1211  return 0;
1212  } else {
1214  "You unwield your weapon in order to attack.", NULL);
1215  break;
1216  }
1217  }
1218  }
1219  return skill_attack(enemy, pl, dir, string, skill);
1220 }
1221 
1244 static int attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1245  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1246  if (op->type == PLAYER)
1248  "You have no ready weapon to attack with!", NULL);
1249  return 0;
1250  }
1251  return skill_attack(NULL, op, dir, string, skill);
1252 }
#define SK_BARGAINING
Definition: skills.h:56
#define AP_UNAPPLY
Definition: define.h:1010
#define SK_HARVESTING
Definition: skills.h:86
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:501
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, sint16 x, sint16 y, sint16 *nx, sint16 *ny)
Definition: map.c:330
sint8 Int
Definition: living.h:78
Definition: player.h:146
int apply_special(object *who, object *op, int aflags)
Definition: apply.c:1139
const char * determine_god(object *op)
Definition: gods.c:118
#define UP_OBJ_FACE
Definition: object.h:356
#define SK_SORCERY
Definition: skills.h:83
#define SK_CLIMBING
Definition: skills.h:67
int skill_ident(object *pl, object *skill)
Definition: skills.c:872
signed short sint16
Definition: global.h:72
void change_exp(object *op, sint64 exp, const char *skill_name, int flag)
Definition: living.c:2015
static int attack_hth(object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1196
#define SET_FLAG(xyz, p)
Definition: define.h:510
sstring add_refcount(sstring str)
Definition: shstr.c:202
#define TRAP
Definition: define.h:326
#define SK_KARATE
Definition: skills.h:66
#define SK_ORATORY
Definition: skills.h:59
#define SK_TWO_HANDED_WEAPON
Definition: skills.h:84
int attack_ob(object *op, object *hitter)
Definition: attack.c:929
#define SK_FLAME_TOUCH
Definition: skills.h:65
int do_harvest(object *pl, int dir, object *skill)
Definition: c_misc.c:2472
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:504
#define SK_MISSILE_WEAPON
Definition: skills.h:71
int use_skill(object *op, const char *string)
Definition: skill_util.c:863
object clone
Definition: object.h:326
#define MSG_TYPE_SKILL_LIST
Definition: newclient.h:506
#define SK_BOWYER
Definition: skills.h:51
sint16 invisible
Definition: object.h:211
short freearr_x[SIZEOFFREE]
Definition: object.c:75
#define PREFER_LOW
Definition: define.h:909
int pick_lock(object *pl, int dir, object *skill)
Definition: skills.c:400
static object * adjust_skill_tool(object *who, object *skill, object *skill_tool)
Definition: skill_util.c:153
rangetype shoottype
Definition: player.h:153
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
#define SK_THAUMATURGY
Definition: skills.h:76
sint64 level_exp(int level, double expmul)
Definition: living.c:1788
int change_skill(object *who, object *new_skill, int flag)
Definition: skill_util.c:301
object * ranges[range_size]
Definition: player.h:157
void update_object(object *op, int action)
Definition: object.c:1112
#define MSG_TYPE_VICTIM_WAS_HIT
Definition: newclient.h:567
uint8 subtype
Definition: object.h:190
#define SK_FIND_TRAPS
Definition: skills.h:62
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Definition: skill_util.c:373
sint64 exp
Definition: living.h:88
#define RUNE
Definition: define.h:325
#define SK_PUNCHING
Definition: skills.h:64
struct obj * above
Definition: object.h:146
static int attack_melee_weapon(object *op, int dir, const char *string, object *skill)
Definition: skill_util.c:1244
#define SK_SMITHERY
Definition: skills.h:50
double expmul
Definition: object.h:246
int jump(object *pl, int dir, object *skill)
Definition: skills.c:651
sint16 x
Definition: object.h:179
int remove_trap(object *op, object *skill)
Definition: skills.c:1272
sint16 sp
Definition: living.h:83
#define NDI_BLACK
Definition: newclient.h:195
#define AP_APPLY
Definition: define.h:1009
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
sint64 last_skill_exp[NUM_SKILLS]
Definition: player.h:192
#define SK_USE_MAGIC_ITEM
Definition: skills.h:73
#define SK_DISARM_TRAPS
Definition: skills.h:74
Definition: object.h:321
#define PLAYER
Definition: define.h:113
sint16 hp
Definition: living.h:81
#define FLAG_CAN_ROLL
Definition: define.h:550
short freearr_y[SIZEOFFREE]
Definition: object.c:81
partylist * party
Definition: player.h:237
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:502
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1689
#define SK_INSCRIPTION
Definition: skills.h:69
int use_alchemy(object *op)
Definition: alchemy.c:1006
#define FLAG_READY_SKILL
Definition: define.h:630
struct obj * chosen_skill
Definition: object.h:237
#define NDI_RED
Definition: newclient.h:198
#define FLAG_ALIVE
Definition: define.h:526
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:503
#define SK_LEVITATION
Definition: skills.h:79
#define SK_THROWING
Definition: skills.h:72
float speed_left
Definition: object.h:182
#define MSG_TYPE_VICTIM
Definition: newclient.h:336
#define FLAG_CAN_USE_SKILL
Definition: define.h:618
int hide(object *op, object *skill)
Definition: skills.c:498
sint16 item_power
Definition: player.h:171
struct mapdef * map
Definition: object.h:155
int is_dragon_pl(const object *op)
Definition: player.c:125
#define SK_CLAWING
Definition: skills.h:78
const char * name
Definition: object.h:167
void link_player_skills(object *op)
Definition: skill_util.c:112
#define SK_DET_MAGIC
Definition: skills.h:58
uint32 tmp_invis
Definition: player.h:179
#define SK_ONE_HANDED_WEAPON
Definition: skills.h:70
sint64 perm_exp
Definition: object.h:220
#define SK_LITERACY
Definition: skills.h:55
struct obj * below
Definition: object.h:145
uint16 difficulty
Definition: map.h:364
int steal(object *op, int dir, object *skill)
Definition: skills.c:290
struct obj * current_weapon
Definition: object.h:221
#define SK_WOODSMAN
Definition: skills.h:68
sint8 Cha
Definition: living.h:78
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:740
#define AP_NOPRINT
Definition: define.h:1020
#define P_OUT_OF_MAP
Definition: map.h:272
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:213
sint8 facing
Definition: object.h:186
static object * find_best_player_hth_skill(object *op)
Definition: skill_util.c:937
sint16 y
Definition: object.h:179
#define LOCKED_DOOR
Definition: define.h:132
struct pl * contr
Definition: object.h:134
#define WEAPON
Definition: define.h:127
#define SK_SUMMONING
Definition: skills.h:80
int describe_shop(const object *op)
Definition: shop.c:1311
#define MAX(x, y)
Definition: define.h:70
#define MSG_TYPE_SKILL
Definition: newclient.h:329
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
#define SK_ALCHEMY
Definition: skills.h:53
#define CLEAR_FLAG(xyz, p)
Definition: define.h:512
#define SK_EVOCATION
Definition: skills.h:82
static int clipped_percent(sint64 a, sint64 b)
Definition: skill_util.c:741
#define MAX_BUF
Definition: define.h:81
#define SK_PRAYING
Definition: skills.h:77
int strncasecmp(const char *s1, const char *s2, int n)
Definition: porting.c:402
const char * skill
Definition: object.h:174
#define NUM_SKILLS
Definition: skills.h:95
void init_skills(void)
Definition: skill_util.c:71
int learn_skill(object *pl, object *scroll)
Definition: skill_util.c:690
#define FLAG_READY_WEAPON
Definition: define.h:631
#define MIN(x, y)
Definition: define.h:67
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
int pray(object *pl, object *skill)
Definition: skills.c:1341
#define SK_WRAITH_FEED
Definition: skills.h:85
#define SK_MEDITATION
Definition: skills.h:63
static int do_skill_attack(object *tmp, object *op, const char *string, object *skill)
Definition: skill_util.c:987
#define SK_LOCKPICKING
Definition: skills.h:48
sint64 calc_skill_exp(object *who, object *op, object *skill)
Definition: skill_util.c:599
#define SK_HIDING
Definition: skills.h:49
void show_skills(object *op, const char *search)
Definition: skill_util.c:773
living stats
Definition: object.h:219
struct archt * arch
Definition: object.h:263
int singing(object *pl, int dir, object *skill)
Definition: skills.c:1123
#define SKILL
Definition: define.h:157
struct Settings settings
Definition: init.c:48
struct archt * next
Definition: object.h:323
#define FLAG_APPLIED
Definition: define.h:531
const char * anim_suffix
Definition: object.h:169
int find_traps(object *pl, object *skill)
Definition: skills.c:1207
sstring add_string(const char *str)
Definition: shstr.c:116
#define MSG_TYPE_ATTACK_DID_HIT
Definition: newclient.h:527
#define GET_MAP_OB(M, X, Y)
Definition: map.h:193
int skill_throw(object *op, object *part, int dir, const char *params, object *skill)
Definition: skills.c:2181
#define SK_PYROMANCY
Definition: skills.h:81
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
void apply_anim_suffix(object *who, sstring suffix)
Definition: anim.c:289
#define SK_SINGING
Definition: skills.h:60
int use_oratory(object *pl, int dir, object *skill)
Definition: skills.c:976
uint8 permanent_exp_ratio
Definition: global.h:346
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
const int learn_spell[MAX_STAT+1]
Definition: living.c:154
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:207
object * last_skill_ob[NUM_SKILLS]
Definition: player.h:191
#define SKILL_TOOL
Definition: define.h:236
void query_name(const object *op, char *buf, size_t size)
Definition: item.c:628
#define SK_SUBTRACT_SKILL_EXP
Definition: skills.h:106
Definition: map.h:346
#define P_IS_ALIVE
Definition: map.h:258
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:51
void clear_skill(object *who)
Definition: skill_util.c:340
object * find_skill_by_number(object *who, int skillno)
Definition: skill_util.c:257
int write_on_item(object *pl, const char *params, object *skill)
Definition: skills.c:1695
const char * skill_names[NUM_SKILLS]
Definition: skill_util.c:65
#define SK_DET_CURSE
Definition: skills.h:61
#define SK_STEALING
Definition: skills.h:54
#define SK_JUMPING
Definition: skills.h:57
sint16 level
Definition: object.h:202
void fix_object(object *op)
Definition: living.c:900
#define SK_SET_TRAP
Definition: skills.h:75
void meditate(object *pl, object *skill)
Definition: skills.c:1390
EXTERN archetype * first_archetype
Definition: global.h:195
sint8 magic
Definition: object.h:199
int skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Definition: skill_util.c:1126
#define MSG_TYPE_ATTACK
Definition: newclient.h:331
uint8 type
Definition: object.h:189
uint8 simple_exp
Definition: global.h:351
#define SK_JEWELER
Definition: skills.h:52
#define FLAG_FREED
Definition: define.h:529
uint32 hide
Definition: object.h:238