Crossfire Server, Trunk  1.75.0
skill_util.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
32 /* define the following for skills utility debugging */
33 /* #define SKILL_UTIL_DEBUG */
34 
35 #include "global.h"
36 
37 #include <assert.h>
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42 
43 #include "living.h"
44 #include "object.h"
45 #include "shop.h"
46 #include "skills.h"
47 #include "spells.h"
48 #include "sproto.h"
49 #include "assets.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 
68 
69 static int free_skill_index() {
70  int i;
71  for (i = 0; i < MAX_SKILLS; i++) {
72  if (skill_names[i] == NULL && skill_faces[i] == NULL) {
73  break;
74  }
75  }
76  return i;
77 }
78 static void do_each_skill(archetype *at) {
79  if (at->clone.type == SKILL) {
80  int i = free_skill_index();
81  if (i == MAX_SKILLS) {
82  LOG(llevError, "init_skills: too many skills, increase MAX_SKILLS and rebuild server!");
84  }
86  if (at->clone.face != NULL)
87  skill_faces[i] = at->clone.face;
88  if (at->clone.msg)
90  }
91 }
92 
97 void init_skills(void) {
98  int i;
99 
100  for (i = 0; i < MAX_SKILLS; i++) {
101  skill_names[i] = NULL;
102  skill_faces[i] = NULL;
103  skill_messages[i] = NULL;
104  }
105 
107 }
108 
114 int get_skill_client_code(const char *skill_name)
115 {
116  int index;
117  for (index = 0; index < MAX_SKILLS && skill_names[index] != NULL; index++)
118  if (strcmp(skill_names[index], skill_name) == 0)
119  return index;
120 
121  assert("invalid skill!");
122  return 0;
123 }
124 
147 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
148  if (!skill && !skill_tool)
149  return NULL;
150 
151  /* If this is a skill that can be used without a tool and no tool found, return it */
152  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
153  return skill;
154 
155  /* Player has a tool to use the skill. If not applied, apply it -
156  * if not successful, return skill if can be used. If they do have the skill tool
157  * but not the skill itself, give it to them.
158  */
159  if (skill_tool) {
160  if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
161  if (apply_special(who, skill_tool, 0)) {
162  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
163  return skill;
164  else
165  return NULL;
166  }
167  }
168  if (!skill) {
169  skill = give_skill_by_name(who, skill_tool->skill);
170  link_player_skills(who);
171  }
172  return skill;
173  }
174  if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
175  return skill;
176  else
177  return NULL;
178 }
179 
209 object *find_skill_by_name(object *who, const char *name) {
210  object *skill = NULL, *skills[MAX_SKILLS], *skill_tools[MAX_SKILLS];
211  const char *skill_names[MAX_SKILLS];
212  char *ourname=NULL;
213  int num_names, highest_level_skill=0, i;
214 
215  if (!name)
216  return NULL;
217 
218  /* Simple case - no commas in past in name, so don't need to tokenize */
219  if (!strchr(name, ',')) {
220  skill_names[0] = name;
221  skill_tools[0] = NULL;
222  skills[0] = NULL;
223  num_names = 1;
224  } else {
225  /* strtok_r is destructive, so we need our own copy */
226  char *lasts;
227  ourname = strdup(name);
228 
229  if ((skill_names[0] = strtok_r(ourname, ",", &lasts)) == NULL) {
230  /* This should really never happen */
231  LOG(llevError, "find_skill_by_name: strtok_r returned null, but strchr did not?\n");
232  free(ourname);
233  return NULL;
234  } else {
235  skill_tools[0] = NULL;
236  skills[0] = NULL;
237  /* we already have the first name from the strtok_r above */
238  num_names=1;
239  while ((skill_names[num_names] = strtok_r(NULL, ",", &lasts)) != NULL) {
240  /* Clean out any leading spacing. typical string would be
241  * skill1, skill2, skill3, ...
242  */
243  while (isspace(*skill_names[num_names]))
244  skill_names[num_names]++;
245  skills[num_names] = NULL;
246  skill_tools[num_names] = NULL;
247  num_names++;
248  }
249  }
250  /* While we don't use ourname below this point, the skill_names[] points into
251  * it, so we can't free it yet.
252  */
253  }
254 
255  FOR_INV_PREPARE(who, tmp) {
256  /* We make sure the length of the string in the object is greater
257  * in length than the passed string. Eg, if we have a skill called
258  * 'hi', we don't want to match if the user passed 'high'
259  */
260  if (tmp->type == SKILL || (tmp->type == SKILL_TOOL && !QUERY_FLAG(tmp, FLAG_UNPAID))) {
261  for (i = 0; i<num_names; i++) {
262  if (!strncasecmp(skill_names[i], tmp->skill, strlen(skill_names[i])) &&
263  strlen(tmp->skill) >= strlen(skill_names[i])) {
264  if (tmp->type == SKILL) {
265  skills[i] = tmp;
266  if (!skill || tmp->level > skill->level) {
267  skill = tmp;
268  highest_level_skill=i;
269  }
270  }
271  else {
272  /* Skill tools don't have levels, so we basically find the
273  * 'best' skill tool for this skill.
274  */
275  if (QUERY_FLAG(tmp, FLAG_APPLIED) || !skill_tools[i] ||
276  !QUERY_FLAG(skill_tools[i], FLAG_APPLIED)) {
277  skill_tools[i] = tmp;
278  }
279  }
280  /* Got a matching name - no reason to look through rest of names */
281  break;
282  }
283  }
284  }
285  } FOR_INV_FINISH();
286  free(ourname);
287  return adjust_skill_tool(who, skills[highest_level_skill], skill_tools[highest_level_skill]);
288 }
289 
312 object *find_skill_by_number(object *who, int skillno) {
313  object *skill = NULL, *skill_tool = NULL;
314 
315  if (skillno <= 0 || skillno > MAX_SKILLS) {
316  return NULL;
317  }
318 
319  FOR_INV_PREPARE(who, tmp) {
320  if (tmp->type == SKILL && tmp->subtype == skillno)
321  skill = tmp;
322 
323  /* Try to find appropriate skilltool. If the player has one already
324  * applied, we try to keep using that one.
325  */
326  else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
327  if (QUERY_FLAG(tmp, FLAG_APPLIED))
328  skill_tool = tmp;
329  else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
330  skill_tool = tmp;
331  }
332  } FOR_INV_FINISH();
333 
334  return adjust_skill_tool(who, skill, skill_tool);
335 }
336 
357 int change_skill(object *who, object *new_skill, int flag) {
358  rangetype old_range;
359 
360  if (who->type != PLAYER)
361  return 0;
362 
363  old_range = who->contr->shoottype;
364 
365  /* The readied skill can be a skill tool, so check on actual skill instead of object. */
366  if (new_skill && who->chosen_skill && who->chosen_skill->skill == new_skill->skill) {
367  /* optimization for changing skill to current skill */
368  if (!(flag&0x1))
369  who->contr->shoottype = range_skill;
370  return 1;
371  }
372 
373  if (who->chosen_skill)
374  apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
375 
376  /* Only goal in this case was to unapply a skill */
377  if (!new_skill)
378  return 0;
379 
380  if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
381  return 0;
382  }
383  if (flag&0x1)
384  who->contr->shoottype = old_range;
385 
386  return 1;
387 }
388 
396 void clear_skill(object *who) {
397  who->chosen_skill = NULL;
399  if (who->type == PLAYER) {
400  who->contr->ranges[range_skill] = NULL;
401  if (who->contr->shoottype == range_skill)
402  who->contr->shoottype = range_none;
403  }
404 }
405 
407  object *op;
408  object *part;
409  object *skill;
410  int dir;
411  const char *string;
412 };
413 
414 bool do_skill_cb(player * /*pl*/, void *data) {
415  struct do_skill_cb_data *args = (struct do_skill_cb_data *)data;
416  return do_skill(args->op, args->part, args->skill, args->dir, args->string) == 1;
417 }
418 
442 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
443  int success = 0, exp = 0;
444 
445  if (!skill)
446  return 0;
447 
448  /* The code below presumes that the skill points to the object that
449  * holds the exp, level, etc of the skill. So if this is a player
450  * go and try to find the actual real skill pointer, and if the
451  * the player doesn't have a bucket for that, create one.
452  */
453  if (skill->type != SKILL && op->type == PLAYER) {
454  object *tmp;
455 
457  if (!tmp) {
458  tmp = give_skill_by_name(op, skill->skill);
459  if (!tmp) {
460  LOG(llevError, "do_skill: asked for skill %s but couldn't find matching SKILL archetype.\n", skill->skill);
461  return 0;
462  }
463  }
464  skill = tmp;
465  }
466 
467  if (skill->anim_suffix)
469 
470  switch (skill->subtype) {
471  case SK_LEVITATION:
472  /* Not 100% sure if this will work with new movement code -
473  * the levitation skill has move_type for flying, so when
474  * equipped, that should transfer to player, when not,
475  * shouldn't.
476  */
477  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
480  "You come to earth.");
481  } else {
484  "You rise into the air!.");
485  }
486  fix_object(op);
487  success = 1;
488  break;
489 
490  case SK_STEALING:
491  exp = success = steal(op, dir, skill);
492  break;
493 
494  case SK_LOCKPICKING:
495  exp = success = pick_lock(op, dir, skill);
496  break;
497 
498  case SK_HIDING:
499  exp = success = hide(op, skill);
500  break;
501 
502  case SK_JUMPING:
503  success = jump(op, dir, skill);
504  break;
505 
506  case SK_INSCRIPTION:
507  exp = success = write_on_item(op, string, skill);
508  break;
509 
510  /* note that the following 'attack' skills gain exp through hit_player() */
511 
512  case SK_KARATE:
513  attack_hth(op, dir, "karate-chopped", skill);
514  break;
515 
516  case SK_PUNCHING:
517  attack_hth(op, dir, "punched", skill);
518  break;
519 
520  case SK_FLAME_TOUCH:
521  attack_hth(op, dir, "flamed", skill);
522  break;
523 
524  case SK_CLAWING:
525  attack_hth(op, dir, "clawed", skill);
526  break;
527 
528  case SK_WRAITH_FEED:
529  attack_hth(op, dir, "fed upon", skill);
530  break;
531 
534  (void)attack_melee_weapon(op, dir, NULL, skill);
535  break;
536 
537  case SK_FIND_TRAPS:
538  exp = success = find_traps(op, skill);
539  break;
540 
541  case SK_SINGING:
542  exp = success = singing(op, dir, skill);
543  break;
544 
545  case SK_ORATORY:
546  exp = success = use_oratory(op, dir, skill);
547  break;
548 
549  case SK_SMITHERY:
550  case SK_BOWYER:
551  case SK_JEWELER:
552  case SK_ALCHEMY:
553  case SK_THAUMATURGY:
554  case SK_LITERACY:
555  case SK_WOODSMAN:
556  if (use_alchemy(op) == 0)
557  exp = success = skill_ident(op, skill);
558  break;
559 
560  case SK_DET_MAGIC:
561  case SK_DET_CURSE:
562  exp = success = skill_ident(op, skill);
563  break;
564 
565  case SK_DISARM_TRAPS:
566  exp = success = remove_trap(op, skill);
567  break;
568 
569  case SK_THROWING:
570  success = skill_throw(op, part, dir, skill);
571  break;
572 
573  case SK_SET_TRAP:
575  "This skill is not currently implemented.");
576  break;
577 
578  case SK_USE_MAGIC_ITEM:
579  case SK_MISSILE_WEAPON:
581  "There is no special attack for this skill.");
582  break;
583 
584  case SK_MEDITATION:
585  if (op->contr && op->contr->repeat_func == NULL) {
586  player_start_repeat(op->contr, "meditation", do_skill_cb);
587  op->contr->repeat_func_data = calloc(1, sizeof(struct do_skill_cb_data));
589  *data = {op, part, skill, dir, string};
590  } else {
591  success = meditate(op, skill);
592  }
593  break;
594 
595  case SK_PRAYING:
596  if (op->contr && op->contr->repeat_func == NULL) {
597  player_start_repeat(op->contr, "praying", do_skill_cb);
598  op->contr->repeat_func_data = calloc(1, sizeof(struct do_skill_cb_data));
600  *data = {op, part, skill, dir, string};
601  } else {
602  success = pray(op, skill);
603  }
604  break;
605 
606  case SK_BARGAINING:
607  success = shop_describe(op);
608  break;
609 
610  case SK_SORCERY:
611  case SK_EVOCATION:
612  case SK_PYROMANCY:
613  case SK_SUMMONING:
614  case SK_CLIMBING:
615  case SK_EARTH_MAGIC:
616  case SK_AIR_MAGIC:
617  case SK_FIRE_MAGIC:
618  case SK_WATER_MAGIC:
620  "This skill is already in effect.");
621  break;
622 
623  case SK_HARVESTING:
624  if (op->contr && op->contr->repeat_func == NULL) {
626  op->contr->repeat_func_data = calloc(1, sizeof(struct do_skill_cb_data));
628  *data = {op, part, skill, dir, string};
629  } else {
630  do_harvest(op, dir, skill);
631  success = 1;
632  }
633  break;
634 
635  default: {
636  char name[MAX_BUF];
637 
639  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
640  break;
641  }
642  }
643 
644  /* For players we now update the speed_left from using the skill.
645  * Monsters have no skill use time because of the random nature in
646  * which use_monster_skill is called already simulates this.
647  * If certain skills should take more/less time, that should be
648  * in the code for the skill itself.
649  */
650 
651  if (op->type == PLAYER)
652  op->speed_left -= 1.0;
653 
654  /* this is a good place to add experience for successful use of skills.
655  * Note that add_exp() will figure out player/monster experience
656  * gain problems.
657  */
658 
659  if (success && exp)
661 
662  return success;
663 }
664 
699 int64_t calc_skill_exp(const object *who, const object *op, const object *skill) {
700  int64_t op_exp;
701  int op_lvl;
702  float base, value, lvl_mult;
703 
704  if (!skill)
705  skill = who;
706 
707  /* Oct 95 - where we have an object, I expanded our treatment
708  * to 3 cases:
709  * non-living magic obj, runes and everything else.
710  *
711  * If an object is not alive and magical we set the base exp higher to
712  * help out exp awards for skill_ident skills. Also, if
713  * an item is type RUNE, we give out exp based on stats.Cha
714  * and level (this was the old system) -b.t.
715  */
716 
717  if (!op) { /* no item/creature */
718  op_lvl = MAX(who->map->difficulty, 1);
719  op_exp = 0;
720  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
721  * for the amount of experience */
722  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
723  op_lvl = op->level;
724  } else { /* all other items/living creatures */
725  op_exp = op->stats.exp;
726  op_lvl = op->level;
727  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
728  op_lvl += 5*abs(op->magic);
729  }
730  }
731 
732  if (op_lvl < 1)
733  op_lvl = 1;
734 
735  if (who->type != PLAYER) { /* for monsters only */
736  return ((int64_t)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
737  }
738 
739  /* for players */
740  base = op_exp;
741  /* if skill really is a skill, then we can look at the skill archetype for
742  * base reward value (exp) and level multiplier factor.
743  */
744  if (skill->type == SKILL) {
745  base += skill->arch->clone.stats.exp;
746  if (settings.simple_exp) {
747  if (skill->arch->clone.level)
748  lvl_mult = (float)skill->arch->clone.level/100.0;
749  else
750  lvl_mult = 1.0; /* no adjustment */
751  } else {
752  if (skill->level)
753  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
754  else
755  lvl_mult = 1.0;
756  }
757  } else {
758  /* Don't divide by zero here! */
759  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
760  }
761 
762  /* assemble the exp total, and return value */
763 
764  value = base*lvl_mult;
765  if (value < 1)
766  value = 1; /* Always give at least 1 exp point */
767 
768 #ifdef SKILL_UTIL_DEBUG
769  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op ? op->name : "", op_lvl);
770 #endif
771  return ((int64_t)value);
772 }
773 
791 int learn_skill(object *pl, object *scroll) {
792  object *tmp;
793 
794  if (!scroll->skill) {
795  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
796  return 2;
797  }
798 
799  /* can't use find_skill_by_name because we want skills the player knows
800  * but can't use natively.
801  */
802 
803  tmp = NULL;
804  FOR_INV_PREPARE(pl, inv)
805  if (inv->type == SKILL && !strncasecmp(scroll->skill, inv->skill, strlen(scroll->skill))) {
806  tmp = inv;
807  break;
808  }
809  FOR_INV_FINISH();
810 
811  /* player already knows it */
812  if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
813  return 0;
814 
815  /* now a random change to learn, based on player Int.
816  * give bonus based on level - otherwise stupid characters
817  * might never be able to learn anything.
818  */
819  if (random_roll(0, 99, pl, PREFER_LOW) > (get_learn_spell(pl->stats.Int)+(pl->level/5)))
820  return 2; /* failure :< */
821 
822  if (!tmp)
823  tmp = give_skill_by_name(pl, scroll->skill);
824 
825  if (!tmp) {
826  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
827  return 2;
828  }
829 
831  link_player_skills(pl);
832  return 1;
833 }
834 
846 static int clipped_percent(int64_t a, int64_t b) {
847  int rv;
848 
849  if (b <= 0)
850  return 0;
851 
852  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
853 
854  if (rv < 0)
855  return 0;
856  else if (rv > 100)
857  return 100;
858 
859  return rv;
860 }
861 
862 
871 static int digits_in_long(int64_t num)
872 {
873  return num == 0 ? 1 : floor( log10( labs(num) ) ) + 1;
874 }
875 
892 void show_skills(object *op, const char *parms) {
893  const char *cp;
894  int i, num_skills_found = 0;
895  /* Need to have a pointer and use strdup for qsort to work properly */
896  char skills[MAX_SKILLS][MAX_BUF];
897  const char *search = parms;
898  bool long_format = false;
899 
900  if ( parms && strncmp(parms,"-l",2) == 0 ) {
901  long_format = true;
902  search += 2;
903  while ( *search && *search != ' ' ) ++search; /* move past other parameters */
904  while ( *search == ' ' ) ++search;
905  }
906  FOR_INV_PREPARE(op, tmp) {
907  if (tmp->type == SKILL) {
908  if (search && strstr(tmp->name, search) == NULL)
909  continue;
910 
912  if ( long_format ) {
913  int perm_level = exp_level(tmp->total_exp * settings.permanent_exp_ratio / 100);
914  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 ")%-*sperm lvl:%3d (xp:%" FMT64 "/%" FMT64 ") ",
915  tmp->name, tmp->level,
916  tmp->stats.exp,
917  level_exp(tmp->level+1, op->expmul),
918  1+2*digits_in_long(op->stats.exp)-digits_in_long(tmp->stats.exp)-digits_in_long(level_exp(tmp->level+1, op->expmul)),
919  "",
920  perm_level,
921  tmp->total_exp * settings.permanent_exp_ratio / 100,
922  level_exp(perm_level+1, op->expmul));
923  } else {
924  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 "/%d%%)",
925  tmp->name, tmp->level,
926  tmp->stats.exp,
927  level_exp(tmp->level+1, op->expmul),
928  clipped_percent(PERM_EXP(tmp->total_exp), tmp->stats.exp));
929  }
930  } else {
931  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 ")",
932  tmp->name, tmp->level,
933  tmp->stats.exp,
934  level_exp(tmp->level+1, op->expmul));
935  }
936  /* I don't know why some characters get a bunch of skills, but
937  * it sometimes happens (maybe a leftover from buggier earlier code
938  * and those character are still about). In any case, lets handle
939  * it so it doesn't crash the server - otherwise, one character may
940  * crash the server numerous times.
941  */
942  if (num_skills_found >= MAX_SKILLS) {
944  "Your character has too many skills.\n"
945  "Something isn't right - contact the server admin");
946  break;
947  }
948  }
949  } FOR_INV_FINISH();
950 
951  if ( search && *search ) {
953  "Player skills%s: (matching '%s')", long_format ? " (long format)":"",search);
954  } else {
956  "Player skills%s:", long_format ? " (long format)":"");
957  }
958 
959  if (num_skills_found > 1)
960  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
961 
962  for (i = 0; i < num_skills_found; i++) {
964  skills[i]);
965  }
966 
967  cp = determine_god(op);
968  if (strcmp(cp, "none") == 0)
969  cp = NULL;
970 
972  "You can handle %d weapon improvements.\n"
973  "You worship %s.\n"
974  "Your equipped item power is %d out of %d\n",
975  op->level/5+5,
976  cp ? cp : "no god at current time",
977  op->contr->item_power, op->level);
978 }
979 
996 int use_skill(object *op, const char *string) {
997  object *skop;
998  size_t len;
999 
1000  if (!string)
1001  return 0;
1002 
1003  skop = NULL;
1004  FOR_INV_PREPARE(op, tmp) {
1005  if (tmp->type == SKILL
1007  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
1008  skop = tmp;
1009  break;
1010  } else if (tmp->type == SKILL_TOOL
1011  && !QUERY_FLAG(tmp, FLAG_UNPAID) /* Holy symbols could be used unpaid w/o this */
1012  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
1013  skop = tmp;
1014  break;
1015  }
1016  } FOR_INV_FINISH();
1017  if (!skop) {
1019  "Unable to find skill %s",
1020  string);
1021  return 0;
1022  }
1023 
1024  len = strlen(skop->skill);
1025 
1026  /* All this logic goes and skips over the skill name to find any
1027  * options given to the skill. Its pretty simple - if there
1028  * are extra parameters (as determined by string length), we
1029  * want to skip over any leading spaces.
1030  */
1031  if (len >= strlen(string)) {
1032  string = NULL;
1033  } else {
1034  string += len;
1035  while (*string == 0x20)
1036  string++;
1037  if (strlen(string) == 0)
1038  string = NULL;
1039  }
1040 
1041 #ifdef SKILL_UTIL_DEBUG
1042  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
1043 #endif
1044 
1045  /* Change to the new skill, then execute it. */
1046  if (do_skill(op, op, skop, op->facing, string))
1047  return 1;
1048 
1049  return 0;
1050 }
1051 
1074 static object *find_best_player_hth_skill(object *op) {
1075  object *best_skill = NULL;
1076  int last_skill;
1077 
1078  if (op->contr->unarmed_skill) {
1079  /* command_unarmed_skill() already does these checks, and right
1080  * now I do not think there is any way to lose unarmed skills.
1081  * But maybe in the future there will be (polymorph?) so handle
1082  * it appropriately. MSW 2009-07-03
1083  *
1084  * Note that the error messages should only print out once when
1085  * the initial failure to switch skills happens, so the player
1086  * should not get spammed with tons of messages unless they have
1087  * no valid unarmed skill
1088  */
1089 
1090  best_skill = find_skill_by_name(op, op->contr->unarmed_skill);
1091 
1092  if (!best_skill) {
1094  "Unable to find skill %s - using default unarmed skill",
1095  op->contr->unarmed_skill);
1096  } else {
1097  size_t i;
1098 
1099  for (i = 0; i < sizeof(unarmed_skills); i++)
1100  if (best_skill->subtype == unarmed_skills[i])
1101  break;
1102  if (i < sizeof(unarmed_skills))
1103  return(best_skill);
1104  }
1105  /* If for some reason the unarmed_skill is not valid, we fall
1106  * through the processing below.
1107  */
1108  }
1109 
1110 
1111  /* Dragons are a special case - gros 25th July 2006 */
1112  /* Perhaps this special case should be removed and unarmed_skill
1113  * set to clawing for dragon characters? MSW 2009-07-03
1114  */
1115  if (is_dragon_pl(op)) {
1116  object *tmp;
1117 
1119  if (tmp) /* I suppose it should always be true - but maybe there's
1120  * draconic toothache ? :) */
1121  return tmp;
1122  }
1123 
1124  last_skill = sizeof(unarmed_skills);
1125  FOR_INV_PREPARE(op, tmp) {
1126  if (tmp->type == SKILL) {
1127  int i;
1128 
1129  /* The order in the array is preferred order. So basically,
1130  * we just cut down the number to search - eg, if we find a skill
1131  * early on in flame touch, then we only need to look into the unarmed_array
1132  * to the entry before flame touch - don't care about the entries afterward,
1133  * because they are inferior skills.
1134  * if we end up finding the best skill (i==0) might as well return
1135  * right away - can't get any better than that.
1136  */
1137  for (i = 0; i < last_skill; i++) {
1138  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
1139  best_skill = tmp;
1140  last_skill = i;
1141  if (i == 0)
1142  return best_skill;
1143  }
1144  }
1145  }
1146  } FOR_INV_FINISH();
1147  return best_skill;
1148 }
1149 
1163 static void do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
1164  int success;
1165 
1166  /* For Players only: if there is no ready weapon, and no "attack" skill
1167  * is readied either then try to find a skill for the player to use.
1168  * it is presumed that if skill is set, it is a valid attack skill (eg,
1169  * the caller should have set it appropriately). We still want to pass
1170  * through that code if skill is set to change to the skill.
1171  */
1172  if (op->type == PLAYER) {
1173  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1174  size_t i;
1175 
1176  if (!skill) {
1177  /* See if the players chosen skill is a combat skill, and use
1178  * it if appropriate.
1179  */
1180  if (op->chosen_skill) {
1181  /* the list is 0-terminated, and talismans, which can be in chosen_skill,
1182  * have a subtype of 0, therefore don't check the 0 */
1183  for (i = 0; unarmed_skills[i] != 0; i++)
1184  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1185  skill = op->chosen_skill;
1186  break;
1187  }
1188  }
1189  /* If we didn't find a skill above, look harder for a good skill */
1190  if (!skill) {
1192 
1193  if (!skill) {
1196  "You have no unarmed combat skills!");
1197  return;
1198  }
1199  }
1200  }
1201  if (skill != op->chosen_skill) {
1202  /* now try to ready the new skill */
1203  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1206  "Couldn't change to skill %s",
1207  skill->name);
1208  return;
1209  }
1210  }
1211  } else {
1212  /* Seen some crashes below where current_weapon is not set,
1213  * even though the flag says it is. So if current weapon isn't set,
1214  * do some work in trying to find the object to use.
1215  */
1216  if (!op->current_weapon) {
1217  object *tmp;
1218 
1219  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1221  if (!tmp) {
1222  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1223  op->current_weapon = NULL;
1224  return;
1225  } else {
1226  char weapon[MAX_BUF];
1227 
1228  query_name(tmp, weapon, MAX_BUF);
1229  op->current_weapon = tmp;
1230  }
1231  }
1232 
1233  /* Has ready weapon - make sure chosen_skill is set up properly */
1235  object *found_skill = find_skill_by_name(op, op->current_weapon->skill);
1236  // found_skill can be NULL -- TOCTTOU when item has mutated, e.g. dipping a bottle in a fountain
1237  change_skill(op, found_skill, 1);
1238  }
1239  }
1240  }
1241 
1242  /* lose invisibility/hiding status for running attacks */
1243 
1244  if (op->type == PLAYER && op->contr->tmp_invis) {
1245  op->contr->tmp_invis = 0;
1246  op->invisible = 0;
1247  op->hide = 0;
1249  }
1250 
1251  success = attack_ob(tmp, op);
1252 
1253  if (tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1254  char op_name[MAX_BUF];
1255  if (!success) { // In case of miss, attack_ob didn't print anything
1256  query_name(tmp, op_name, MAX_BUF);
1259  "You miss %s!",
1260  op_name);
1261  } else if (string != NULL) { // If string is NULL, message was already displayed
1262  if (op->type == PLAYER) {
1263  query_name(tmp, op_name, MAX_BUF);
1266  "You %s %s!",
1267  string, op_name);
1268  } else if (tmp->type == PLAYER) {
1269  query_name(op, op_name, MAX_BUF);
1272  "%s %s you!",
1273  op_name, string);
1274  }
1275  }
1276  }
1277 }
1278 
1302 void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1303  int16_t tx, ty;
1304  mapstruct *m;
1305  int mflags;
1306 
1307  if (!dir)
1308  dir = pl->facing;
1309  tx = freearr_x[dir];
1310  ty = freearr_y[dir];
1311 
1312  /* If we don't yet have an opponent, find if one exists, and attack.
1313  * Legal opponents are the same as outlined in move_player_attack()
1314  */
1315 
1316  if (tmp == NULL) {
1317  m = pl->map;
1318  tx = pl->x+freearr_x[dir];
1319  ty = pl->y+freearr_y[dir];
1320 
1321  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1322  if (mflags&P_OUT_OF_MAP)
1323  return;
1324 
1325  /* space must be blocked for there to be anything interesting to do */
1326  if (!(mflags&P_IS_ALIVE)
1327  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1328  return;
1329  }
1330 
1331  FOR_MAP_PREPARE(m, tx, ty, tmp2)
1332  if ((QUERY_FLAG(tmp2, FLAG_ALIVE) && tmp2->stats.hp >= 0)
1333  || QUERY_FLAG(tmp2, FLAG_CAN_ROLL)
1334  || tmp2->type == LOCKED_DOOR) {
1335  /* Don't attack party members */
1336  if ((pl->type == PLAYER && tmp2->type == PLAYER)
1337  && (pl->contr->party != NULL && pl->contr->party == tmp2->contr->party))
1338  return;
1339  tmp = tmp2;
1340  break;
1341  }
1342  FOR_MAP_FINISH();
1343  }
1344  if (!tmp) {
1345  if (pl->type == PLAYER)
1347  "There is nothing to attack!");
1348  return;
1349  }
1350 
1351  do_skill_attack(tmp, pl, string, skill);
1352 }
1353 
1372 static void attack_hth(object *pl, int dir, const char *string, object *skill) {
1373  object *weapon;
1374 
1375  if (QUERY_FLAG(pl, FLAG_READY_WEAPON)) {
1376  weapon = object_find_by_type_applied(pl, WEAPON);
1377  if (weapon != NULL) {
1378  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1379  char weaponname[MAX_BUF];
1380 
1381  query_name(weapon, weaponname, MAX_BUF);
1384  "You are unable to unwield %s in order to attack with %s.",
1385  weaponname, skill->name);
1386  return;
1387  } else {
1389  "You unwield your weapon in order to attack.");
1390  }
1391  }
1392  }
1393  skill_attack(NULL, pl, dir, string, skill);
1394 }
1395 
1416 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1417  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1418  if (op->type == PLAYER)
1420  "You have no ready weapon to attack with!");
1421  return;
1422  }
1423  skill_attack(NULL, op, dir, string, skill);
1424 }
Face
New face structure - this enforces the notion that data is face by face only - you can not change the...
Definition: face.h:14
living::exp
int64_t exp
Experience.
Definition: living.h:47
skills
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other skills
Definition: stats.txt:126
PLAYER
@ PLAYER
Definition: object.h:112
global.h
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
Settings::simple_exp
uint8_t simple_exp
If true, use the simple experience system.
Definition: global.h:267
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Finishes FOR_MAP_PREPARE().
Definition: define.h:714
SK_FIRE_MAGIC
@ SK_FIRE_MAGIC
Fire magic, unused.
Definition: skills.h:62
object_find_by_type_applied
object * object_find_by_type_applied(const object *who, int type)
Find applied object in inventory.
Definition: object.cpp:4068
MSG_TYPE_ATTACK_MISS
#define MSG_TYPE_ATTACK_MISS
attack didn't hit
Definition: newclient.h:624
player::unarmed_skill
const char * unarmed_skill
Prefered skill to use in unarmed combat.
Definition: player.h:223
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
learn_skill
int learn_skill(object *pl, object *scroll)
Player is trying to learn a skill.
Definition: skill_util.cpp:791
mapstruct::difficulty
uint16_t difficulty
What level the player should be to play here.
Definition: map.h:337
SK_INSCRIPTION
@ SK_INSCRIPTION
Inscription.
Definition: skills.h:41
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
use_alchemy
int use_alchemy(object *op)
Handle use_skill for alchemy-like items.
Definition: alchemy.cpp:1062
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:369
do_skill_attack
static void do_skill_attack(object *tmp, object *op, const char *string, object *skill)
We have got an appropriate opponent from either move_player_attack() or skill_attack().
Definition: skill_util.cpp:1163
SK_ALCHEMY
@ SK_ALCHEMY
Alchemy.
Definition: skills.h:25
player
One player.
Definition: player.h:107
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Messages related to skill use.
Definition: newclient.h:411
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
archetypes_for_each
void archetypes_for_each(arch_op op)
Definition: assets.cpp:305
SK_CLAWING
@ SK_CLAWING
Clawing.
Definition: skills.h:50
AP_APPLY
#define AP_APPLY
Item is to be applied.
Definition: define.h:558
Settings::permanent_exp_ratio
uint8_t permanent_exp_ratio
How much exp should be 'permenant' and unable to be lost.
Definition: global.h:262
object::arch
struct archetype * arch
Pointer to archetype.
Definition: object.h:424
MSG_TYPE_ATTACK_DID_HIT
#define MSG_TYPE_ATTACK_DID_HIT
Player hit something else.
Definition: newclient.h:616
SK_DET_MAGIC
@ SK_DET_MAGIC
Detect magic.
Definition: skills.h:30
object::invisible
int16_t invisible
How much longer the object will be invis.
Definition: object.h:370
TRAP
@ TRAP
Definition: object.h:246
object::x
int16_t x
Definition: object.h:335
SK_DISARM_TRAPS
@ SK_DISARM_TRAPS
Disarm traps.
Definition: skills.h:46
object::speed_left
float speed_left
How much speed is left to spend this round.
Definition: object.h:338
PREFER_LOW
#define PREFER_LOW
Definition: define.h:548
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
give_skill_by_name
object * give_skill_by_name(object *op, const char *skill_name)
Given the skill name skill_name, we find the skill archetype/object, set appropriate values,...
Definition: living.cpp:1788
object::anim_suffix
sstring anim_suffix
Used to determine combined animations.
Definition: object.h:324
WEAPON
@ WEAPON
Definition: object.h:124
AP_UNAPPLY
#define AP_UNAPPLY
Item is to be remvoed.
Definition: define.h:559
object::expmul
double expmul
needed experience = (calc_exp*expmul) - means some races/classes can need less/more exp to gain level...
Definition: object.h:407
SK_PRAYING
@ SK_PRAYING
Praying.
Definition: skills.h:49
write_on_item
int write_on_item(object *pl, const char *params, object *skill)
Implement the 'inscription' skill, which checks for the required skills and marked items before runni...
Definition: skills.cpp:1768
unarmed_skills
uint8_t unarmed_skills[UNARMED_SKILLS_COUNT]
Table of unarmed attack skills.
Definition: skills.cpp:31
range_none
@ range_none
No range selected.
Definition: player.h:30
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
use_oratory
int use_oratory(object *pl, int dir, object *skill)
Oratory skill handling.
Definition: skills.cpp:1005
MIN
#define MIN(x, y)
Definition: compat.h:21
SKILL
@ SKILL
Also see SKILL_TOOL (74) below.
Definition: object.h:148
singing
int singing(object *pl, int dir, object *skill)
Singing skill handling.
Definition: skills.cpp:1158
RUNE
@ RUNE
Definition: object.h:245
skill_ident
int skill_ident(object *pl, object *skill)
Main identification skill handling.
Definition: skills.cpp:936
fix_object
void fix_object(object *op)
Updates all abilities given by applied objects in the inventory of the given object.
Definition: living.cpp:1132
object_find_by_type_and_skill
object * object_find_by_type_and_skill(const object *who, int type, const char *skill)
Find object in inventory by type and skill.
Definition: object.cpp:4168
SK_KARATE
@ SK_KARATE
Karate.
Definition: skills.h:38
NDI_RED
#define NDI_RED
Definition: newclient.h:249
SEE_LAST_ERROR
@ SEE_LAST_ERROR
Definition: define.h:52
find_traps
int find_traps(object *pl, object *skill)
Checks for traps on the spaces around the player or in certain objects.
Definition: skills.cpp:1249
SK_MEDITATION
@ SK_MEDITATION
Meditation.
Definition: skills.h:35
SK_SET_TRAP
@ SK_SET_TRAP
Set traps, unused.
Definition: skills.h:47
skills.h
object::hide
uint8_t hide
The object is hidden, not invisible.
Definition: object.h:397
find_skill_by_number
object * find_skill_by_number(object *who, int skillno)
This returns the first skill pointer of the given subtype (the one that accumulates exp,...
Definition: skill_util.cpp:312
skill_messages
sstring skill_messages[MAX_SKILLS]
Will contain the message for the skills, initialized by init_skill().
Definition: skill_util.cpp:67
FLAG_CAN_ROLL
#define FLAG_CAN_ROLL
Object can be rolled.
Definition: define.h:241
object::level
int16_t level
Level of creature or object.
Definition: object.h:361
SK_SINGING
@ SK_SINGING
Singing.
Definition: skills.h:32
SK_SORCERY
@ SK_SORCERY
Sorcery.
Definition: skills.h:55
digits_in_long
static int digits_in_long(int64_t num)
Gives the number of digits that ld will print for a long int.
Definition: skill_util.cpp:871
MSG_TYPE_VICTIM
#define MSG_TYPE_VICTIM
Something bad is happening to the player.
Definition: newclient.h:419
MAX
#define MAX(x, y)
Definition: compat.h:24
free_skill_index
static int free_skill_index()
Definition: skill_util.cpp:69
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
SK_EARTH_MAGIC
@ SK_EARTH_MAGIC
Earth magic, unused.
Definition: skills.h:60
meditate
int meditate(object *pl, object *skill)
Meditation skill handling.
Definition: skills.cpp:1437
FLAG_ALIVE
#define FLAG_ALIVE
Object can fight (or be fought)
Definition: define.h:217
object::y
int16_t y
Position in the map for this object.
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:424
skill_names
const char * skill_names[MAX_SKILLS]
Will contain a number-name mapping for skills, initialized by init_skills().
Definition: skill_util.cpp:57
FLAG_FREED
#define FLAG_FREED
Object is in the list of free objects.
Definition: define.h:220
object_update
void object_update(object *op, int action)
object_update() updates the array which represents the map.
Definition: object.cpp:1419
object::contr
struct player * contr
Pointer to the player which control this object.
Definition: object.h:284
FMT64
#define FMT64
Definition: compat.h:16
FLAG_READY_SKILL
#define FLAG_READY_SKILL
(Monster or Player) has a skill readied
Definition: define.h:320
object::chosen_skill
object * chosen_skill
The skill chosen to use.
Definition: object.h:396
pick_lock
int pick_lock(object *pl, int dir, object *skill)
Lock pick handling.
Definition: skills.cpp:400
object::subtype
uint8_t subtype
Subtype of object.
Definition: object.h:349
SK_HARVESTING
@ SK_HARVESTING
Harvesting.
Definition: skills.h:58
change_skill
int change_skill(object *who, object *new_skill, int flag)
This changes the object's skill to new_skill.
Definition: skill_util.cpp:357
add_refcount
sstring add_refcount(sstring str)
Like add_string(), but the string is already a shared string.
Definition: shstr.cpp:224
SK_TWO_HANDED_WEAPON
@ SK_TWO_HANDED_WEAPON
Two handed weapons.
Definition: skills.h:56
determine_god
const char * determine_god(object *op)
Determines if op worships a god.
Definition: gods.cpp:55
freearr_y
short freearr_y[SIZEOFFREE]
Y offset when searching around a spot.
Definition: object.cpp:305
SK_JEWELER
@ SK_JEWELER
Jeweler.
Definition: skills.h:24
FLAG_UNPAID
#define FLAG_UNPAID
Object hasn't been paid for yet.
Definition: define.h:223
attack_hth
static void attack_hth(object *pl, int dir, const char *string, object *skill)
This handles all hand-to-hand attacks.
Definition: skill_util.cpp:1372
clipped_percent
static int clipped_percent(int64_t a, int64_t b)
Gives a percentage clipped to 0% -> 100% of a/b.
Definition: skill_util.cpp:846
query_name
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.cpp:593
shop_describe
int shop_describe(const object *op)
Give the player a description of the shop on their current map.
Definition: shop.cpp:1128
player::ranges
object * ranges[range_size]
Object for each range.
Definition: player.h:118
SK_LITERACY
@ SK_LITERACY
Literacy.
Definition: skills.h:27
remove_trap
int remove_trap(object *op, object *skill)
This skill will disarm any previously discovered trap.
Definition: skills.cpp:1320
SK_DET_CURSE
@ SK_DET_CURSE
Detect curse.
Definition: skills.h:33
archetype::clone
object clone
An object from which to do object_copy()
Definition: object.h:487
add_string
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
apply_anim_suffix
void apply_anim_suffix(object *who, const char *suffix)
Applies a compound animation to an object.
Definition: anim.cpp:150
FLAG_CAN_USE_SKILL
#define FLAG_CAN_USE_SKILL
The monster can use skills.
Definition: define.h:308
SK_BOWYER
@ SK_BOWYER
Bowyer.
Definition: skills.h:23
LOCKED_DOOR
@ LOCKED_DOOR
Definition: object.h:128
do_skill
int do_skill(object *op, object *part, object *skill, int dir, const char *string)
Main skills use function-similar in scope to cast_spell().
Definition: skill_util.cpp:442
skill_faces
const Face * skill_faces[MAX_SKILLS]
Will contain the face numbers for the skills, initialized by init_skill().
Definition: skill_util.cpp:61
object::face
const Face * face
Face with colors.
Definition: object.h:341
exp_level
int exp_level(int64_t exp)
Returns the level for a given exp.
Definition: living.cpp:1897
MSG_TYPE_ATTACK
#define MSG_TYPE_ATTACK
Attack related messages.
Definition: newclient.h:413
SK_EVOCATION
@ SK_EVOCATION
Evocation.
Definition: skills.h:54
player::tmp_invis
uint32_t tmp_invis
Will invis go away when we attack?
Definition: player.h:142
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
object::magic
int8_t magic
Any magical bonuses to this item.
Definition: object.h:358
GET_MAP_MOVE_BLOCK
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Gets the blocking state of a square.
Definition: map.h:197
FOR_INV_FINISH
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:661
player::item_power
int16_t item_power
Total item power of objects equipped.
Definition: player.h:132
MSG_TYPE_SKILL_MISSING
#define MSG_TYPE_SKILL_MISSING
Don't have the skill.
Definition: newclient.h:591
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Changes experience to a player/monster.
Definition: living.cpp:2179
do_skill_cb_data::skill
object * skill
Definition: skill_util.cpp:409
rangetype
rangetype
What range is currently selected by the player.
Definition: player.h:28
SK_BARGAINING
@ SK_BARGAINING
Bargaining.
Definition: skills.h:28
level_exp
int64_t level_exp(int level, double expmul)
Returns how much experience is needed for a player to become the given level.
Definition: living.cpp:1885
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
player::shoottype
rangetype shoottype
Which range-attack is being used by player.
Definition: player.h:114
pray
int pray(object *pl, object *skill)
Praying skill handling.
Definition: skills.cpp:1388
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:254
SK_SUMMONING
@ SK_SUMMONING
Summoning.
Definition: skills.h:52
sproto.h
SK_AIR_MAGIC
@ SK_AIR_MAGIC
Air magic, unused.
Definition: skills.h:59
skill_attack
void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill)
Core routine for use when we attack using a skills system.
Definition: skill_util.cpp:1302
do_each_skill
static void do_each_skill(archetype *at)
Definition: skill_util.cpp:78
living::sp
int16_t sp
Spell points.
Definition: living.h:42
MAX_SKILLS
#define MAX_SKILLS
This is the maximum number of skills the game may handle.
Definition: skills.h:70
MSG_TYPE_VICTIM_WAS_HIT
#define MSG_TYPE_VICTIM_WAS_HIT
Player was hit by something.
Definition: newclient.h:655
skill_throw
int skill_throw(object *op, object *part, int dir, object *skill)
Throwing skill handling.
Definition: skills.cpp:2283
get_skill_client_code
int get_skill_client_code(const char *skill_name)
Return the code of the skill for a client, the index in the skill_names array.
Definition: skill_util.cpp:114
living::Int
int8_t Int
Definition: living.h:36
clear_skill
void clear_skill(object *who)
This function just clears the chosen_skill and range_skill values in the player.
Definition: skill_util.cpp:396
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.cpp:42
object::facing
int8_t facing
Object is oriented/facing that way.
Definition: object.h:345
attack_melee_weapon
static void attack_melee_weapon(object *op, int dir, const char *string, object *skill)
This handles melee weapon attacks -b.t.
Definition: skill_util.cpp:1416
NDI_BLACK
#define NDI_BLACK
Definition: newclient.h:246
fatal
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:595
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
find_best_player_hth_skill
static object * find_best_player_hth_skill(object *op)
Finds the best unarmed skill the player has, and returns it.
Definition: skill_util.cpp:1074
adjust_skill_tool
static object * adjust_skill_tool(object *who, object *skill, object *skill_tool)
This returns specified skill if it can be used, potentially using tool to help.
Definition: skill_util.cpp:147
object::current_weapon
object * current_weapon
Pointer to the weapon currently used.
Definition: object.h:380
SK_FLAME_TOUCH
@ SK_FLAME_TOUCH
Flame-touch.
Definition: skills.h:37
SK_JUMPING
@ SK_JUMPING
Jumping.
Definition: skills.h:29
SK_HIDING
@ SK_HIDING
Hiding.
Definition: skills.h:21
SK_LEVITATION
@ SK_LEVITATION
Levitation.
Definition: skills.h:51
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Constructs a loop iterating over all objects of a map tile.
Definition: define.h:707
do_skill_cb_data::part
object * part
Definition: skill_util.cpp:408
PERM_EXP
#define PERM_EXP(exptotal)
Convert saved total experience into permanent experience.
Definition: global.h:236
SK_LOCKPICKING
@ SK_LOCKPICKING
Lockpicking.
Definition: skills.h:20
P_IS_ALIVE
#define P_IS_ALIVE
Something alive is on this space.
Definition: map.h:242
MSG_TYPE_SKILL_ERROR
#define MSG_TYPE_SKILL_ERROR
Doing something wrong.
Definition: newclient.h:592
SK_WOODSMAN
@ SK_WOODSMAN
Woodsman.
Definition: skills.h:40
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
spells.h
FLAG_READY_WEAPON
#define FLAG_READY_WEAPON
(Monster or Player) has a weapon readied
Definition: define.h:321
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Failure in using skill.
Definition: newclient.h:594
use_skill
int use_skill(object *op, const char *string)
Similar to invoke command, it executes the skill in the direction that the user is facing.
Definition: skill_util.cpp:996
is_dragon_pl
int is_dragon_pl(const object *op)
Checks if player is a dragon.
Definition: player.cpp:123
get_learn_spell
int get_learn_spell(int stat)
Definition: living.cpp:2377
SK_PUNCHING
@ SK_PUNCHING
Punching.
Definition: skills.h:36
MSG_TYPE_SKILL_LIST
#define MSG_TYPE_SKILL_LIST
List of skills.
Definition: newclient.h:596
SK_WRAITH_FEED
@ SK_WRAITH_FEED
Wraith feed.
Definition: skills.h:57
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
This rolls up wall, blocks_magic, blocks_view, etc, all into one function that just returns a P_.
Definition: map.cpp:280
mapstruct
This is a game-map.
Definition: map.h:320
AP_NOPRINT
#define AP_NOPRINT
Don't print messages - caller will do that may be some that still print.
Definition: define.h:569
sstring
const typedef char * sstring
Definition: sstring.h:2
floor
Magical Runes Runes are magical inscriptions on the dungeon floor
Definition: runes-guide.txt:3
living::Cha
int8_t Cha
Definition: living.h:36
object::skill
sstring skill
Name of the skill this object uses/grants.
Definition: object.h:329
SK_STEALING
@ SK_STEALING
Stealing.
Definition: skills.h:26
shop.h
FLAG_APPLIED
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:222
object::msg
sstring msg
If this is a book/sign/magic mouth/etc.
Definition: object.h:330
unpaid_count::pl
object * pl
Definition: shop.cpp:677
SKILL_TOOL
@ SKILL_TOOL
Allows the use of a skill.
Definition: object.h:194
assets.h
SK_MISSILE_WEAPON
@ SK_MISSILE_WEAPON
Missile weapon.
Definition: skills.h:43
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:370
do_harvest
void do_harvest(object *pl, int dir, object *skill)
Player is trying to harvest something.
Definition: c_misc.cpp:2260
SK_SUBTRACT_SKILL_EXP
#define SK_SUBTRACT_SKILL_EXP
Used when removing exp.
Definition: skills.h:81
apply_special
int apply_special(object *who, object *op, int aflags)
Apply an object.
Definition: apply.cpp:818
SK_ONE_HANDED_WEAPON
@ SK_ONE_HANDED_WEAPON
One handed weapon.
Definition: skills.h:42
do_skill_cb_data
Definition: skill_util.cpp:406
show_skills
void show_skills(object *op, const char *parms)
Displays a player's skill list, and some other non skill related info (god, max weapon improvements,...
Definition: skill_util.cpp:892
do_skill_cb
bool do_skill_cb(player *, void *data)
Definition: skill_util.cpp:414
UP_OBJ_FACE
#define UP_OBJ_FACE
Only thing that changed was the face.
Definition: object.h:533
player::party
partylist * party
Party this player is part of.
Definition: player.h:205
steal
int steal(object *op, int dir, object *skill)
Main stealing function.
Definition: skills.cpp:288
data
====Textual A command containing textual data has data fields separated by one ASCII space character. word::A sequence of ASCII characters that does not contain the space or nul character. This is to distinguish it from the _string_, which may contain space characters. Not to be confused with a machine word. int::A _word_ containing the textual representation of an integer. Not to be confused with any of the binary integers in the following section. Otherwise known as the "string value of integer data". Must be parsed, e.g. using `atoi()` to get the actual integer value. string::A sequence of ASCII characters. This must only appear at the end of a command, since spaces are used to separate fields of a textual message.=====Binary All multi-byte integers are transmitted in network byte order(MSB first). int8::1-byte(8-bit) integer int16::2-byte(16-bit) integer int32::4-byte(32-bit) integer lstring::A length-prefixed string, which consists of an `int8` followed by that many bytes of the actual string. This is used to transmit a string(that may contain spaces) in the middle of binary data. l2string::Like _lstring_, but is prefixed with an `int16` to support longer strings Implementation Notes ~~~~~~~~~~~~~~~~~~~~ - Typical implementations read two bytes to determine the length of the subsequent read for the actual message, then read and parse the data from each message according to the commands described below. To send a message, the sender builds the message in a buffer, counts the length of the message, sends the length, and finally sends the actual message. TIP:Incorrectly transmitting or receiving the `length` field can lead to apparent "no response" issues as the client or server blocks to read the entire length of the message. - Since the protocol is highly interactive, it may be useful to set `TCP_NODELAY` on both the client and server. - If you are using a language with a buffered output stream, remember to flush the stream after a complete message. - If the connection is lost(which will also happen if the output buffer overflowing), the player is saved and the server cleans up. This does open up some abuses, but there is no perfect solution here. - The server only reads data from the socket if the player has an action. This isn 't really good, since many of the commands below might not be actual commands for the player. The alternative is to look at the data, and if it is a player command and there isn 't time, store it away to be processed later. But this increases complexity, in that the server must start buffering the commands. Fortunately, for now, there are few such client commands. Commands -------- In the documentation below, `S->C` represents a message to the client from the server, and `C->S` represents a message to the server from the client. Commands are documented in a brief format like:C->S:version< csval >[scval[vinfo]] Fields are enclosed like `< this >`. Optional fields are denoted like `[this]`. Spaces that appear in the command are literal, i.e. the<< _version > > command above uses spaces to separate its fields, but the command below does not:C->S:accountlogin< name >< password > As described in<< _messages > >, if a command contains data, then the command is separated from the data by a literal space. Many of the commands below refer to 'object tags'. Whenever the server creates an object, it creates a unique tag for that object(starting at 1 when the server is first run, and ever increasing.) Tags are unique, but are not consistent between runs. Thus, the client can not store tags when it exits and hope to re-use them when it joins the server at a later time - tags are only valid for the current connection. The protocol commands are broken into various sections which based somewhat on what the commands are for(ie, item related commands, map commands, image commands, etc.) In this way, all the commands related to similar functionality is in the same place. Initialization ~~~~~~~~~~~~~~ version ^^^^^^^ C->S:version< csval >[scval[vinfo]] S->C:version< csval >[scval[vinfo]] Used by the client and server to exchange which version of the Crossfire protocol they understand. Neither send this in response to the other - they should both send this shortly after a connection is established. csval::int, version level of C->S communications scval::int, version level of S->C communications vinfo::string, that is purely for informative that general client/server info(ie, javaclient, x11client, winclient, sinix server, etc). It is purely of interest of server admins who can see what type of clients people are using.=====Version ID If a new command is added to the protocol in the C->S direction, then the version number in csval will get increased. Likewise, the same is true for the scval. The version are currently integers, in the form ABCD. A=1, and will likely for quite a while. This will only really change if needed from rollover of B. B represents major protocol changes - if B mismatches, the clients will be totally unusable. Such an example would be change of map or item sending commands(either new commands or new format.) C represents more minor but still significant changes - clients might still work together, but some features that used to work may now fail due to the mismatch. An example may be a change in the meaning of some field in some command - providing the field is the same size, it still should be decoded properly, but the meaning won 't be processed properly. D represents very minor changes or new commands. Things should work no worse if D does not match, however if they do match, some new features might be included. An example of the would be the C->S mark command to mark items. Server not understanding this just means that the server can not process it, and will ignore it.=====Handling As far as the client is concerned, its _scval_ must be at least equal to the server, and its _csval_ should not be newer than the server. The server does not care about the version command it receives right now - all it currently does is log mismatches. In theory, the server should keep track of what the client has, and adjust the commands it sends respectively in the S->C direction. The server is resilant enough that it won 't crash with a version mismatch(however, client may end up sending commands that the server just ignores). It is really up to the client to enforce versioning and quit if the versions don 't match. NOTE:Since all packets have the length as the first 2 bytes, all that either the client or server needs to be able to do is look at the first string and see if it understands it. If not, it knows how many bytes it can skip. As such, exact version matches should not be necessary for proper operation - however, both the client and server needs to be coded to handle such cases.=====History _scval_ and _vinfo_ were added in version 1020. Before then, there was only one version sent in the version command. NOTE:For the most part, this has been obsoleted by the setup command which always return status and whether it understood the command or not. However there are still some cases where using this versioning is useful - an example it the addition of the requestinfo/replyinfo commands - the client wants to wait for acknowledge of all the replyinfo commands it has issued before sending the addme command. However, if the server doesn 't understand these options, the client will never get a response. With the versioning, the client can look at the version and know if it should wait for a response or if the server will never send back. setup ^^^^^ C->S, S->C:setup< option1 >< value1 >< option2 >< value2 > ... Sent by the client to request protocol option changes. This can be at any point during the life of a connection, but usually sent at least once right after the<< _version > > command. The server responds with a message in the same format confirming what configuration options were set. The server only sends a setup command in response to one from the client. The sc_version should be updated in the server if commands have been obsoleted such that old clients may not be able to play. option::word, name of configuration option value::word, value of configuration option. May need further parsing according to the setup options below=====Setup Options There are really 2 set of setup commands here:. Those that control preferences of the client(how big is the map, what faceset to use, etc). . Those that describe capabilities of the client(client supports this protocol command or that) .Setup Options[options="autowidth,header"]|===========================|Command|Description|beat|Ask the server to enable heartbeat support. When heartbeat is enabled, the client must send the server a command every three seconds. If no commands need to be sent, use the `beat` no-op command. Clients that do not contact the server within the interval are assumed to have a temporary connection failure.|bot(0/1 value)|If set to 1, the client will not be considered a player when updating information to the metaserver. This is to avoid having a server with many bots appear more crowded than others.|darkness(0/1 value)|If set to 1(default), the server will send darkness information in the map protocol commands. If 0, the server will not include darkness, thus saving a minor amount of bandwidth. Since the client is free to ignore the darkness information, this does not allow the client to cheat. In the case of the old 'map' protocol command, turning darkness off will result in the masking faces not getting sent to the client.|extended_stats(0/1 value)|If set to 1, the server will send the CS_STAT_RACE_xxx and CS_STAT_BASE_xxx values too, so the client can display various status related to statistics. Default is 0.|facecache(0/1)|Determines if the client is caching images(1) or wants the images sent to it without caching them(0). Default is 0. This replaces the setfacemode command.|faceset(8 bit)|Faceset the client wishes to use. If the faceset is not valid, the server returns the faceset the client will be using(default 0).|loginmethod(8 bit)|Client sends this to server to note login support. This is basically used as a subset of the csversion/scversion to find out what level of login support the server and client support. Current defined values:0:no advanced support - only legacy login method 1:account based login(described more below) 2:new character creation support This list may grow - for example, advanced character creation could become a feature.|map2cmd:(1)|This indicates client support for the map2 protocol command. See the map2 protocol details above for the main differences. Obsolete:This is the only supported mode now, but many clients use it as a sanity check for protocol versions, so the server still replies. It doesn 't do anything with the data|mapsize(int x) X(int y)|Sets the map size to x X y. Note the spaces here are only for clarity - there should be no spaces when actually sent(it should be 11x11 or 25x25). The default map size unless changed is 11x11. The minimum map size the server will allow is 9x9(no technical reason this could be smaller, but I don 't think the game would be smaller). The maximum map size supported in the current protocol is 63x63. However, each server can have its maximum map size sent to most any value. If the client sends an invalid mapsize command or a mapsize of 0x0, the server will respond with a mapsize that is the maximum size the server supports. Thus, if the client wants to know the maximum map size, it can just do a 'mapsize 0x0' or 'mapsize' and it will get the maximum size back. The server will constrain the provided mapsize x &y to the configured minumum and maximums. For example, if the maximum map size is 25x25, the minimum map size is 9x9, and the client sends a 31x7 mapsize request, the mapsize will be set to 25x9 and the server will send back a mapsize 25x9 setup command. When the values are valid, the server will send back a mapsize XxY setup command. Note that this is from its parsed values, so it may not match stringwise with what the client sent, but will match 0 wise. For example, the client may send a 'mapsize 025X025' command, in which case the server will respond with a 'mapsize 25x25' command - the data is functionally the same. The server will send an updated map view when this command is sent.|notifications(int value)|Value indicating what notifications the client accepts. It is incremental, a value means "all notifications till this level". The following levels are supported:1:quest-related notifications("addquest" and "updquest") 2:knowledge-related notifications("addknowledge") 3:character status flags(overloaded, blind,...)|num_look_objects(int value)|The maximum number of objects shown in the ground view. If more objects are present, fake objects are created for selecting the previous/next group of items. Defaults to 50 if not set. The server may adjust the given value to a suitable one data
Definition: protocol.txt:379
player::repeat_func_data
void * repeat_func_data
Arguments to pass into repeat_func.
Definition: player.h:233
SK_PYROMANCY
@ SK_PYROMANCY
Pyromancy.
Definition: skills.h:53
calc_skill_exp
int64_t calc_skill_exp(const object *who, const object *op, const object *skill)
Calculates amount of experience can be gained for successful use of a skill.
Definition: skill_util.cpp:699
init_skills
void init_skills(void)
This just sets up the skill_names table above.
Definition: skill_util.cpp:97
do_skill_cb_data::op
object * op
Definition: skill_util.cpp:407
do_skill_cb_data::dir
int dir
Definition: skill_util.cpp:410
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.cpp:316
SK_SMITHERY
@ SK_SMITHERY
Smithery.
Definition: skills.h:22
a
Magical Runes Runes are magical inscriptions on the dungeon which cast a spell or detonate when something steps on them Flying objects don t detonate runes Beware ! Runes are invisible most of the time They are only visible occasionally ! There are several runes which are there are some special runes which may only be called with the invoke and people may apply it to read it Maybe useful for mazes ! This rune will not nor is it ordinarily invisible Partial Visibility of they ll be visible only part of the time They have a(your level/2) chance of being visible in any given round
SK_THAUMATURGY
@ SK_THAUMATURGY
Thaumaturgy.
Definition: skills.h:48
OB_TYPE_MOVE_BLOCK
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Basic macro to see if if ob1 can not move onto a space based on the 'type' move_block parameter Add c...
Definition: define.h:418
object::stats
living stats
Str, Con, Dex, etc.
Definition: object.h:378
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Successfully used skill.
Definition: newclient.h:593
range_skill
@ range_skill
Use skill.
Definition: player.h:35
attack_ob
int attack_ob(object *op, object *hitter)
Simple wrapper for attack_ob_simple(), will use hitter's values.
Definition: attack.cpp:937
SK_FIND_TRAPS
@ SK_FIND_TRAPS
Find traps.
Definition: skills.h:34
player_start_repeat
void player_start_repeat(player *pl, const char *action, repeat_cb cb)
Start repeating an action.
Definition: player.cpp:4549
SK_USE_MAGIC_ITEM
@ SK_USE_MAGIC_ITEM
Use magic item.
Definition: skills.h:45
freearr_x
short freearr_x[SIZEOFFREE]
X offset when searching around a spot.
Definition: object.cpp:299
hide
int hide(object *op, object *skill)
Main hide handling.
Definition: skills.cpp:505
player::repeat_func
repeat_cb repeat_func
If not NULL, automatically repeat the action repeat_func until interrupted.
Definition: player.h:231
link_player_skills
void link_player_skills(object *op)
This function goes through the player inventory and sets up the last_skills[] array in the player obj...
Definition: player.cpp:288
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
This returns the skill pointer of the given name (the one that accumulates exp, has the level,...
Definition: skill_util.cpp:209
SK_CLIMBING
@ SK_CLIMBING
Climbing.
Definition: skills.h:39
living.h
SK_THROWING
@ SK_THROWING
Throwing.
Definition: skills.h:44
do_skill_cb_data::string
const char * string
Definition: skill_util.cpp:411
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:654
object.h
SK_ORATORY
@ SK_ORATORY
Oratory.
Definition: skills.h:31
SK_WATER_MAGIC
@ SK_WATER_MAGIC
Water magic, unused.
Definition: skills.h:61
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:15
jump
int jump(object *pl, int dir, object *skill)
Jump skill handling.
Definition: skills.cpp:675