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
00033 #include <global.h>
00034 #include <object.h>
00035 #ifndef __CEXTRACT__
00036 #include <sproto.h>
00037 #endif
00038 #include <living.h>
00039 #include <skills.h>
00040 #include <spells.h>
00041 #include <book.h>
00042
00057 static int adj_stealchance(object *op, object *victim, int roll) {
00058 object *equip;
00059
00060 if (!op || !victim || !roll)
00061 return -1;
00062
00063
00064
00065
00066 if (op->type == PLAYER
00067 && op->body_used[BODY_ARMS] <= 0
00068 && op->body_info[BODY_ARMS]) {
00069 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00070 "But you have no free hands to steal with!", NULL);
00071 return -1;
00072 }
00073
00074
00075
00076
00077 if (!QUERY_FLAG(victim, FLAG_UNAGGRESSIVE))
00078 roll = roll/2;
00079
00080
00081
00082 if (QUERY_FLAG(victim, FLAG_SLEEP))
00083 roll = roll*3;
00084 else if (op->invisible)
00085 roll = roll*2;
00086
00087
00088
00089
00090 for (equip = op->inv; equip; equip = equip->below) {
00091 if (equip->type == WEAPON && QUERY_FLAG(equip, FLAG_APPLIED)) {
00092 roll -= equip->weight/10000;
00093 }
00094 if (equip->type == BOW && QUERY_FLAG(equip, FLAG_APPLIED))
00095 roll -= equip->weight/5000;
00096 if (equip->type == SHIELD && QUERY_FLAG(equip, FLAG_APPLIED)) {
00097 roll -= equip->weight/2000;
00098 }
00099 if (equip->type == ARMOUR && QUERY_FLAG(equip, FLAG_APPLIED))
00100 roll -= equip->weight/5000;
00101 if (equip->type == GLOVES && QUERY_FLAG(equip, FLAG_APPLIED))
00102 roll -= equip->weight/100;
00103 }
00104 if (roll < 0)
00105 roll = 0;
00106 return roll;
00107 }
00108
00127 static int attempt_steal(object *op, object *who, object *skill) {
00128 object *success = NULL, *tmp = NULL, *next;
00129 int roll = 0, chance = 0, stats_value;
00130 rv_vector rv;
00131 char name[MAX_BUF];
00132
00133 stats_value = ((who->stats.Dex+who->stats.Int)*3)/2;
00134
00135
00136
00137
00138
00139 if (op->type != PLAYER && QUERY_FLAG(op, FLAG_NO_STEAL)) {
00140 if (can_detect_enemy(op, who, &rv)) {
00141 npc_call_help(op);
00142 CLEAR_FLAG(op, FLAG_UNAGGRESSIVE);
00143 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00144 "Your attempt is prevented!", NULL);
00145 return 0;
00146 } else
00147 op->stats.Wis += (op->stats.Int/5)+1;
00148 if (op->stats.Wis > MAX_STAT)
00149 op->stats.Wis = MAX_STAT;
00150 }
00151 if (op->type == PLAYER && QUERY_FLAG(op, FLAG_WIZ)) {
00152 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00153 "You can't steal from the dungeon master!", NULL);
00154 return 0;
00155 }
00156 if (op->type == PLAYER && who->type == PLAYER && settings.no_player_stealing) {
00157 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00158 "You can't steal from other players!", NULL);
00159 return 0;
00160 }
00161
00162
00163
00164 for (tmp = op->inv; tmp != NULL; tmp = next) {
00165 next = tmp->below;
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177 if (QUERY_FLAG(tmp, FLAG_WAS_WIZ)
00178 || QUERY_FLAG(tmp, FLAG_APPLIED)
00179 || !(tmp->type)
00180 || tmp->type == EXPERIENCE
00181 || tmp->type == SPELL
00182 || QUERY_FLAG(tmp, FLAG_STARTEQUIP)
00183 || QUERY_FLAG(tmp, FLAG_NO_STEAL)
00184 || tmp->invisible)
00185 continue;
00186
00187
00188
00189
00190
00191 roll = die_roll(2, 100, who, PREFER_LOW)/2;
00192
00193 if ((chance = adj_stealchance(who, op, (stats_value+skill->level*10-op->level*3))) == -1)
00194 return 0;
00195 else if (roll < chance) {
00196 tag_t tmp_count = tmp->count;
00197
00198 pick_up(who, tmp);
00199
00200
00201
00202
00203
00204 if (was_destroyed(tmp, tmp_count) || tmp->env != op) {
00205
00206 success = tmp;
00207 CLEAR_FLAG(tmp, FLAG_INV_LOCKED);
00208 }
00209 break;
00210 }
00211 }
00212
00213 if (!tmp) {
00214 query_name(op, name, MAX_BUF);
00215 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00216 "%s%s has nothing you can steal!",
00217 "%s%s has nothing you can steal!",
00218 op->type == PLAYER ? "" : "The ", name);
00219 return 0;
00220 }
00221
00222
00223
00224
00225
00226
00227 if ((roll >= skill->level)
00228 || !chance
00229 || (tmp && tmp->weight > (250*(random_roll(0, stats_value+skill->level*10-1, who, PREFER_LOW))))) {
00230
00231 if (who->hide)
00232 make_visible(who);
00233
00234 if (op->type != PLAYER) {
00235
00236 if (who->type == PLAYER) {
00237 npc_call_help(op);
00238 query_name(op, name, MAX_BUF);
00239 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00240 "%s notices your attempted pilfering!",
00241 "%s notices your attempted pilfering!",
00242 name);
00243 }
00244 CLEAR_FLAG(op, FLAG_UNAGGRESSIVE);
00245
00246
00247
00248 SET_FLAG(op, FLAG_NO_STEAL);
00249 } else {
00250 char buf[MAX_BUF];
00251
00252
00253 if (success && who->stats.Int > random_roll(0, 19, op, PREFER_LOW)) {
00254 query_name(success, name, MAX_BUF);
00255 snprintf(buf, sizeof(buf), "Your %s is missing!", name);
00256 } else {
00257 snprintf(buf, sizeof(buf), "Your pack feels strangely lighter.");
00258 }
00259 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
00260 buf, buf);
00261 if (!success) {
00262 if (who->invisible) {
00263 snprintf(buf, sizeof(buf), "you feel itchy fingers getting at your pack.");
00264 } else {
00265 query_name(who, name, MAX_BUF);
00266 snprintf(buf, sizeof(buf), "%s looks very shifty.", name);
00267 }
00268 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_STEAL,
00269 buf, buf);
00270 }
00271 }
00272
00273 }
00274 return success ? 1 : 0;
00275 }
00276
00277
00290 int steal(object *op, int dir, object *skill) {
00291 object *tmp, *next;
00292 sint16 x, y;
00293 mapstruct *m;
00294 int mflags;
00295
00296 x = op->x+freearr_x[dir];
00297 y = op->y+freearr_y[dir];
00298
00299 if (dir == 0) {
00300
00301 return 0;
00302 }
00303
00304 m = op->map;
00305 mflags = get_map_flags(m, &m, x, y, &x, &y);
00306
00307
00308
00309 if ((mflags&P_OUT_OF_MAP) || !(mflags&P_IS_ALIVE))
00310 return 0;
00311
00312
00313 if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y)))
00314 return 0;
00315
00316
00317 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above)
00318 ;
00319
00320
00321 for (; tmp != NULL; tmp = next) {
00322 next = tmp->below;
00323
00324
00325
00326 if (tmp->head)
00327 tmp = tmp->head;
00328
00329 if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER))
00330 continue;
00331
00332
00333 if (tmp->type == PLAYER && QUERY_FLAG(tmp, FLAG_WIZ) && tmp->contr->hidden)
00334 continue;
00335 if (attempt_steal(tmp, op, skill)) {
00336 if (tmp->type == PLAYER)
00337 return 0;
00338
00339
00340 if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->attack_movement == PETMOVE) {
00341 object *owner = get_owner(tmp);
00342 if (owner != NULL && owner->type == PLAYER)
00343 return 0;
00344 }
00345
00346 return (calc_skill_exp(op, tmp, skill));
00347 }
00348 }
00349 return 0;
00350 }
00351
00366 static int attempt_pick_lock(object *door, object *pl, object *skill) {
00367 int difficulty = pl->map->difficulty ? pl->map->difficulty : 0;
00368 int success = 0, number;
00369
00370
00371
00372
00373
00374 number = (die_roll(2, 40, pl, PREFER_LOW)-2)/2;
00375 if (number < (pl->stats.Dex+skill->level-difficulty)) {
00376 remove_door(door);
00377 success = 1;
00378 } else if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP)) {
00379 spring_trap(door->inv, pl);
00380 }
00381 return success;
00382 }
00383
00384
00400 int pick_lock(object *pl, int dir, object *skill) {
00401 object *tmp;
00402 int x = pl->x+freearr_x[dir];
00403 int y = pl->y+freearr_y[dir];
00404
00405 if (!dir)
00406 dir = pl->facing;
00407
00408
00409 if (out_of_map(pl->map, x, y)) {
00410 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00411 "There is no lock there.", NULL);
00412 return 0;
00413 }
00414
00415 for (tmp = GET_MAP_OB(pl->map, x, y); tmp; tmp = tmp->above)
00416 if (tmp->type == DOOR || tmp->type == LOCKED_DOOR)
00417 break;
00418
00419 if (!tmp) {
00420 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00421 "There is no lock there.", NULL);
00422 return 0;
00423 }
00424 if (tmp->type == LOCKED_DOOR) {
00425 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00426 "You can't pick that lock!", NULL);
00427 return 0;
00428 }
00429
00430 if (!tmp->move_block) {
00431 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00432 "The door has no lock!", NULL);
00433 return 0;
00434 }
00435
00436 if (attempt_pick_lock(tmp, pl, skill)) {
00437 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00438 "You pick the lock.", NULL);
00439 return calc_skill_exp(pl, NULL, skill);
00440 } else {
00441 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00442 "You fail to pick the lock.", NULL);
00443 return 0;
00444 }
00445 }
00446
00447
00467 static int attempt_hide(object *op, object *skill) {
00468 int number, difficulty = op->map->difficulty;
00469 int terrain = hideability(op);
00470
00471 if (terrain < -10)
00472 return 0;
00473
00474
00475
00476
00477
00478 number = (die_roll(2, 25, op, PREFER_LOW)-2)/2;
00479 if (!stand_near_hostile(op) && (number < (op->stats.Dex+skill->level+terrain-difficulty))) {
00480 op->invisible += 100;
00481 if (op->type == PLAYER)
00482 op->contr->tmp_invis = 1;
00483 op->hide = 1;
00484 return 1;
00485 }
00486 return 0;
00487 }
00488
00498 int hide(object *op, object *skill) {
00499
00500
00501
00502 if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
00503 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00504 "You don't need to hide while invisible!", NULL);
00505 return 0;
00506 } else if (!op->hide && op->invisible > 0 && op->type == PLAYER) {
00507 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOOD_EFFECT_END,
00508 "Your attempt to hide breaks the invisibility spell!", NULL);
00509 make_visible(op);
00510 }
00511
00512 if (op->invisible > (50*skill->level)) {
00513 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00514 "You are as hidden as you can get.", NULL);
00515 return 0;
00516 }
00517
00518 if (attempt_hide(op, skill)) {
00519 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00520 "You hide in the shadows.", NULL);
00521 update_object(op, UP_OBJ_FACE);
00522 return calc_skill_exp(op, NULL, skill);
00523 }
00524 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00525 "You fail to conceal yourself.", NULL);
00526 return 0;
00527 }
00528
00529
00536 static void stop_jump(object *pl) {
00537 fix_object(pl);
00538 insert_ob_in_map(pl, pl->map, pl, 0);
00539 }
00540
00554 static int attempt_jump(object *pl, int dir, int spaces, object *skill) {
00555 object *tmp;
00556 int i, exp = 0, dx = freearr_x[dir], dy = freearr_y[dir], mflags;
00557 sint16 x, y;
00558 mapstruct *m;
00559
00560
00561
00562
00563
00564
00565
00566
00567 remove_ob(pl);
00568
00569
00570
00571
00572
00573
00574 pl->move_type |= MOVE_FLY_LOW;
00575
00576 for (i = 0; i <= spaces; i++) {
00577 x = pl->x+dx;
00578 y = pl->y+dy;
00579 m = pl->map;
00580
00581 mflags = get_map_flags(m, &m, x, y, &x, &y);
00582
00583 if (mflags&P_OUT_OF_MAP) {
00584 (void)stop_jump(pl);
00585 return 0;
00586 }
00587 if (OB_TYPE_MOVE_BLOCK(pl, GET_MAP_MOVE_BLOCK(m, x, y))) {
00588 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00589 "Your jump is blocked.", NULL);
00590 stop_jump(pl);
00591 return 0;
00592 }
00593
00594 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
00595
00596 if (QUERY_FLAG(tmp, FLAG_MONSTER)
00597 || (tmp->type == PLAYER && (!QUERY_FLAG(tmp, FLAG_WIZ) || !tmp->contr->hidden))) {
00598 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00599 "You jump into %s%s.",
00600 "You jump into %s%s.",
00601 tmp->type == PLAYER ? "" : "the ", tmp->name);
00602
00603 if (tmp->type != PLAYER
00604 || (pl->type == PLAYER && pl->contr->party == NULL)
00605 || (pl->type == PLAYER && tmp->type == PLAYER && pl->contr->party != tmp->contr->party))
00606 exp = skill_attack(tmp, pl, pl->facing, "kicked", skill);
00607
00608 stop_jump(pl);
00609 return exp;
00610 }
00611
00612
00613
00614
00615 if (tmp->move_on&MOVE_FLY_LOW) {
00616 pl->x = x;
00617 pl->y = y;
00618 pl->map = m;
00619 if (pl->contr)
00620 esrv_map_scroll(&pl->contr->socket, dx, dy);
00621 stop_jump(pl);
00622 return calc_skill_exp(pl, NULL, skill);
00623 }
00624 }
00625 pl->x = x;
00626 pl->y = y;
00627 pl->map = m;
00628 if (pl->contr)
00629 esrv_map_scroll(&pl->contr->socket, dx, dy);
00630 }
00631 stop_jump(pl);
00632 return calc_skill_exp(pl, NULL, skill);
00633 }
00634
00651 int jump(object *pl, int dir, object *skill) {
00652 int spaces = 0, stats;
00653 int str = pl->stats.Str;
00654 int dex = pl->stats.Dex;
00655
00656 dex = dex ? dex : 15;
00657 str = str ? str : 10;
00658
00659 stats = str*str*str*dex*skill->level;
00660
00661 if (pl->carrying != 0)
00662 spaces = (int)(stats/pl->carrying);
00663 else
00664 spaces = 2;
00665
00666 if (spaces > 2)
00667 spaces = 2;
00668 else if (spaces == 0) {
00669 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00670 "You are carrying too much weight to jump.", NULL);
00671 return 0;
00672 }
00673 return attempt_jump(pl, dir, spaces, skill);
00674 }
00675
00686 static int do_skill_detect_curse(object *pl, object *skill) {
00687 object *tmp;
00688 int success = 0;
00689
00690 for (tmp = pl->inv; tmp; tmp = tmp->below)
00691 if (!tmp->invisible
00692 && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00693 && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)
00694 && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
00695 && tmp->item_power < skill->level) {
00696 SET_FLAG(tmp, FLAG_KNOWN_CURSED);
00697 esrv_update_item(UPD_FLAGS, pl, tmp);
00698 success += calc_skill_exp(pl, tmp, skill);
00699 }
00700
00701
00702
00703 for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
00704 if ((can_pick(pl, tmp) || QUERY_FLAG(tmp, FLAG_IS_CAULDRON))
00705 && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00706 && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)
00707 && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
00708 && tmp->item_power < skill->level) {
00709 SET_FLAG(tmp, FLAG_KNOWN_CURSED);
00710 esrv_update_item(UPD_FLAGS, pl, tmp);
00711 success += calc_skill_exp(pl, tmp, skill);
00712 }
00713
00714 return success;
00715 }
00716
00727 static int do_skill_detect_magic(object *pl, object *skill) {
00728 object *tmp;
00729 int success = 0;
00730
00731 for (tmp = pl->inv; tmp; tmp = tmp->below)
00732 if (!tmp->invisible
00733 && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00734 && !QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL)
00735 && (is_magical(tmp))
00736 && tmp->item_power < skill->level) {
00737 SET_FLAG(tmp, FLAG_KNOWN_MAGICAL);
00738 esrv_update_item(UPD_FLAGS, pl, tmp);
00739 success += calc_skill_exp(pl, tmp, skill);
00740 }
00741
00742
00743 for (tmp = GET_MAP_OB(pl->map, pl->x, pl->y); tmp; tmp = tmp->above)
00744 if (can_pick(pl, tmp)
00745 && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00746 && !QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL)
00747 && (is_magical(tmp)) && tmp->item_power < skill->level) {
00748 SET_FLAG(tmp, FLAG_KNOWN_MAGICAL);
00749 esrv_update_item(UPD_FLAGS, pl, tmp);
00750 success += calc_skill_exp(pl, tmp, skill);
00751 }
00752
00753 return success;
00754 }
00755
00771 static int do_skill_ident2(object *tmp, object *pl, int obj_class, object *skill) {
00772 int success = 0, chance, ip;
00773 int skill_value = skill->level*pl->stats.Int ? pl->stats.Int : 10;
00774
00775 if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)
00776 && !QUERY_FLAG(tmp, FLAG_NO_SKILL_IDENT)
00777 && need_identify(tmp)
00778 && !tmp->invisible
00779 && tmp->type == obj_class) {
00780 ip = tmp->magic;
00781 if (tmp->item_power > ip)
00782 ip = tmp->item_power;
00783
00784 chance = die_roll(3, 10, pl, PREFER_LOW)-3+rndm(0, (tmp->magic ? tmp->magic*5 : 1)-1);
00785 if (skill_value >= chance) {
00786 identify(tmp);
00787 if (pl->type == PLAYER) {
00788 char desc[MAX_BUF];
00789
00790 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00791 "You identify %s.",
00792 "You identify %s.",
00793 ob_describe(tmp, pl, desc, sizeof(desc)));
00794 if (tmp->msg) {
00795 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO,
00796 "The item has a story:\n%s",
00797 "The item has a story:\n%s",
00798 tmp->msg);
00799
00800 }
00801 }
00802 success += calc_skill_exp(pl, tmp, skill);
00803 } else
00804 SET_FLAG(tmp, FLAG_NO_SKILL_IDENT);
00805 }
00806 return success;
00807 }
00808
00821 static int do_skill_ident(object *pl, int obj_class, object *skill) {
00822 object *tmp;
00823 int success = 0, area, i;
00824
00825
00826 for (tmp = pl->inv; tmp; tmp = tmp->below)
00827 success += do_skill_ident2(tmp, pl, obj_class, skill);
00828
00829
00830
00831
00832
00833
00834 if (skill->level > 64) {
00835 area = 49;
00836 } else if (skill->level > 16) {
00837 area = 25;
00838 } else if (skill->level > 4) {
00839 area = 9;
00840 } else {
00841 area = 1;
00842 }
00843
00844 for (i = 0; i < area; i++) {
00845 sint16 x = pl->x+freearr_x[i];
00846 sint16 y = pl->y+freearr_y[i];
00847 mapstruct *m = pl->map;
00848 int mflags;
00849
00850 mflags = get_map_flags(m, &m, x, y, &x, &y);
00851 if (mflags&P_OUT_OF_MAP)
00852 continue;
00853
00854 if (can_see_monsterP(m, pl->x, pl->y, i)) {
00855 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
00856 success += do_skill_ident2(tmp, pl, obj_class, skill);
00857 }
00858 }
00859 }
00860 return success;
00861 }
00862
00872 int skill_ident(object *pl, object *skill) {
00873 int success = 0;
00874
00875 if (pl->type != PLAYER)
00876 return 0;
00877
00878 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00879 "You look at the objects nearby...", NULL);
00880
00881 switch (skill->subtype) {
00882 case SK_SMITHERY:
00883 success += do_skill_ident(pl, WEAPON, skill)
00884 +do_skill_ident(pl, ARMOUR, skill)
00885 +do_skill_ident(pl, BRACERS, skill)
00886 +do_skill_ident(pl, CLOAK, skill)
00887 +do_skill_ident(pl, BOOTS, skill)
00888 +do_skill_ident(pl, SHIELD, skill)
00889 +do_skill_ident(pl, GIRDLE, skill)
00890 +do_skill_ident(pl, HELMET, skill)
00891 +do_skill_ident(pl, GLOVES, skill);
00892 break;
00893
00894 case SK_BOWYER:
00895 success += do_skill_ident(pl, BOW, skill)
00896 +do_skill_ident(pl, ARROW, skill);
00897 break;
00898
00899 case SK_ALCHEMY:
00900 success += do_skill_ident(pl, POTION, skill)
00901 +do_skill_ident(pl, POISON, skill)
00902 +do_skill_ident(pl, CONTAINER, skill)
00903 +do_skill_ident(pl, DRINK, skill)
00904 +do_skill_ident(pl, INORGANIC, skill);
00905 break;
00906
00907 case SK_WOODSMAN:
00908 success += do_skill_ident(pl, FOOD, skill)
00909 +do_skill_ident(pl, DRINK, skill)
00910 +do_skill_ident(pl, FLESH, skill);
00911 break;
00912
00913 case SK_JEWELER:
00914 success += do_skill_ident(pl, GEM, skill)
00915 +do_skill_ident(pl, RING, skill)
00916 +do_skill_ident(pl, AMULET, skill);
00917 break;
00918
00919 case SK_LITERACY:
00920 success += do_skill_ident(pl, SPELLBOOK, skill)
00921 +do_skill_ident(pl, SCROLL, skill)
00922 +do_skill_ident(pl, BOOK, skill);
00923 break;
00924
00925 case SK_THAUMATURGY:
00926 success += do_skill_ident(pl, WAND, skill)
00927 +do_skill_ident(pl, ROD, skill)
00928 +do_skill_ident(pl, HORN, skill);
00929 break;
00930
00931 case SK_DET_CURSE:
00932 success = do_skill_detect_curse(pl, skill);
00933 if (success)
00934 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00935 "...and discover cursed items!", NULL);
00936 break;
00937
00938 case SK_DET_MAGIC:
00939 success = do_skill_detect_magic(pl, skill);
00940 if (success)
00941 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00942 "...and discover items imbued with mystic forces!", NULL);
00943 break;
00944
00945 default:
00946 LOG(llevError, "Error: bad call to skill_ident()\n");
00947 return 0;
00948 break;
00949 }
00950 if (!success) {
00951 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00952 "...and learn nothing more.", NULL);
00953 }
00954 return success;
00955 }
00956
00957
00976 int use_oratory(object *pl, int dir, object *skill) {
00977 sint16 x = pl->x+freearr_x[dir], y = pl->y+freearr_y[dir];
00978 int mflags, chance;
00979 object *tmp;
00980 mapstruct *m;
00981 char name[MAX_BUF];
00982
00983 if (pl->type != PLAYER)
00984 return 0;
00985 m = pl->map;
00986 mflags = get_map_flags(m, &m, x, y, &x, &y);
00987 if (mflags&P_OUT_OF_MAP)
00988 return 0;
00989
00990
00991
00992 if (!(mflags&P_IS_ALIVE)) {
00993 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00994 "There is nothing to orate to.", NULL);
00995 return 0;
00996 }
00997
00998 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
00999
01000
01001
01002
01003
01004
01005 if (tmp->type == PLAYER)
01006 return 0;
01007 if (tmp->more || tmp->head)
01008 return 0;
01009 if (tmp->msg)
01010 return 0;
01011
01012 if (QUERY_FLAG(tmp, FLAG_MONSTER))
01013 break;
01014 }
01015
01016 if (!tmp) {
01017 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01018 "There is nothing to orate to.", NULL);
01019 return 0;
01020 }
01021
01022 query_name(tmp, name, MAX_BUF);
01023 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01024 "You orate to the %s.",
01025 "You orate to the %s.",
01026 name);
01027
01028
01029
01030
01031 if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
01032 query_name(tmp, name, MAX_BUF);
01033 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01034 "Too bad the %s isn't listening!",
01035 "Too bad the %s isn't listening!",
01036 name);
01037 return 0;
01038 }
01039
01040
01041 if (QUERY_FLAG(tmp, FLAG_FRIENDLY) && (tmp->attack_movement == PETMOVE)) {
01042 if (get_owner(tmp) == pl) {
01043 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01044 "Your follower loves your speech.", NULL);
01045 return 0;
01046 } else if (skill->level > tmp->level) {
01047
01048
01049
01050 set_owner(tmp, pl);
01051 query_name(tmp, name, MAX_BUF);
01052 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01053 "You convince the %s to follow you instead!",
01054 "You convince the %s to follow you instead!",
01055 name);
01056
01057
01058
01059
01060 return 0;
01061 } else {
01062
01063 return 0;
01064 }
01065 }
01066
01067 chance = skill->level*2+(pl->stats.Cha-2*tmp->stats.Int)/2;
01068
01069
01070 if (chance > 0 && tmp->level < (random_roll(0, chance-1, pl, PREFER_HIGH)-1)) {
01071 query_name(tmp, name, MAX_BUF);
01072 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01073 "You convince the %s to become your follower.",
01074 "You convince the %s to become your follower.",
01075 name);
01076
01077 set_owner(tmp, pl);
01078 tmp->stats.exp = 0;
01079 add_friendly_object(tmp);
01080 SET_FLAG(tmp, FLAG_FRIENDLY);
01081 tmp->attack_movement = PETMOVE;
01082 return calc_skill_exp(pl, tmp, skill);
01083 }
01084
01085 else if ((skill->level+((pl->stats.Cha-10)/2)) < random_roll(1, 2*tmp->level, pl, PREFER_LOW)) {
01086 query_name(tmp, name, MAX_BUF);
01087 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01088 "Your speech angers the %s!",
01089 "Your speech angers the %s!",
01090 name);
01091
01092 if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
01093 CLEAR_FLAG(tmp, FLAG_FRIENDLY);
01094 remove_friendly_object(tmp);
01095 tmp->attack_movement = 0;
01096 }
01097 CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE);
01098 }
01099 return 0;
01100 }
01101
01122 int singing(object *pl, int dir, object *skill) {
01123 int i, exp = 0, chance, mflags;
01124 object *tmp;
01125 mapstruct *m;
01126 sint16 x, y;
01127 char name[MAX_BUF];
01128
01129 if (pl->type != PLAYER)
01130 return 0;
01131
01132 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01133 "You sing", NULL);
01134 for (i = dir; i < (dir+MIN(skill->level, SIZEOFFREE)); i++) {
01135 x = pl->x+freearr_x[i];
01136 y = pl->y+freearr_y[i];
01137 m = pl->map;
01138
01139 mflags = get_map_flags(m, &m, x, y, &x, &y);
01140 if (mflags&P_OUT_OF_MAP)
01141 continue;
01142 if (!(mflags&P_IS_ALIVE))
01143 continue;
01144
01145 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
01146 if (QUERY_FLAG(tmp, FLAG_MONSTER))
01147 break;
01148
01149 if (tmp->type == PLAYER)
01150 break;
01151 }
01152
01153
01154
01155
01156 if (tmp
01157 && QUERY_FLAG(tmp, FLAG_MONSTER)
01158 && !QUERY_FLAG(tmp, FLAG_NO_STEAL)
01159 && !QUERY_FLAG(tmp, FLAG_SPLITTING)
01160 && !QUERY_FLAG(tmp, FLAG_HITBACK)
01161 && (tmp->level <= skill->level)
01162 && (!tmp->head)
01163 && !QUERY_FLAG(tmp, FLAG_UNDEAD)
01164 && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)
01165 && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
01166
01167
01168
01169
01170 chance = skill->level*2+(pl->stats.Cha-5-tmp->stats.Int)/2;
01171 if (chance && tmp->level*2 < random_roll(0, chance-1, pl, PREFER_HIGH)) {
01172 SET_FLAG(tmp, FLAG_UNAGGRESSIVE);
01173 query_name(tmp, name, MAX_BUF);
01174 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01175 "You calm down the %s",
01176 "You calm down the %s",
01177 name);
01178
01179
01180 if (!QUERY_FLAG(tmp, FLAG_NO_STEAL))
01181 exp += calc_skill_exp(pl, tmp, skill);
01182 SET_FLAG(tmp, FLAG_NO_STEAL);
01183 } else {
01184 query_name(tmp, name, MAX_BUF);
01185 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01186 "Too bad the %s isn't listening!",
01187 "Too bad the %s isn't listening!",
01188 name);
01189 SET_FLAG(tmp, FLAG_NO_STEAL);
01190 }
01191 }
01192 }
01193 return exp;
01194 }
01195
01206 int find_traps(object *pl, object *skill) {
01207 object *tmp, *tmp2;
01208 int i, expsum = 0, mflags;
01209 sint16 x, y;
01210 mapstruct *m;
01211
01212
01213
01214
01215
01216 for (i = 0; i < 9; i++) {
01217 x = pl->x+freearr_x[i];
01218 y = pl->y+freearr_y[i];
01219 m = pl->map;
01220
01221 mflags = get_map_flags(m, &m, x, y, &x, &y);
01222 if (mflags&P_OUT_OF_MAP)
01223 continue;
01224
01225
01226 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
01227
01228
01229
01230
01231
01232 if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
01233 for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
01234 if (tmp2->type == RUNE || tmp2->type == TRAP)
01235 if (trap_see(pl, tmp2)) {
01236 trap_show(tmp2, tmp);
01237 if (tmp2->stats.Cha > 1) {
01238 if (!tmp2->owner || tmp2->owner->type != PLAYER)
01239 expsum += calc_skill_exp(pl, tmp2, skill);
01240
01241 tmp2->stats.Cha = 1;
01242 }
01243 }
01244 }
01245 if ((tmp->type == RUNE || tmp->type == TRAP) && trap_see(pl, tmp)) {
01246 trap_show(tmp, tmp);
01247 if (tmp->stats.Cha > 1) {
01248 if (!tmp->owner || tmp->owner->type != PLAYER)
01249 expsum += calc_skill_exp(pl, tmp, skill);
01250 tmp->stats.Cha = 1;
01251 }
01252 }
01253 }
01254 }
01255 draw_ext_info(NDI_BLACK, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01256 "You search the area.", NULL);
01257 return expsum;
01258 }
01259
01271 int remove_trap(object *op, object *skill) {
01272 object *tmp, *tmp2;
01273 int i, success = 0, mflags;
01274 mapstruct *m;
01275 sint16 x, y;
01276
01277 for (i = 0; i < 9; i++) {
01278 x = op->x+freearr_x[i];
01279 y = op->y+freearr_y[i];
01280 m = op->map;
01281
01282 mflags = get_map_flags(m, &m, x, y, &x, &y);
01283 if (mflags&P_OUT_OF_MAP)
01284 continue;
01285
01286
01287 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
01288
01289
01290
01291
01292
01293 if (tmp->type != PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER)) {
01294 for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below)
01295 if ((tmp2->type == RUNE || tmp2->type == TRAP) && tmp2->stats.Cha <= 1) {
01296 trap_show(tmp2, tmp);
01297 if (trap_disarm(op, tmp2, 1, skill) && (!tmp2->owner || tmp2->owner->type != PLAYER)) {
01298 tmp->stats.exp = tmp->stats.Cha*tmp->level;
01299 success += calc_skill_exp(op, tmp2, skill);
01300 } else {
01301
01302 return success;
01303 }
01304 }
01305 }
01306 if ((tmp->type == RUNE || tmp->type == TRAP) && tmp->stats.Cha <= 1) {
01307 trap_show(tmp, tmp);
01308 if (trap_disarm(op, tmp, 1, skill) && (!tmp->owner || tmp->owner->type != PLAYER)) {
01309 tmp->stats.exp = tmp->stats.Cha*tmp->level;
01310 success += calc_skill_exp(op, tmp, skill);
01311 } else {
01312
01313 return success;
01314 }
01315 }
01316 }
01317 }
01318 return success;
01319 }
01320
01321
01340 int pray(object *pl, object *skill) {
01341 char buf[MAX_BUF];
01342 object *tmp;
01343
01344 if (pl->type != PLAYER)
01345 return 0;
01346
01347 snprintf(buf, sizeof(buf), "You pray.");
01348
01349
01350
01351
01352
01353
01354 for (tmp = pl->below; tmp != NULL; tmp = tmp->below) {
01355
01356 if (tmp && tmp->type == HOLY_ALTAR && tmp->other_arch) {
01357 snprintf(buf, sizeof(buf), "You pray over the %s.", tmp->name);
01358 pray_at_altar(pl, tmp, skill);
01359 break;
01360 }
01361 }
01362
01363 draw_ext_info(NDI_BLACK, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01364 buf, buf);
01365
01366 if (pl->stats.grace < pl->stats.maxgrace) {
01367 pl->stats.grace++;
01368 pl->last_grace = -1;
01369 }
01370 return 0;
01371 }
01372
01389 void meditate(object *pl, object *skill) {
01390 object *tmp;
01391
01392 if (pl->type != PLAYER)
01393 return;
01394
01395
01396 if (QUERY_FLAG(pl, FLAG_READY_WEAPON) && (skill->level < 6)) {
01397 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01398 "You can't concentrate while wielding a weapon!", NULL);
01399 return;
01400 } else {
01401 for (tmp = pl->inv; tmp; tmp = tmp->below)
01402 if (((tmp->type == ARMOUR && skill->level < 12)
01403 || (tmp->type == HELMET && skill->level < 10)
01404 || (tmp->type == SHIELD && skill->level < 6)
01405 || (tmp->type == BOOTS && skill->level < 4)
01406 || (tmp->type == GLOVES && skill->level < 2))
01407 && QUERY_FLAG(tmp, FLAG_APPLIED)) {
01408 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01409 "You can't concentrate while wearing so much armour!", NULL);
01410 return;
01411 }
01412 }
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422 draw_ext_info(NDI_BLACK, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01423 "You meditate.", NULL);
01424
01425 if (pl->stats.sp < pl->stats.maxsp) {
01426 pl->stats.sp++;
01427 pl->last_sp = -1;
01428 } else if (pl->stats.hp < pl->stats.maxhp) {
01429 pl->stats.hp++;
01430 pl->last_heal = -1;
01431 }
01432 }
01433
01450 static int write_note(object *pl, object *item, const char *msg, object *skill) {
01451 char buf[BOOK_BUF];
01452 object *newBook = NULL;
01453
01454
01455 if (!item || item->type != BOOK)
01456 return 0;
01457
01458 if (!msg) {
01459 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01460 "No message to write!\nUsage: use_skill %s <message>",
01461 "No message to write!\nUsage: use_skill %s <message>",
01462 skill->skill);
01463 return 0;
01464 }
01465 if (strcasestr_local(msg, "endmsg")) {
01466 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01467 "Trying to cheat now are we?", NULL);
01468 return 0;
01469 }
01470
01471
01472 if (execute_event(item, EVENT_TRIGGER, pl, NULL, msg, SCRIPT_FIX_ALL) != 0)
01473 return strlen(msg);
01474
01475 buf[0] = 0;
01476 if (!book_overflow(item->msg, msg, BOOK_BUF)) {
01477 if (item->msg)
01478 snprintf(buf, sizeof(buf), "%s%s\n", item->msg, msg);
01479 else
01480 snprintf(buf, sizeof(buf), "%s\n", msg);
01481
01482 if (item->nrof > 1) {
01483 newBook = get_object();
01484 copy_object(item, newBook);
01485 decrease_ob(item);
01486 newBook->nrof = 1;
01487 if (newBook->msg)
01488 free_string(newBook->msg);
01489 newBook->msg = add_string(buf);
01490 newBook = insert_ob_in_ob(newBook, pl);
01491 } else {
01492 if (item->msg)
01493 free_string(item->msg);
01494 item->msg = add_string(buf);
01495
01496
01497
01498
01499 }
01500 query_short_name(item, buf, BOOK_BUF);
01501 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01502 "You write in the %s.",
01503 "You write in the %s.",
01504 buf);
01505 return strlen(msg);
01506 } else {
01507 query_short_name(item, buf, BOOK_BUF);
01508 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01509 "Your message won't fit in the %s!",
01510 "Your message won't fit in the %s!",
01511 buf);
01512 }
01513 return 0;
01514 }
01515
01531 static int write_scroll(object *pl, object *scroll, object *skill) {
01532 int success = 0, confused = 0, grace_cost = 0;
01533 object *newscroll, *chosen_spell, *tmp;
01534
01535
01536 if (scroll->type != SCROLL) {
01537 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01538 "A spell can only be inscribed into a scroll!", NULL);
01539 return 0;
01540 }
01541
01542
01543 chosen_spell = pl->contr->ranges[range_magic];
01544 if (!chosen_spell) {
01545 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01546 "You need a spell readied in order to inscribe!", NULL);
01547 return 0;
01548 }
01549
01550 grace_cost = SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
01551 if (grace_cost > 0 && grace_cost > pl->stats.grace) {
01552 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01553 "You don't have enough grace to write a scroll of %s.",
01554 "You don't have enough grace to write a scroll of %s.",
01555 chosen_spell->name);
01556 return 0;
01557 }
01558 if (SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA) > pl->stats.sp) {
01559 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01560 "You don't have enough mana to write a scroll of %s.",
01561 "You don't have enough mana to write a scroll of %s.",
01562 chosen_spell->name);
01563 return 0;
01564 }
01565
01566
01567
01568 if (chosen_spell->path_attuned&pl->path_denied && settings.allow_denied_spells_writing == 0) {
01569 char name[MAX_BUF];
01570
01571 query_name(chosen_spell, name, MAX_BUF);
01572 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01573 "The simple idea of writing a scroll of %s makes you sick !",
01574 "The simple idea of writing a scroll of %s makes you sick !",
01575 name);
01576 return 0;
01577 }
01578
01579
01580
01581
01582
01583 if ((scroll->stats.sp || scroll->inv)
01584 && random_roll(0, scroll->level*2, pl, PREFER_LOW) > skill->level) {
01585 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01586 "Oops! You accidently read it while trying to write on it.", NULL);
01587 manual_apply(pl, scroll, 0);
01588 return 0;
01589 }
01590
01591 if (execute_event(scroll, EVENT_TRIGGER, pl, chosen_spell, NULL, 0) != 0)
01592 return 0;
01593
01594
01595 if (QUERY_FLAG(pl, FLAG_CONFUSED))
01596 confused = 1;
01597
01598
01599 pl->stats.grace -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_GRACE);
01600 pl->stats.sp -= SP_level_spellpoint_cost(pl, chosen_spell, SPELL_MANA);
01601
01602 if (random_roll(0, chosen_spell->level*4-1, pl, PREFER_LOW) < skill->level) {
01603 if (scroll->nrof > 1) {
01604 newscroll = get_object();
01605 copy_object(scroll, newscroll);
01606 decrease_ob(scroll);
01607 newscroll->nrof = 1;
01608 } else {
01609 newscroll = scroll;
01610 }
01611
01612 if (!confused) {
01613 newscroll->level = MAX(skill->level, chosen_spell->level);
01614 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
01615 "You succeed in writing a new scroll.", NULL);
01616 } else {
01617 chosen_spell = find_random_spell_in_ob(pl, NULL);
01618 if (!chosen_spell)
01619 return 0;
01620
01621 newscroll->level = MAX(skill->level, chosen_spell->level);
01622 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01623 "In your confused state, you write down some odd spell.", NULL);
01624 }
01625
01626 if (newscroll->inv) {
01627 object *ninv;
01628
01629 ninv = newscroll->inv;
01630 remove_ob(ninv);
01631 free_object(ninv);
01632 }
01633 tmp = get_object();
01634 copy_object(chosen_spell, tmp);
01635 insert_ob_in_ob(tmp, newscroll);
01636
01637
01638 newscroll->path_attuned = tmp->path_repelled;
01639
01640
01641
01642
01643 newscroll->value = newscroll->arch->clone.value*newscroll->inv->value*(newscroll->level+50)/(newscroll->inv->level+50);
01644 newscroll->stats.exp = newscroll->value/5;
01645
01646
01647 if (newscroll == scroll) {
01648
01649 remove_ob(newscroll);
01650 }
01651 newscroll = insert_ob_in_ob(newscroll, pl);
01652 success = calc_skill_exp(pl, newscroll, skill);
01653 if (!confused)
01654 success *= 2;
01655 success = success*skill->level;
01656 return success;
01657
01658 } else {
01659
01660 if (chosen_spell->level > skill->level || confused) {
01661 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01662 "Ouch! Your attempt to write a new scroll strains your mind!", NULL);
01663 if (random_roll(0, 1, pl, PREFER_LOW) == 1)
01664 drain_specific_stat(pl, 4);
01665 else {
01666 confuse_living(pl, pl, 99);
01667 return (-30*chosen_spell->level);
01668 }
01669 } else if (random_roll(0, pl->stats.Int-1, pl, PREFER_HIGH) < 15) {
01670 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01671 "Your attempt to write a new scroll rattles your mind!", NULL);
01672 confuse_living(pl, pl, 99);
01673 } else
01674 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01675 "You fail to write a new scroll.", NULL);
01676 }
01677 return 0;
01678 }
01679
01694 int write_on_item(object *pl, const char *params, object *skill) {
01695 object *item;
01696 const char *string = params;
01697 int msgtype;
01698 archetype *skat;
01699
01700 if (pl->type != PLAYER)
01701 return 0;
01702
01703 if (!params) {
01704 params = "";
01705 string = params;
01706 }
01707 skat = get_archetype_by_type_subtype(SKILL, SK_LITERACY);
01708
01709
01710 if (!find_skill_by_name(pl, skat->clone.skill)) {
01711 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
01712 "You must learn to read before you can write!", NULL);
01713 return 0;
01714 }
01715
01716 if (QUERY_FLAG(pl, FLAG_BLIND) && !QUERY_FLAG(pl, FLAG_WIZ)) {
01717 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01718 "You are unable to write while blind.", NULL);
01719 return 0;
01720 }
01721
01722
01723
01724
01725
01726 msgtype = (string[0] != '\0') ? BOOK : SCROLL;
01727
01728
01729 if (!(item = find_marked_object(pl))) {
01730 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01731 "You don't have any marked item to write on.", NULL);
01732 return 0;
01733 }
01734
01735 if (QUERY_FLAG(item, FLAG_UNPAID)) {
01736 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01737 "You had better pay for that before you write on it.", NULL);
01738 return 0;
01739 }
01740 if (msgtype != item->type) {
01741 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01742 "You have no %s to write %s",
01743 "You have no %s to write %s",
01744 msgtype == BOOK ? "book" : "scroll",
01745 msgtype == BOOK ? "on" : "your spell down");
01746 return 0;
01747 }
01748
01749 if (msgtype == SCROLL) {
01750 return write_scroll(pl, item, skill);
01751 } else if (msgtype == BOOK) {
01752 return write_note(pl, item, string, skill);
01753 }
01754 return 0;
01755 }
01756
01757
01758
01776 static object *find_throw_ob(object *op, const char *request) {
01777 object *tmp;
01778 char name[MAX_BUF];
01779
01780 if (!op) {
01781 LOG(llevError, "find_throw_ob(): confused! have a NULL thrower!\n");
01782 return (object *)NULL;
01783 }
01784
01785
01786 tmp = find_marked_object(op);
01787 if (tmp != NULL) {
01788
01789 if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
01790 tmp = NULL;
01791 }
01792 }
01793
01794
01795 if (tmp == NULL) {
01796 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
01797
01798 if (tmp->invisible || QUERY_FLAG(tmp, FLAG_INV_LOCKED))
01799 continue;
01800 query_name(tmp, name, MAX_BUF);
01801 if (!request
01802 || !strcmp(name, request)
01803 || !strcmp(tmp->name, request))
01804 break;
01805 }
01806 }
01807
01808
01809
01810
01811
01812 if (!tmp)
01813 return NULL;
01814
01815 if (QUERY_FLAG(tmp, FLAG_APPLIED)) {
01816 if (tmp->type != WEAPON) {
01817 query_name(tmp, name, MAX_BUF);
01818 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01819 "You can't throw %s.",
01820 "You can't throw %s.",
01821 name);
01822 tmp = NULL;
01823 } else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
01824 query_name(tmp, name, MAX_BUF);
01825 draw_ext_info_format(NDI_UNIQUE, 0, op,
01826 MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01827 "The %s sticks to your hand!",
01828 "The %s sticks to your hand!",
01829 name);
01830 tmp = NULL;
01831 } else {
01832 if (apply_special(op, tmp, AP_UNAPPLY|AP_NO_MERGE)) {
01833 LOG(llevError, "BUG: find_throw_ob(): couldn't unapply\n");
01834 tmp = NULL;
01835 }
01836 }
01837 } else if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
01838 query_name(tmp, name, MAX_BUF);
01839 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01840 "You should pay for the %s first.",
01841 "You should pay for the %s first.",
01842 name);
01843 tmp = NULL;
01844 }
01845
01846 if (tmp && QUERY_FLAG(tmp, FLAG_INV_LOCKED)) {
01847 LOG(llevError, "BUG: find_throw_ob(): object is locked\n");
01848 tmp = NULL;
01849 }
01850 return tmp;
01851 }
01852
01864 static object *make_throw_ob(object *orig) {
01865 object *toss_item;
01866
01867 if (!orig)
01868 return NULL;
01869
01870 toss_item = get_object();
01871 if (QUERY_FLAG(orig, FLAG_APPLIED)) {
01872 LOG(llevError, "BUG: make_throw_ob(): ob is applied\n");
01873
01874 CLEAR_FLAG(orig, FLAG_APPLIED);
01875 }
01876 copy_object(orig, toss_item);
01877 toss_item->type = THROWN_OBJ;
01878 CLEAR_FLAG(toss_item, FLAG_CHANGING);
01879 toss_item->stats.dam = 0;
01880 insert_ob_in_ob(orig, toss_item);
01881 return toss_item;
01882 }
01883
01884
01905 static int do_throw(object *op, object *part, object *toss_item, int dir, object *skill) {
01906 object *throw_ob = toss_item, *left = NULL;
01907 tag_t left_tag;
01908 int eff_str = 0, maxc, str = op->stats.Str, dam = 0;
01909 int pause_f, weight_f = 0, mflags;
01910 float str_factor = 1.0, load_factor = 1.0, item_factor = 1.0;
01911 mapstruct *m;
01912 sint16 sx, sy;
01913 tag_t tag;
01914 char name[MAX_BUF];
01915
01916 if (throw_ob == NULL) {
01917 if (op->type == PLAYER) {
01918 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01919 "You have nothing to throw.", NULL);
01920 }
01921 return 0;
01922 }
01923 if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP)) {
01924 if (op->type == PLAYER) {
01925 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01926 "The gods won't let you throw that.", NULL);
01927 }
01928 return 0;
01929 }
01930
01931
01932
01933
01934
01935
01936
01937 if (str > MAX_STAT) {
01938 str_factor = (float)str/(float)MAX_STAT;
01939 str = MAX_STAT;
01940 }
01941
01942
01943
01944 maxc=max_carry[str] * 1000;
01945 if (op->type == PLAYER && op->carrying > maxc)
01946 load_factor = (float)maxc/(float) op->carrying;
01947
01948
01949 if (throw_ob->weight > 0)
01950 item_factor = (float) maxc/(float) (3.0 * throw_ob->weight);
01951 else {
01952 query_name(throw_ob, name, MAX_BUF);
01953 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01954 "You can't throw %s.",
01955 "You can't throw %s.",
01956 name);
01957 return 0;
01958 }
01959
01960 eff_str = str*MIN(load_factor, 1.0);
01961 eff_str = (float)eff_str*item_factor*str_factor;
01962
01963
01964
01965 if (eff_str > MAX_STAT)
01966 eff_str = MAX_STAT;
01967
01968 #ifdef DEBUG_THROW
01969 LOG(llevDebug, "%s carries %d, eff_str=%d\n", op->name, op->carrying, eff_str);
01970 LOG(llevDebug, " max_c=%d, item_f=%f, load_f=%f, str=%d\n", (weight_limit[op->stats.Str]*FREE_PLAYER_LOAD_PERCENT), item_factor, load_factor, op->stats.Str);
01971 LOG(llevDebug, " str_factor=%f\n", str_factor);
01972 LOG(llevDebug, " item %s weight= %d\n", throw_ob->name, throw_ob->weight);
01973 #endif
01974
01975
01976
01977
01978
01979 mflags = get_map_flags(part->map, &m, part->x+freearr_x[dir], part->y+freearr_y[dir], &sx, &sy);
01980
01981 if (!dir
01982 || (eff_str <= 1)
01983 || (mflags&P_OUT_OF_MAP)
01984 || (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW)) {
01985
01986
01987 remove_ob(throw_ob);
01988 throw_ob->x = part->x; throw_ob->y = part->y;
01989 insert_ob_in_map(throw_ob, part->map, op, 0);
01990 if (op->type == PLAYER) {
01991 if (eff_str <= 1) {
01992 query_name(throw_ob, name, MAX_BUF);
01993 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01994 "Your load is so heavy you drop %s to the ground.",
01995 "Your load is so heavy you drop %s to the ground.",
01996 name);
01997 } else if (!dir) {
01998 query_name(throw_ob, name, MAX_BUF);
01999 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
02000 "You throw %s at the ground.",
02001 "You throw %s at the ground.",
02002 name);
02003 } else
02004 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
02005 "Something is in the way.", NULL);
02006 }
02007 return 0;
02008 }
02009
02010 left = throw_ob;
02011
02012 left_tag = left->count;
02013
02014
02015
02016
02017
02018 if ((throw_ob = get_split_ob(throw_ob, 1, NULL, 0)) == NULL) {
02019 throw_ob = left;
02020 remove_ob(left);
02021 }
02022
02023
02024 if (throw_ob->type == POTION && throw_ob->subtype == POT_DUST) {
02025 cast_dust(op, throw_ob, dir);
02026 return 1;
02027 }
02028
02029
02030
02031
02032
02033 if ((toss_item = make_throw_ob(throw_ob))) {
02034 throw_ob = toss_item;
02035 if (throw_ob->skill)
02036 free_string(throw_ob->skill);
02037 throw_ob->skill = add_string(skill->skill);
02038 } else {
02039 insert_ob_in_ob(throw_ob, op);
02040 return 0;
02041 }
02042
02043 set_owner(throw_ob, op);
02044
02045
02046
02047
02048 set_owner(throw_ob->inv, op);
02049 throw_ob->direction = dir;
02050 throw_ob->x = part->x;
02051 throw_ob->y = part->y;
02052
02053
02054 dam = str_factor*dam_bonus[eff_str];
02055
02056
02057
02058
02059 throw_ob->last_sp = (eff_str*3)/5;
02060
02061
02062 throw_ob->speed = (speed_bonus[eff_str]+1.0)/1.5;
02063 throw_ob->speed = MIN(1.0, throw_ob->speed);
02064
02065
02066 weight_f = MIN(throw_ob->weight/2000, MAX_STAT);
02067 throw_ob->stats.dam += (dam/3)+dam_bonus[weight_f]+(throw_ob->weight/15000)-2;
02068
02069
02070 throw_ob->stats.food = (dam/2)+(throw_ob->weight/60000);
02071
02072
02073 throw_ob->stats.wc = 25-dex_bonus[op->stats.Dex]-thaco_bonus[eff_str]-skill->level;
02074
02075
02076
02077
02078
02079
02080
02081 if (QUERY_FLAG(throw_ob->inv, FLAG_IS_THROWN)) {
02082 throw_ob->last_sp += eff_str/3;
02083 throw_ob->stats.dam += throw_ob->inv->stats.dam+throw_ob->magic+2;
02084 throw_ob->stats.wc -= throw_ob->magic+throw_ob->inv->stats.wc;
02085
02086 if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
02087 SET_ANIMATION(throw_ob, dir);
02088 } else {
02089
02090 if (throw_ob->material&M_LEATHER) {
02091 throw_ob->stats.dam -= 1;
02092 throw_ob->stats.food -= 10;
02093 }
02094 if (throw_ob->material&M_GLASS)
02095 throw_ob->stats.food += 60;
02096
02097 if (throw_ob->material&M_ORGANIC) {
02098 throw_ob->stats.dam -= 3;
02099 throw_ob->stats.food += 55;
02100 }
02101 if (throw_ob->material&M_PAPER||throw_ob->material&M_CLOTH) {
02102 throw_ob->stats.dam -= 5;
02103 throw_ob->speed *= 0.8;
02104 throw_ob->stats.wc += 3;
02105 throw_ob->stats.food -= 30;
02106 }
02107
02108 if (throw_ob->weight > 500)
02109 throw_ob->speed *= 0.8;
02110 if (throw_ob->weight > 50)
02111 throw_ob->speed *= 0.5;
02112
02113 }
02114
02115
02116 if (throw_ob->stats.dam < 0)
02117 throw_ob->stats.dam = 0;
02118 if (throw_ob->last_sp > eff_str)
02119 throw_ob->last_sp = eff_str;
02120 if (throw_ob->stats.food < 0)
02121 throw_ob->stats.food = 0;
02122 if (throw_ob->stats.food > 100)
02123 throw_ob->stats.food = 100;
02124 if (throw_ob->stats.wc > 30)
02125 throw_ob->stats.wc = 30;
02126
02127
02128 pause_f = ((2*eff_str)/3)+20+skill->level;
02129
02130
02131 if (pause_f < 10)
02132 pause_f = 10;
02133 if (pause_f > 100)
02134 pause_f = 100;
02135
02136
02137
02138
02139
02140 op->speed_left -= 50/pause_f;
02141
02142 update_ob_speed(throw_ob);
02143 throw_ob->speed_left = 0;
02144 throw_ob->map = part->map;
02145
02146 throw_ob->move_type = MOVE_FLY_LOW;
02147 throw_ob->move_on = MOVE_FLY_LOW|MOVE_WALK;
02148
02149
02150 execute_event(throw_ob, EVENT_THROW, op, NULL, NULL, SCRIPT_FIX_ACTIVATOR);
02151 #ifdef DEBUG_THROW
02152 LOG(llevDebug, " pause_f=%d \n", pause_f);
02153 LOG(llevDebug, " %s stats: wc=%d dam=%d dist=%d spd=%f break=%d\n", throw_ob->name, throw_ob->stats.wc, throw_ob->stats.dam, throw_ob->last_sp, throw_ob->speed, throw_ob->stats.food);
02154 LOG(llevDebug, "inserting tossitem (%d) into map\n", throw_ob->count);
02155 #endif
02156 tag = throw_ob->count;
02157 insert_ob_in_map(throw_ob, part->map, op, 0);
02158 if (!was_destroyed(throw_ob, tag))
02159 ob_process(throw_ob);
02160 return 1;
02161 }
02162
02180 int skill_throw(object *op, object *part, int dir, const char *params, object *skill) {
02181 object *throw_ob;
02182
02183 if (op->type == PLAYER)
02184 throw_ob = find_throw_ob(op, params);
02185 else
02186 throw_ob = find_mon_throw_ob(op);
02187
02188 return do_throw(op, part, throw_ob, dir, skill);
02189 }