00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00046
00047
00048
00049 #define WANT_UNARMED_SKILLS
00050
00051 #include <global.h>
00052 #include <object.h>
00053 #ifndef __CEXTRACT__
00054 #include <sproto.h>
00055 #endif
00056 #include <living.h>
00057 #include <spells.h>
00058
00059 static int attack_hth(object *pl, int dir, const char *string, object *skill);
00060 static int attack_melee_weapon(object *op, int dir, const char *string, object *skill);
00061
00065 const char *skill_names[NUM_SKILLS];
00066
00071 void init_skills(void) {
00072 int i;
00073 archetype *at;
00074
00075 for (i = 0; i < NUM_SKILLS; i++)
00076 skill_names[i] = NULL;
00077
00078 for (at = first_archetype; at != NULL; at = at->next) {
00079 if (at->clone.type == SKILL) {
00080 if (at->clone.subtype >= sizeof(skill_names)/sizeof(*skill_names)) {
00081 LOG(llevError, "init_skills: invalid skill subtype %d for skill %s\n", at->clone.subtype, at->clone.skill);
00082 } else if (skill_names[at->clone.subtype] != NULL) {
00083 LOG(llevError, "init_skills: multiple skill using same subtype %d, %s, %s\n",
00084 at->clone.subtype, skill_names[at->clone.subtype], at->clone.skill);
00085 } else {
00086 skill_names[at->clone.subtype] = add_refcount(at->clone.skill);
00087 }
00088 }
00089 }
00090
00091
00092
00093
00094
00095 for (i = 1; i < NUM_SKILLS; i++) {
00096 if (!skill_names[i]) {
00097 LOG(llevError, "init_skills: skill subtype %d doesn't have a name?\n", i);
00098 skill_names[i] = add_string("dummy skill");
00099 }
00100 }
00101 }
00102
00112 void link_player_skills(object *op) {
00113 object *tmp;
00114
00115 for (tmp = op->inv; tmp; tmp = tmp->below) {
00116 if (tmp->type == SKILL) {
00117
00118 if (op->contr->last_skill_ob[tmp->subtype] && op->contr->last_skill_ob[tmp->subtype] != tmp) {
00119 LOG(llevError, "Multiple skills with the same subtype? %s, %s\n", op->contr->last_skill_ob[tmp->subtype]->skill, tmp->skill);
00120 }
00121 if (tmp->subtype >= NUM_SKILLS) {
00122 LOG(llevError, "Invalid subtype number %d (range 0-%d)\n", tmp->subtype, NUM_SKILLS);
00123 } else {
00124 op->contr->last_skill_ob[tmp->subtype] = tmp;
00125 op->contr->last_skill_exp[tmp->subtype] = -1;
00126 }
00127 }
00128 }
00129 }
00130
00153 static object *adjust_skill_tool(object *who, object *skill, object *skill_tool) {
00154 if (!skill && !skill_tool)
00155 return NULL;
00156
00157
00158 if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL) && (!skill_tool || QUERY_FLAG(skill_tool, FLAG_APPLIED) || strcmp(skill->skill, "clawing") == 0))
00159 return skill;
00160
00161
00162
00163
00164
00165 if (skill_tool) {
00166 if (!QUERY_FLAG(skill_tool, FLAG_APPLIED)) {
00167 if (apply_special(who, skill_tool, 0)) {
00168 if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
00169 return skill;
00170 else
00171 return NULL;
00172 }
00173 }
00174 if (!skill) {
00175 skill = give_skill_by_name(who, skill_tool->skill);
00176 link_player_skills(who);
00177 }
00178 return skill;
00179 }
00180 if (skill && QUERY_FLAG(skill, FLAG_CAN_USE_SKILL))
00181 return skill;
00182 else
00183 return NULL;
00184 }
00185
00207 object *find_skill_by_name(object *who, const char *name) {
00208 object *skill = NULL, *skill_tool = NULL, *tmp;
00209
00210 if (!name)
00211 return NULL;
00212
00213
00214
00215
00216
00217 for (tmp = who->inv; tmp != NULL; tmp = tmp->below) {
00218 if (tmp->type == SKILL
00219 && !strncasecmp(name, tmp->skill, strlen(name))
00220 && strlen(tmp->skill) >= strlen(name))
00221 skill = tmp;
00222
00223
00224
00225
00226 else if (tmp->type == SKILL_TOOL
00227 && !strncasecmp(name, tmp->skill, strlen(name))
00228 && strlen(tmp->skill) >= strlen(name)) {
00229 if (QUERY_FLAG(tmp, FLAG_APPLIED))
00230 skill_tool = tmp;
00231 else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
00232 skill_tool = tmp;
00233 }
00234 }
00235
00236 return adjust_skill_tool(who, skill, skill_tool);
00237 }
00238
00257 object *find_skill_by_number(object *who, int skillno) {
00258 object *skill = NULL, *skill_tool = NULL, *tmp;
00259
00260 if (skillno < 1 || skillno >= NUM_SKILLS)
00261 return NULL;
00262
00263 for (tmp = who->inv; tmp != NULL; tmp = tmp->below) {
00264 if (tmp->type == SKILL && tmp->subtype == skillno)
00265 skill = tmp;
00266
00267
00268
00269
00270 else if (tmp->type == SKILL_TOOL && tmp->subtype == skillno) {
00271 if (QUERY_FLAG(tmp, FLAG_APPLIED))
00272 skill_tool = tmp;
00273 else if (!skill_tool || !QUERY_FLAG(skill_tool, FLAG_APPLIED))
00274 skill_tool = tmp;
00275 }
00276 }
00277
00278 return adjust_skill_tool(who, skill, skill_tool);
00279 }
00280
00301 int change_skill(object *who, object *new_skill, int flag) {
00302 int old_range;
00303
00304 if (who->type != PLAYER)
00305 return 0;
00306
00307 old_range = who->contr->shoottype;
00308
00309 if (who->chosen_skill && who->chosen_skill == new_skill) {
00310
00311 if (who->type == PLAYER && !(flag&0x1))
00312 who->contr->shoottype = range_skill;
00313 return 1;
00314 }
00315
00316 if (!new_skill || who->chosen_skill)
00317 if (who->chosen_skill)
00318 apply_special(who, who->chosen_skill, AP_UNAPPLY|(flag&AP_NOPRINT));
00319
00320
00321 if (!new_skill)
00322 return 0;
00323
00324 if (apply_special(who, new_skill, AP_APPLY|(flag&AP_NOPRINT))) {
00325 return 0;
00326 }
00327 if (flag&0x1)
00328 who->contr->shoottype = old_range;
00329
00330 return 1;
00331 }
00332
00340 void clear_skill(object *who) {
00341 who->chosen_skill = NULL;
00342 CLEAR_FLAG(who, FLAG_READY_SKILL);
00343 if (who->type == PLAYER) {
00344 who->contr->ranges[range_skill] = NULL;
00345 if (who->contr->shoottype == range_skill)
00346 who->contr->shoottype = range_none;
00347 }
00348 }
00349
00373 int do_skill(object *op, object *part, object *skill, int dir, const char *string) {
00374 int success = 0, exp = 0;
00375 object *tmp;
00376
00377 if (!skill)
00378 return 0;
00379
00380
00381
00382
00383
00384
00385 if (skill->type != SKILL && op->type == PLAYER) {
00386 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
00387 if (tmp->type == SKILL && tmp->skill == skill->skill)
00388 break;
00389 }
00390 if (!tmp)
00391 tmp = give_skill_by_name(op, skill->skill);
00392 skill = tmp;
00393 }
00394
00395 if (skill->anim_suffix)
00396 apply_anim_suffix(op, skill->anim_suffix);
00397
00398 switch (skill->subtype) {
00399 case SK_LEVITATION:
00400
00401
00402
00403
00404
00405 if (QUERY_FLAG(skill, FLAG_APPLIED)) {
00406 CLEAR_FLAG(skill, FLAG_APPLIED);
00407 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00408 "You come to earth.", NULL);
00409 } else {
00410 SET_FLAG(skill, FLAG_APPLIED);
00411 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00412 "You rise into the air!.", NULL);
00413 }
00414 fix_object(op);
00415 success = 1;
00416 break;
00417
00418 case SK_STEALING:
00419 exp = success = steal(op, dir, skill);
00420 break;
00421
00422 case SK_LOCKPICKING:
00423 exp = success = pick_lock(op, dir, skill);
00424 break;
00425
00426 case SK_HIDING:
00427 exp = success = hide(op, skill);
00428 break;
00429
00430 case SK_JUMPING:
00431 success = jump(op, dir, skill);
00432 break;
00433
00434 case SK_INSCRIPTION:
00435 exp = success = write_on_item(op, string, skill);
00436 break;
00437
00438 case SK_MEDITATION:
00439 meditate(op, skill);
00440 success = 1;
00441 break;
00442
00443
00444 case SK_KARATE:
00445 (void)attack_hth(op, dir, "karate-chopped", skill);
00446 break;
00447
00448 case SK_PUNCHING:
00449 (void)attack_hth(op, dir, "punched", skill);
00450 break;
00451
00452 case SK_FLAME_TOUCH:
00453 (void)attack_hth(op, dir, "flamed", skill);
00454 break;
00455
00456 case SK_CLAWING:
00457 (void)attack_hth(op, dir, "clawed", skill);
00458 break;
00459
00460 case SK_WRAITH_FEED:
00461 (void)attack_hth(op, dir, "fed upon", skill);
00462 break;
00463
00464 case SK_ONE_HANDED_WEAPON:
00465 case SK_TWO_HANDED_WEAPON:
00466 (void)attack_melee_weapon(op, dir, NULL, skill);
00467 break;
00468
00469 case SK_FIND_TRAPS:
00470 exp = success = find_traps(op, skill);
00471 break;
00472
00473 case SK_SINGING:
00474 exp = success = singing(op, dir, skill);
00475 break;
00476
00477 case SK_ORATORY:
00478 exp = success = use_oratory(op, dir, skill);
00479 break;
00480
00481 case SK_SMITHERY:
00482 case SK_BOWYER:
00483 case SK_JEWELER:
00484 case SK_ALCHEMY:
00485 case SK_THAUMATURGY:
00486 case SK_LITERACY:
00487 case SK_WOODSMAN:
00488 if (use_alchemy(op) == 0)
00489 exp = success = skill_ident(op, skill);
00490 break;
00491
00492 case SK_DET_MAGIC:
00493 case SK_DET_CURSE:
00494 exp = success = skill_ident(op, skill);
00495 break;
00496
00497 case SK_DISARM_TRAPS:
00498 exp = success = remove_trap(op, skill);
00499 break;
00500
00501 case SK_THROWING:
00502 success = skill_throw(op, part, dir, string, skill);
00503 break;
00504
00505 case SK_SET_TRAP:
00506 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00507 "This skill is not currently implemented.", NULL);
00508 break;
00509
00510 case SK_USE_MAGIC_ITEM:
00511 case SK_MISSILE_WEAPON:
00512 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00513 "There is no special attack for this skill.", NULL);
00514 break;
00515
00516 case SK_PRAYING:
00517 success = pray(op, skill);
00518 break;
00519
00520 case SK_BARGAINING:
00521 success = describe_shop(op);
00522 break;
00523
00524 case SK_SORCERY:
00525 case SK_EVOCATION:
00526 case SK_PYROMANCY:
00527 case SK_SUMMONING:
00528 case SK_CLIMBING:
00529 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00530 "This skill is already in effect.", NULL);
00531 break;
00532
00533 case SK_HARVESTING:
00534 success = do_harvest(op, dir, skill);
00535 break;
00536
00537 default: {
00538 char name[MAX_BUF];
00539
00540 query_name(op, name, MAX_BUF);
00541 LOG(llevDebug, "%s attempted to use unknown skill: %d\n", name, op->chosen_skill->stats.sp);
00542 break;
00543 }
00544 }
00545
00546
00547
00548
00549
00550
00551
00552
00553 if (op->type == PLAYER)
00554 op->speed_left -= 1.0;
00555
00556
00557
00558
00559
00560
00561 if (success && exp)
00562 change_exp(op, exp, skill->skill, SK_SUBTRACT_SKILL_EXP);
00563
00564 return success;
00565 }
00566
00599 sint64 calc_skill_exp(object *who, object *op, object *skill) {
00600 sint64 op_exp = 0;
00601 int op_lvl = 0;
00602 float base, value, lvl_mult = 0.0;
00603
00604 if (!skill)
00605 skill = who;
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617 if (!op) {
00618 op_lvl = MAX(who->map->difficulty, 1);
00619 op_exp = 0;
00620 } else if (op->type == RUNE || op->type == TRAP) {
00621
00622 op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
00623 op_lvl = op->level;
00624 } else {
00625 op_exp = op->stats.exp;
00626 op_lvl = op->level;
00627 if (!QUERY_FLAG(op, FLAG_ALIVE)) {
00628 op_lvl += 5*abs(op->magic);
00629 }
00630 }
00631
00632 if (op_lvl < 1)
00633 op_lvl = 1;
00634
00635 if (who->type != PLAYER) {
00636 return ((sint64)(op_exp*0.1)+1);
00637 } else {
00638 base = op_exp;
00639
00640
00641
00642 if (skill->type == SKILL) {
00643 base += skill->arch->clone.stats.exp;
00644 if (settings.simple_exp) {
00645 if (skill->arch->clone.level)
00646 lvl_mult = (float)skill->arch->clone.level/100.0;
00647 else
00648 lvl_mult = 1.0;
00649 } else {
00650 if (skill->level)
00651 lvl_mult = ((float)skill->arch->clone.level*(float)op_lvl)/((float)skill->level*100.0);
00652 else
00653 lvl_mult = 1.0;
00654 }
00655 } else {
00656
00657 lvl_mult = (float)op_lvl/(float)(skill->level ? skill->level : 1);
00658 }
00659 }
00660
00661
00662
00663 value = base*lvl_mult;
00664 if (value < 1)
00665 value = 1;
00666
00667 #ifdef SKILL_UTIL_DEBUG
00668 LOG(llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
00669 #endif
00670 return ((sint64)value);
00671 }
00672
00690 int learn_skill(object *pl, object *scroll) {
00691 object *tmp;
00692
00693 if (!scroll->skill) {
00694 LOG(llevError, "skill scroll %s does not have skill pointer set.\n", scroll->name);
00695 return 2;
00696 }
00697
00698
00699
00700
00701
00702 for (tmp = pl->inv; tmp != NULL; tmp = tmp->below)
00703 if (tmp->type == SKILL && !strncasecmp(scroll->skill, tmp->skill, strlen(scroll->skill)))
00704 break;
00705
00706
00707 if (tmp && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL))
00708 return 0;
00709
00710
00711
00712
00713
00714 if (random_roll(0, 99, pl, PREFER_LOW) > (learn_spell[pl->stats.Int]+(pl->level/5)))
00715 return 2;
00716
00717 if (!tmp)
00718 tmp = give_skill_by_name(pl, scroll->skill);
00719
00720 if (!tmp) {
00721 LOG(llevError, "skill scroll %s does not have valid skill name (%s).\n", scroll->name, scroll->skill);
00722 return 2;
00723 }
00724
00725 SET_FLAG(tmp, FLAG_CAN_USE_SKILL);
00726 link_player_skills(pl);
00727 return 1;
00728 }
00729
00741 static int clipped_percent(sint64 a, sint64 b) {
00742 int rv;
00743
00744 if (b <= 0)
00745 return 0;
00746
00747 rv = (int)((100.0f*((float)a)/((float)b))+0.5f);
00748
00749 if (rv < 0)
00750 return 0;
00751 else if (rv > 100)
00752 return 100;
00753
00754 return rv;
00755 }
00756
00773 void show_skills(object *op, const char *search) {
00774 object *tmp = NULL;
00775 char buf[MAX_BUF];
00776 const char *cp;
00777 int i, num_skills_found = 0;
00778 static const char *const periods = "........................................";
00779
00780 char skills[NUM_SKILLS][MAX_BUF];
00781
00782 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
00783 if (tmp->type == SKILL) {
00784 if (search && strstr(tmp->name, search) == NULL)
00785 continue;
00786
00787 snprintf(buf, sizeof(buf), "%s%s", tmp->name, periods);
00788 buf[40] = 0;
00789
00790 if (settings.permanent_exp_ratio) {
00791 snprintf(skills[num_skills_found++], MAX_BUF, "%slvl:%3d (xp:%"FMT64"/%"FMT64"/%d%%)",
00792 buf, tmp->level,
00793 tmp->stats.exp,
00794 level_exp(tmp->level+1, op->expmul),
00795 clipped_percent(tmp->perm_exp, tmp->stats.exp));
00796 } else {
00797 snprintf(skills[num_skills_found++], MAX_BUF, "%slvl:%3d (xp:%"FMT64"/%"FMT64")",
00798 buf, tmp->level,
00799 tmp->stats.exp,
00800 level_exp(tmp->level+1, op->expmul));
00801 }
00802
00803
00804
00805
00806
00807
00808 if (num_skills_found >= NUM_SKILLS) {
00809 draw_ext_info(NDI_RED, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00810 "Your character has too many skills. "
00811 "Something isn't right - contact the server admin", NULL);
00812 break;
00813 }
00814 }
00815 }
00816
00817 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_LIST,
00818 "Player skills:", NULL);
00819
00820 if (num_skills_found > 1)
00821 qsort(skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *))strcmp);
00822
00823 for (i = 0; i < num_skills_found; i++) {
00824 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_LIST,
00825 skills[i], skills[i]);
00826 }
00827
00828 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_LIST,
00829 "You can handle %d weapon improvements.",
00830 "You can handle %d weapon improvements.",
00831 op->level/5+5);
00832
00833 cp = determine_god(op);
00834 if (strcmp(cp, "none") == 0)
00835 cp = NULL;
00836 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_LIST,
00837 "You worship %s.",
00838 "You worship %s.",
00839 cp ? cp : "no god at current time");
00840
00841 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_LIST,
00842 "Your equipped item power is %d out of %d\n",
00843 "Your equipped item power is %d out of %d\n",
00844 op->contr->item_power, op->level);
00845 }
00846
00863 int use_skill(object *op, const char *string) {
00864 object *skop;
00865 size_t len;
00866
00867 if (!string)
00868 return 0;
00869
00870 for (skop = op->inv; skop != NULL; skop = skop->below) {
00871 if (skop->type == SKILL
00872 && QUERY_FLAG(skop, FLAG_CAN_USE_SKILL)
00873 && !strncasecmp(string, skop->skill, MIN(strlen(string), strlen(skop->skill))))
00874 break;
00875 else if (skop->type == SKILL_TOOL
00876 && !strncasecmp(string, skop->skill, MIN(strlen(string), strlen(skop->skill))))
00877 break;
00878 }
00879 if (!skop) {
00880 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
00881 "Unable to find skill %s",
00882 "Unable to find skill %s",
00883 string);
00884 return 0;
00885 }
00886
00887 len = strlen(skop->skill);
00888
00889
00890
00891
00892
00893
00894 if (len >= strlen(string)) {
00895 string = NULL;
00896 } else {
00897 string += len;
00898 while (*string == 0x20)
00899 string++;
00900 if (strlen(string) == 0)
00901 string = NULL;
00902 }
00903
00904 #ifdef SKILL_UTIL_DEBUG
00905 LOG(llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
00906 #endif
00907
00908
00909 if (do_skill(op, op, skop, op->facing, string))
00910 return 1;
00911
00912 return 0;
00913 }
00914
00937 static object *find_best_player_hth_skill(object *op) {
00938 object *tmp, *best_skill = NULL;
00939 int dragon = is_dragon_pl(op), last_skill = sizeof(unarmed_skills), i;
00940
00941
00942 if (dragon) {
00943 tmp = find_skill_by_number(op, SK_CLAWING);
00944 if (tmp)
00945
00946 return tmp;
00947 }
00948 for (tmp = op->inv; tmp; tmp = tmp->below) {
00949 if (tmp->type == SKILL) {
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959 for (i = 0; i < last_skill; i++) {
00960 if (tmp->subtype == unarmed_skills[i] && QUERY_FLAG(tmp, FLAG_CAN_USE_SKILL)) {
00961 best_skill = tmp;
00962 last_skill = i;
00963 if (i == 0)
00964 return best_skill;
00965 }
00966 }
00967 }
00968 }
00969 return best_skill;
00970 }
00971
00987 static int do_skill_attack(object *tmp, object *op, const char *string, object *skill) {
00988 int success;
00989
00990
00991
00992
00993
00994
00995
00996 if (op->type == PLAYER) {
00997 if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
00998 size_t i;
00999
01000 if (!skill) {
01001
01002
01003
01004 if (op->chosen_skill) {
01005 for (i = 0; i < sizeof(unarmed_skills); i++)
01006 if (op->chosen_skill->subtype == unarmed_skills[i]) {
01007 skill = op->chosen_skill;
01008 break;
01009 }
01010 }
01011
01012 if (!skill) {
01013 skill = find_best_player_hth_skill(op);
01014
01015 if (!skill) {
01016 draw_ext_info(NDI_BLACK, 0, op,
01017 MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
01018 "You have no unarmed combat skills!", NULL);
01019 return 0;
01020 }
01021 }
01022 }
01023 if (skill != op->chosen_skill) {
01024
01025 if (!change_skill(op, skill, 1)) {
01026 draw_ext_info_format(NDI_UNIQUE, 0, tmp,
01027 MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01028 "Couldn't change to skill %s",
01029 "Couldn't change to skill %s",
01030 skill->name);
01031 return 0;
01032 }
01033 }
01034 } else {
01035
01036
01037
01038
01039 if (!op->current_weapon) {
01040 object *tmp;
01041
01042 LOG(llevError, "Player %s does not have current weapon set but flag_ready_weapon is set\n", op->name);
01043 for (tmp = op->inv; tmp; tmp = tmp->below)
01044 if (tmp->type == WEAPON && QUERY_FLAG(tmp, FLAG_APPLIED))
01045 break;
01046
01047 if (!tmp) {
01048 LOG(llevError, "Could not find applied weapon on %s\n", op->name);
01049 op->current_weapon = NULL;
01050 return 0;
01051 } else {
01052 char weapon[MAX_BUF];
01053
01054 query_name(tmp, weapon, MAX_BUF);
01055 op->current_weapon = tmp;
01056 }
01057 }
01058
01059
01060 if (!op->chosen_skill || op->current_weapon->skill != op->chosen_skill->skill) {
01061 change_skill(op, find_skill_by_name(op, op->current_weapon->skill), 1);
01062 }
01063 }
01064 }
01065
01066
01067
01068 if (op->type == PLAYER && op->contr->tmp_invis) {
01069 op->contr->tmp_invis = 0;
01070 op->invisible = 0;
01071 op->hide = 0;
01072 update_object(op, UP_OBJ_FACE);
01073 }
01074
01075 success = attack_ob(tmp, op);
01076
01077
01078
01079 if (success && string != NULL && tmp && !QUERY_FLAG(tmp, FLAG_FREED)) {
01080 char op_name[MAX_BUF];
01081 if(op->type==PLAYER) {
01082 query_name(tmp, op_name, MAX_BUF);
01083 draw_ext_info_format(NDI_UNIQUE, 0,op,
01084 MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_HIT,
01085 "You %s %s!",
01086 "You %s %s!",
01087 string,op_name);
01088 }
01089 else if(tmp->type==PLAYER) {
01090 query_name(op, op_name, MAX_BUF);
01091 draw_ext_info_format(NDI_UNIQUE, 0,tmp,
01092 MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT,
01093 "%s %s you!",
01094 "%s %s you!",
01095 op_name,string);
01096 }
01097 }
01098 return success;
01099 }
01100
01126 int skill_attack(object *tmp, object *pl, int dir, const char *string, object *skill) {
01127 sint16 tx, ty;
01128 mapstruct *m;
01129 int mflags;
01130
01131 if (!dir)
01132 dir = pl->facing;
01133 tx = freearr_x[dir];
01134 ty = freearr_y[dir];
01135
01136
01137
01138
01139
01140 if (tmp == NULL) {
01141 m = pl->map;
01142 tx = pl->x+freearr_x[dir];
01143 ty = pl->y+freearr_y[dir];
01144
01145 mflags = get_map_flags(m, &m, tx, ty, &tx, &ty);
01146 if (mflags&P_OUT_OF_MAP)
01147 return 0;
01148
01149
01150 if (!(mflags&P_IS_ALIVE)
01151 && !OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, tx, ty))) {
01152 return 0;
01153 }
01154
01155 for (tmp = GET_MAP_OB(m, tx, ty); tmp; tmp = tmp->above)
01156 if ((QUERY_FLAG(tmp, FLAG_ALIVE) && tmp->stats.hp >= 0)
01157 || QUERY_FLAG(tmp, FLAG_CAN_ROLL)
01158 || tmp->type == LOCKED_DOOR) {
01159
01160 if ((pl->type == PLAYER && tmp->type == PLAYER)
01161 && (pl->contr->party != NULL && pl->contr->party == tmp->contr->party))
01162 return 0;
01163 break;
01164 }
01165 }
01166 if (!tmp) {
01167 if (pl->type == PLAYER)
01168 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01169 "There is nothing to attack!", NULL);
01170 return 0;
01171 }
01172
01173 return do_skill_attack(tmp, pl, string, skill);
01174 }
01175
01196 static int attack_hth(object *pl, int dir, const char *string, object *skill) {
01197 object *enemy = NULL, *weapon;
01198
01199 if (QUERY_FLAG(pl, FLAG_READY_WEAPON))
01200 for (weapon = pl->inv; weapon; weapon = weapon->below) {
01201 if (weapon->type == WEAPON && QUERY_FLAG(weapon, FLAG_APPLIED)) {
01202 if (apply_special(pl, weapon, AP_UNAPPLY|AP_NOPRINT)) {
01203 char weaponname[MAX_BUF];
01204
01205 query_name(weapon, weaponname, MAX_BUF);
01206 draw_ext_info_format(NDI_UNIQUE, 0, pl,
01207 MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01208 "You are unable to unwield %s in order to attack with %s.",
01209 "You are unable to unwield %s in order to attack with %s.",
01210 weaponname, skill->name);
01211 return 0;
01212 } else {
01213 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01214 "You unwield your weapon in order to attack.", NULL);
01215 break;
01216 }
01217 }
01218 }
01219 return skill_attack(enemy, pl, dir, string, skill);
01220 }
01221
01244 static int attack_melee_weapon(object *op, int dir, const char *string, object *skill) {
01245 if (!QUERY_FLAG(op, FLAG_READY_WEAPON)) {
01246 if (op->type == PLAYER)
01247 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01248 "You have no ready weapon to attack with!", NULL);
01249 return 0;
01250 }
01251 return skill_attack(NULL, op, dir, string, skill);
01252 }