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)) {
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  }
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  do_skill(args->op, args->part, args->skill, args->dir, args->string);
417  return true;
418 }
419 
443 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
444  int success = 0, exp = 0;
445 
446  if (!skill)
447  return 0;
448 
449  /* The code below presumes that the skill points to the object that
450  * holds the exp, level, etc of the skill. So if this is a player
451  * go and try to find the actual real skill pointer, and if the
452  * the player doesn't have a bucket for that, create one.
453  */
454  if (skill->type != SKILL && op->type == PLAYER) {
455  object *tmp;
456 
458  if (!tmp) {
459  tmp = give_skill_by_name(op, skill->skill);
460  if (!tmp) {
461  LOG(llevError, "do_skill: asked for skill %s but couldn't find matching SKILL archetype.\n", skill->skill);
462  return 0;
463  }
464  }
465  skill = tmp;
466  }
467 
468  if (skill->anim_suffix)
469  apply_anim_suffix(op, skill->anim_suffix);
470 
471  switch (skill->subtype) {
472  case SK_LEVITATION:
473  /* Not 100% sure if this will work with new movement code -
474  * the levitation skill has move_type for flying, so when
475  * equipped, that should transfer to player, when not,
476  * shouldn't.
477  */
478  if (QUERY_FLAG(skill, FLAG_APPLIED)) {
481  "You come to earth.");
482  } else {
485  "You rise into the air!.");
486  }
487  fix_object(op);
488  success = 1;
489  break;
490 
491  case SK_STEALING:
492  exp = success = steal(op, dir, skill);
493  break;
494 
495  case SK_LOCKPICKING:
496  exp = success = pick_lock(op, dir, skill);
497  break;
498 
499  case SK_HIDING:
500  exp = success = hide(op, skill);
501  break;
502 
503  case SK_JUMPING:
504  success = jump(op, dir, skill);
505  break;
506 
507  case SK_INSCRIPTION:
508  exp = success = write_on_item(op, string, skill);
509  break;
510 
511  case SK_MEDITATION:
512  meditate(op, skill);
513  success = 1;
514  break;
515  /* note that the following 'attack' skills gain exp through hit_player() */
516 
517  case SK_KARATE:
518  attack_hth(op, dir, "karate-chopped", skill);
519  break;
520 
521  case SK_PUNCHING:
522  attack_hth(op, dir, "punched", skill);
523  break;
524 
525  case SK_FLAME_TOUCH:
526  attack_hth(op, dir, "flamed", skill);
527  break;
528 
529  case SK_CLAWING:
530  attack_hth(op, dir, "clawed", skill);
531  break;
532 
533  case SK_WRAITH_FEED:
534  attack_hth(op, dir, "fed upon", skill);
535  break;
536 
539  (void)attack_melee_weapon(op, dir, NULL, skill);
540  break;
541 
542  case SK_FIND_TRAPS:
543  exp = success = find_traps(op, skill);
544  break;
545 
546  case SK_SINGING:
547  exp = success = singing(op, dir, skill);
548  break;
549 
550  case SK_ORATORY:
551  exp = success = use_oratory(op, dir, skill);
552  break;
553 
554  case SK_SMITHERY:
555  case SK_BOWYER:
556  case SK_JEWELER:
557  case SK_ALCHEMY:
558  case SK_THAUMATURGY:
559  case SK_LITERACY:
560  case SK_WOODSMAN:
561  if (use_alchemy(op) == 0)
562  exp = success = skill_ident(op, skill);
563  break;
564 
565  case SK_DET_MAGIC:
566  case SK_DET_CURSE:
567  exp = success = skill_ident(op, skill);
568  break;
569 
570  case SK_DISARM_TRAPS:
571  exp = success = remove_trap(op, skill);
572  break;
573 
574  case SK_THROWING:
575  success = skill_throw(op, part, dir, skill);
576  break;
577 
578  case SK_SET_TRAP:
580  "This skill is not currently implemented.");
581  break;
582 
583  case SK_USE_MAGIC_ITEM:
584  case SK_MISSILE_WEAPON:
586  "There is no special attack for this skill.");
587  break;
588 
589  case SK_PRAYING:
590  if (op->contr) {
591  player_start_repeat(op->contr, "praying", do_skill_cb);
592  op->contr->repeat_func_data = calloc(1, sizeof(struct do_skill_cb_data));
594  *data = {op, part, skill, dir, string};
595  }
596  success = pray(op, skill);
597  break;
598 
599  case SK_BARGAINING:
600  success = shop_describe(op);
601  break;
602 
603  case SK_SORCERY:
604  case SK_EVOCATION:
605  case SK_PYROMANCY:
606  case SK_SUMMONING:
607  case SK_CLIMBING:
608  case SK_EARTH_MAGIC:
609  case SK_AIR_MAGIC:
610  case SK_FIRE_MAGIC:
611  case SK_WATER_MAGIC:
613  "This skill is already in effect.");
614  break;
615 
616  case SK_HARVESTING:
617  if (op->contr) {
619  op->contr->repeat_func_data = calloc(1, sizeof(struct do_skill_cb_data));
621  *data = {op, part, skill, dir, string};
622  }
623  do_harvest(op, dir, skill);
624  success = 0;
625  break;
626 
627  default: {
628  char name[MAX_BUF];
629 
631  LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
632  break;
633  }
634  }
635 
636  /* For players we now update the speed_left from using the skill.
637  * Monsters have no skill use time because of the random nature in
638  * which use_monster_skill is called already simulates this.
639  * If certain skills should take more/less time, that should be
640  * in the code for the skill itself.
641  */
642 
643  if (op->type == PLAYER)
644  op->speed_left -= 1.0;
645 
646  /* this is a good place to add experience for successful use of skills.
647  * Note that add_exp() will figure out player/monster experience
648  * gain problems.
649  */
650 
651  if (success && exp)
652  change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
653 
654  return success;
655 }
656 
691 int64_t calc_skill_exp(const object *who, const object *op, const object *skill) {
692  int64_t op_exp;
693  int op_lvl;
694  float base, value, lvl_mult;
695 
696  if (!skill)
697  skill = who;
698 
699  /* Oct 95 - where we have an object, I expanded our treatment
700  * to 3 cases:
701  * non-living magic obj, runes and everything else.
702  *
703  * If an object is not alive and magical we set the base exp higher to
704  * help out exp awards for skill_ident skills. Also, if
705  * an item is type RUNE, we give out exp based on stats.Cha
706  * and level (this was the old system) -b.t.
707  */
708 
709  if (!op) { /* no item/creature */
710  op_lvl = MAX(who->map->difficulty, 1);
711  op_exp = 0;
712  } else if (op->type == RUNE || op->type == TRAP) { /* all traps. If stats.Cha > 1 we use that
713  * for the amount of experience */
714  op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
715  op_lvl = op->level;
716  } else { /* all other items/living creatures */
717  op_exp = op->stats.exp;
718  op_lvl = op->level;
719  if (!QUERY_FLAG(op, FLAG_ALIVE)) { /* for ident/make items */
720  op_lvl += 5*abs(op->magic);
721  }
722  }
723 
724  if (op_lvl < 1)
725  op_lvl = 1;
726 
727  if (who->type != PLAYER) { /* for monsters only */
728  return ((int64_t)(op_exp*0.1)+1); /* we add one to insure positive value is returned */
729  }
730 
731  /* for players */
732  base = op_exp;
733  /* if skill really is a skill, then we can look at the skill archetype for
734  * base reward value (exp) and level multiplier factor.
735  */
736  if (skill->type == SKILL) {
737  base += skill->arch->clone.stats.exp;
738  if (settings.simple_exp) {
739  if (skill->arch->clone.level)
740  lvl_mult = (float)skill->arch->clone.level/100.0;
741  else
742  lvl_mult = 1.0; /* no adjustment */
743  } else {
744  if (skill->level)
745  lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
746  else
747  lvl_mult = 1.0;
748  }
749  } else {
750  /* Don't divide by zero here! */
751  lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
752  }
753 
754  /* assemble the exp total, and return value */
755 
756  value = base*lvl_mult;
757  if (value < 1)
758  value = 1; /* Always give at least 1 exp point */
759 
760 #ifdef SKILL_UTIL_DEBUG
761  LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op ? op->name : "", op_lvl);
762 #endif
763  return ((int64_t)value);
764 }
765 
783 int learn_skill(object *pl, object *scroll) {
784  object *tmp;
785 
786  if (!scroll->skill) {
787  LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
788  return 2;
789  }
790 
791  /* can't use find_skill_by_name because we want skills the player knows
792  * but can't use natively.
793  */
794 
795  tmp = NULL;
796  FOR_INV_PREPARE(pl, inv)
797  if (inv->type == SKILL && !strncasecmp(scroll->skill, inv->skill, strlen(scroll->skill))) {
798  tmp = inv;
799  break;
800  }
801  FOR_INV_FINISH();
802 
803  /* player already knows it */
804  if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
805  return 0;
806 
807  /* now a random change to learn, based on player Int.
808  * give bonus based on level - otherwise stupid characters
809  * might never be able to learn anything.
810  */
811  if (random_roll(0, 99, pl, PREFER_LOW) > (get_learn_spell(pl->stats.Int)+(pl->level/5)))
812  return 2; /* failure :< */
813 
814  if (!tmp)
815  tmp = give_skill_by_name(pl, scroll->skill);
816 
817  if (!tmp) {
818  LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
819  return 2;
820  }
821 
823  link_player_skills(pl);
824  return 1;
825 }
826 
838 static int clipped_percent(int64_t a, int64_t b) {
839  int rv;
840 
841  if (b <= 0)
842  return 0;
843 
844  rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
845 
846  if (rv < 0)
847  return 0;
848  else if (rv > 100)
849  return 100;
850 
851  return rv;
852 }
853 
854 
863 static int digits_in_long(int64_t num)
864 {
865  return num == 0 ? 1 : floor( log10( labs(num) ) ) + 1;
866 }
867 
884 void show_skills(object *op, const char *parms) {
885  const char *cp;
886  int i, num_skills_found = 0;
887  /* Need to have a pointer and use strdup for qsort to work properly */
888  char skills[MAX_SKILLS][MAX_BUF];
889  const char *search = parms;
890  bool long_format = false;
891 
892  if ( parms && strncmp(parms,"-l",2) == 0 ) {
893  long_format = true;
894  search += 2;
895  while ( *search && *search != ' ' ) ++search; /* move past other parameters */
896  while ( *search == ' ' ) ++search;
897  }
898  FOR_INV_PREPARE(op, tmp) {
899  if (tmp->type == SKILL) {
900  if (search && strstr(tmp->name, search) == NULL)
901  continue;
902 
904  if ( long_format ) {
905  int perm_level = exp_level(tmp->total_exp * settings.permanent_exp_ratio / 100);
906  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 ")%-*sperm lvl:%3d (xp:%" FMT64 "/%" FMT64 ") ",
907  tmp->name, tmp->level,
908  tmp->stats.exp,
909  level_exp(tmp->level+1, op->expmul),
910  1+2*digits_in_long(op->stats.exp)-digits_in_long(tmp->stats.exp)-digits_in_long(level_exp(tmp->level+1, op->expmul)),
911  "",
912  perm_level,
913  tmp->total_exp * settings.permanent_exp_ratio / 100,
914  level_exp(perm_level+1, op->expmul));
915  } else {
916  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 "/%d%%)",
917  tmp->name, tmp->level,
918  tmp->stats.exp,
919  level_exp(tmp->level+1, op->expmul),
920  clipped_percent(PERM_EXP(tmp->total_exp), tmp->stats.exp));
921  }
922  } else {
923  snprintf(skills[num_skills_found++], MAX_BUF, "[fixed]%-20slvl:%3d (xp:%" FMT64 "/%" FMT64 ")",
924  tmp->name, tmp->level,
925  tmp->stats.exp,
926  level_exp(tmp->level+1, op->expmul));
927  }
928  /* I don't know why some characters get a bunch of skills, but
929  * it sometimes happens (maybe a leftover from buggier earlier code
930  * and those character are still about). In any case, lets handle
931  * it so it doesn't crash the server - otherwise, one character may
932  * crash the server numerous times.
933  */
934  if (num_skills_found >= MAX_SKILLS) {
936  "Your character has too many skills.\n"
937  "Something isn't right - contact the server admin");
938  break;
939  }
940  }
941  } FOR_INV_FINISH();
942 
943  if ( search && *search ) {
945  "Player skills%s: (matching '%s')", long_format ? " (long format)":"",search);
946  } else {
948  "Player skills%s:", long_format ? " (long format)":"");
949  }
950 
951  if (num_skills_found > 1)
952  qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
953 
954  for (i = 0; i < num_skills_found; i++) {
956  skills[i]);
957  }
958 
959  cp = determine_god(op);
960  if (strcmp(cp, "none") == 0)
961  cp = NULL;
962 
964  "You can handle %d weapon improvements.\n"
965  "You worship %s.\n"
966  "Your equipped item power is %d out of %d\n",
967  op->level/5+5,
968  cp ? cp : "no god at current time",
969  op->contr->item_power, op->level);
970 }
971 
988 int use_skill(object *op, const char *string) {
989  object *skop;
990  size_t len;
991 
992  if (!string)
993  return 0;
994 
995  skop = NULL;
996  FOR_INV_PREPARE(op, tmp) {
997  if (tmp->type == SKILL
999  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
1000  skop = tmp;
1001  break;
1002  } else if (tmp->type == SKILL_TOOL
1003  && !QUERY_FLAG(tmp, FLAG_UNPAID) /* Holy symbols could be used unpaid w/o this */
1004  && !strncasecmp(string, tmp->skill, MIN(strlen(string), strlen(tmp->skill)))) {
1005  skop = tmp;
1006  break;
1007  }
1008  } FOR_INV_FINISH();
1009  if (!skop) {
1011  "Unable to find skill %s",
1012  string);
1013  return 0;
1014  }
1015 
1016  len = strlen(skop->skill);
1017 
1018  /* All this logic goes and skips over the skill name to find any
1019  * options given to the skill. Its pretty simple - if there
1020  * are extra parameters (as determined by string length), we
1021  * want to skip over any leading spaces.
1022  */
1023  if (len >= strlen(string)) {
1024  string = NULL;
1025  } else {
1026  string += len;
1027  while (*string == 0x20)
1028  string++;
1029  if (strlen(string) == 0)
1030  string = NULL;
1031  }
1032 
1033 #ifdef SKILL_UTIL_DEBUG
1034  LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
1035 #endif
1036 
1037  /* Change to the new skill, then execute it. */
1038  if (do_skill(op, op, skop, op->facing, string))
1039  return 1;
1040 
1041  return 0;
1042 }
1043 
1066 static object *find_best_player_hth_skill(object *op) {
1067  object *best_skill = NULL;
1068  int last_skill;
1069 
1070  if (op->contr->unarmed_skill) {
1071  /* command_unarmed_skill() already does these checks, and right
1072  * now I do not think there is any way to lose unarmed skills.
1073  * But maybe in the future there will be (polymorph?) so handle
1074  * it appropriately. MSW 2009-07-03
1075  *
1076  * Note that the error messages should only print out once when
1077  * the initial failure to switch skills happens, so the player
1078  * should not get spammed with tons of messages unless they have
1079  * no valid unarmed skill
1080  */
1081 
1082  best_skill = find_skill_by_name(op, op->contr->unarmed_skill);
1083 
1084  if (!best_skill) {
1086  "Unable to find skill %s - using default unarmed skill",
1087  op->contr->unarmed_skill);
1088  } else {
1089  size_t i;
1090 
1091  for (i = 0; i < sizeof(unarmed_skills); i++)
1092  if (best_skill->subtype == unarmed_skills[i])
1093  break;
1094  if (i < sizeof(unarmed_skills))
1095  return(best_skill);
1096  }
1097  /* If for some reason the unarmed_skill is not valid, we fall
1098  * through the processing below.
1099  */
1100  }
1101 
1102 
1103  /* Dragons are a special case - gros 25th July 2006 */
1104  /* Perhaps this special case should be removed and unarmed_skill
1105  * set to clawing for dragon characters? MSW 2009-07-03
1106  */
1107  if (is_dragon_pl(op)) {
1108  object *tmp;
1109 
1111  if (tmp) /* I suppose it should always be true - but maybe there's
1112  * draconic toothache ? :) */
1113  return tmp;
1114  }
1115 
1116  last_skill = sizeof(unarmed_skills);
1117  FOR_INV_PREPARE(op, tmp) {
1118  if (tmp->type == SKILL) {
1119  int i;
1120 
1121  /* The order in the array is preferred order. So basically,
1122  * we just cut down the number to search - eg, if we find a skill
1123  * early on in flame touch, then we only need to look into the unarmed_array
1124  * to the entry before flame touch - don't care about the entries afterward,
1125  * because they are inferior skills.
1126  * if we end up finding the best skill (i==0) might as well return
1127  * right away - can't get any better than that.
1128  */
1129  for (i = 0; i < last_skill; i++) {
1130  if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
1131  best_skill = tmp;
1132  last_skill = i;
1133  if (i == 0)
1134  return best_skill;
1135  }
1136  }
1137  }
1138  } FOR_INV_FINISH();
1139  return best_skill;
1140 }
1141 
1155 static void do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
1156  int success;
1157 
1158  /* For Players only: if there is no ready weapon, and no "attack" skill
1159  * is readied either then try to find a skill for the player to use.
1160  * it is presumed that if skill is set, it is a valid attack skill (eg,
1161  * the caller should have set it appropriately). We still want to pass
1162  * through that code if skill is set to change to the skill.
1163  */
1164  if (op->type == PLAYER) {
1165  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1166  size_t i;
1167 
1168  if (!skill) {
1169  /* See if the players chosen skill is a combat skill, and use
1170  * it if appropriate.
1171  */
1172  if (op->chosen_skill) {
1173  /* the list is 0-terminated, and talismans, which can be in chosen_skill,
1174  * have a subtype of 0, therefore don't check the 0 */
1175  for (i = 0; unarmed_skills[i] != 0; i++)
1176  if (op->chosen_skill->subtype == unarmed_skills[i]) {
1177  skill = op->chosen_skill;
1178  break;
1179  }
1180  }
1181  /* If we didn't find a skill above, look harder for a good skill */
1182  if (!skill) {
1184 
1185  if (!skill) {
1188  "You have no unarmed combat skills!");
1189  return;
1190  }
1191  }
1192  }
1193  if (skill != op->chosen_skill) {
1194  /* now try to ready the new skill */
1195  if (!change_skill(op, skill, 1)) { /* oh oh, trouble! */
1198  "Couldn't change to skill %s",
1199  skill->name);
1200  return;
1201  }
1202  }
1203  } else {
1204  /* Seen some crashes below where current_weapon is not set,
1205  * even though the flag says it is. So if current weapon isn't set,
1206  * do some work in trying to find the object to use.
1207  */
1208  if (!op->current_weapon) {
1209  object *tmp;
1210 
1211  LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
1213  if (!tmp) {
1214  LOG(llevError, "Could not find applied weapon on %s\n", op->name);
1215  op->current_weapon = NULL;
1216  return;
1217  } else {
1218  char weapon[MAX_BUF];
1219 
1220  query_name(tmp, weapon, MAX_BUF);
1221  op->current_weapon = tmp;
1222  }
1223  }
1224 
1225  /* Has ready weapon - make sure chosen_skill is set up properly */
1227  object *found_skill = find_skill_by_name(op, op->current_weapon->skill);
1228  // found_skill can be NULL -- TOCTTOU when item has mutated, e.g. dipping a bottle in a fountain
1229  change_skill(op, found_skill, 1);
1230  }
1231  }
1232  }
1233 
1234  /* lose invisibility/hiding status for running attacks */
1235 
1236  if (op->type == PLAYER && op->contr->tmp_invis) {
1237  op->contr->tmp_invis = 0;
1238  op->invisible = 0;
1239  op->hide = 0;
1241  }
1242 
1243  success = attack_ob(tmp, op);
1244 
1245  if (tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
1246  char op_name[MAX_BUF];
1247  if (!success) { // In case of miss, attack_ob didn't print anything
1248  query_name(tmp, op_name, MAX_BUF);
1251  "You miss %s!",
1252  op_name);
1253  } else if (string != NULL) { // If string is NULL, message was already displayed
1254  if (op->type == PLAYER) {
1255  query_name(tmp, op_name, MAX_BUF);
1258  "You %s %s!",
1259  string, op_name);
1260  } else if (tmp->type == PLAYER) {
1261  query_name(op, op_name, MAX_BUF);
1264  "%s %s you!",
1265  op_name, string);
1266  }
1267  }
1268  }
1269 }
1270 
1294 void skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
1295  int16_t tx, ty;
1296  mapstruct *m;
1297  int mflags;
1298 
1299  if (!dir)
1300  dir = pl->facing;
1301  tx = freearr_x[dir];
1302  ty = freearr_y[dir];
1303 
1304  /* If we don't yet have an opponent, find if one exists, and attack.
1305  * Legal opponents are the same as outlined in move_player_attack()
1306  */
1307 
1308  if (tmp == NULL) {
1309  m = pl->map;
1310  tx = pl->x+freearr_x[dir];
1311  ty = pl->y+freearr_y[dir];
1312 
1313  mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
1314  if (mflags&P_OUT_OF_MAP)
1315  return;
1316 
1317  /* space must be blocked for there to be anything interesting to do */
1318  if (!(mflags&P_IS_ALIVE)
1319  && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
1320  return;
1321  }
1322 
1323  FOR_MAP_PREPARE(m, tx, ty, tmp2)
1324  if ((QUERY_FLAG(tmp2, FLAG_ALIVE) && tmp2->stats.hp >= 0)
1325  || QUERY_FLAG(tmp2, FLAG_CAN_ROLL)
1326  || tmp2->type == LOCKED_DOOR) {
1327  /* Don't attack party members */
1328  if ((pl->type == PLAYER && tmp2->type == PLAYER)
1329  && (pl->contr->party != NULL && pl->contr->party == tmp2->contr->party))
1330  return;
1331  tmp = tmp2;
1332  break;
1333  }
1334  FOR_MAP_FINISH();
1335  }
1336  if (!tmp) {
1337  if (pl->type == PLAYER)
1339  "There is nothing to attack!");
1340  return;
1341  }
1342 
1343  do_skill_attack(tmp, pl, string, skill);
1344 }
1345 
1364 static void attack_hth(object *pl, int dir, const char *string, object *skill) {
1365  object *weapon;
1366 
1367  if (QUERY_FLAG(pl, FLAG_READY_WEAPON)) {
1368  weapon = object_find_by_type_applied(pl, WEAPON);
1369  if (weapon != NULL) {
1370  if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
1371  char weaponname[MAX_BUF];
1372 
1373  query_name(weapon, weaponname, MAX_BUF);
1376  "You are unable to unwield %s in order to attack with %s.",
1377  weaponname, skill->name);
1378  return;
1379  } else {
1381  "You unwield your weapon in order to attack.");
1382  }
1383  }
1384  }
1385  skill_attack(NULL, pl, dir, string, skill);
1386 }
1387 
1408 static void attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
1409  if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
1410  if (op->type == PLAYER)
1412  "You have no ready weapon to attack with!");
1413  return;
1414  }
1415  skill_attack(NULL, op, dir, string, skill);
1416 }
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:264
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
Error, serious thing.
Definition: logger.h:11
learn_skill
int learn_skill(object *pl, object *scroll)
Player is trying to learn a skill.
Definition: skill_util.cpp:783
mapstruct::difficulty
uint16_t difficulty
What level the player should be to play here.
Definition: map.h:336
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:58
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:1155
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:259
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
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:1771
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:863
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
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
SK_EARTH_MAGIC
@ SK_EARTH_MAGIC
Earth magic, unused.
Definition: skills.h:60
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:1364
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:838
query_name
void query_name(const object *op, char *buf, size_t size)
Describes an item.
Definition: item.cpp:594
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:443
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:195
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:1393
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:252
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:1294
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:2286
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:1408
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:590
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:1066
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:232
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:240
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
do_skill_cb
bool do_skill_cb(player *pl, void *data)
Definition: skill_util.cpp:414
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:988
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:300
mapstruct
This is a game-map.
Definition: map.h:318
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:2258
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:884
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
meditate
void meditate(object *pl, object *skill)
Meditation skill handling.
Definition: skills.cpp:1441
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:691
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
SK_SMITHERY
@ SK_SMITHERY
Smithery.
Definition: skills.h:22
skill
skill
Definition: arch-handbook.txt:585
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:4545
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
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:13
jump
int jump(object *pl, int dir, object *skill)
Jump skill handling.
Definition: skills.cpp:675
hide
uint32 hide
Definition: arch-handbook.txt:572