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
00028
00035 #include <global.h>
00036 #include <assert.h>
00037 #ifndef WIN32
00038 #include <pwd.h>
00039 #endif
00040 #ifndef __CEXTRACT__
00041 #include <sproto.h>
00042 #endif
00043 #include <sounds.h>
00044 #include <living.h>
00045 #include <object.h>
00046 #include <spells.h>
00047 #include <skills.h>
00048 #include <newclient.h>
00049
00050 static archetype *get_player_archetype(archetype *at);
00051
00052 static int action_makes_visible(object *op);
00053
00062 player *find_player(const char *plname) {
00063 player *pl;
00064 char name[MAX_BUF];
00065
00066 for (pl = first_player; pl != NULL; pl = pl->next) {
00067 if (pl->ob != NULL) {
00068 query_name(pl->ob, name, MAX_BUF);
00069 if (!strcmp(name, plname))
00070 return pl;
00071 }
00072 }
00073 return NULL;
00074 }
00075
00084 player *find_player_partial_name(const char *plname) {
00085 player *pl;
00086 player *found = NULL;
00087 size_t namelen = strlen(plname);
00088
00089 for (pl = first_player; pl != NULL; pl = pl->next) {
00090 if (strlen(pl->ob->name) < namelen)
00091 continue;
00092
00093 if (!strcmp(pl->ob->name, plname))
00094 return pl;
00095
00096 if (!strncasecmp(pl->ob->name, plname, namelen)) {
00097 if (found)
00098 return NULL;
00099
00100 found = pl;
00101 }
00102 }
00103 return found;
00104 }
00105
00112 void display_motd(const object *op) {
00113 char buf[MAX_BUF];
00114 char motd[HUGE_BUF];
00115 FILE *fp;
00116 int comp;
00117 int size;
00118
00119 snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.motd);
00120 if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
00121 return;
00122 }
00123 motd[0] = '\0';
00124 size = 0;
00125 while (fgets(buf, MAX_BUF, fp) != NULL) {
00126 if (*buf == '#')
00127 continue;
00128 strncat(motd+size, buf, HUGE_BUF-size);
00129 size += strlen(buf);
00130 }
00131 draw_ext_info(NDI_UNIQUE|NDI_GREEN, 0, op, MSG_TYPE_MOTD, MSG_SUBTYPE_NONE,
00132 motd, NULL);
00133 close_and_delete(fp, comp);
00134 }
00135
00142 void send_rules(const object *op) {
00143 char buf[MAX_BUF];
00144 char rules[HUGE_BUF];
00145 FILE *fp;
00146 int comp;
00147 int size;
00148
00149 snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.rules);
00150 if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
00151 return;
00152 }
00153 rules[0] = '\0';
00154 size = 0;
00155 while (fgets(buf, MAX_BUF, fp) != NULL) {
00156 if (*buf == '#')
00157 continue;
00158 if (size+strlen(buf) >= HUGE_BUF) {
00159 LOG(llevDebug, "Warning, rules size is > %d bytes.\n", HUGE_BUF);
00160 break;
00161 }
00162 strncat(rules+size, buf, HUGE_BUF-size);
00163 size += strlen(buf);
00164 }
00165 draw_ext_info(NDI_UNIQUE|NDI_GREEN, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_RULES,
00166 rules, NULL);
00167 close_and_delete(fp, comp);
00168 }
00169
00176 void send_news(const object *op) {
00177 char buf[MAX_BUF];
00178 char news[HUGE_BUF];
00179 char subject[MAX_BUF];
00180 FILE *fp;
00181 int comp;
00182 int size;
00183
00184 snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.news);
00185 if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL)
00186 return;
00187 news[0] = '\0';
00188 subject[0] = '\0';
00189 size = 0;
00190 while (fgets(buf, MAX_BUF, fp) != NULL) {
00191 if (*buf == '#')
00192 continue;
00193 if (*buf == '%') {
00194 if (size > 0)
00195 draw_ext_info_format(NDI_UNIQUE|NDI_GREEN, 0, op,
00196 MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS,
00197 "%s:\n%s",
00198 "%s:\n%s",
00199 subject, news);
00200 strcpy(subject, buf+1);
00201 strip_endline(subject);
00202 size = 0;
00203 news[0] = '\0';
00204 } else {
00205 if (size+strlen(buf) >= HUGE_BUF) {
00206 LOG(llevDebug, "Warning, one news item has size > %d bytes.\n", HUGE_BUF);
00207 break;
00208 }
00209 strncat(news+size, buf, HUGE_BUF-size);
00210 size += strlen(buf);
00211 }
00212 }
00213
00214 draw_ext_info_format(NDI_UNIQUE|NDI_GREEN, 0, op,
00215 MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS,
00216 "%s:\n%s",
00217 "%s:\n%s",
00218 subject, news);
00219 close_and_delete(fp, comp);
00220 }
00221
00230 int playername_ok(const char *cp) {
00231
00232 if (*cp == '-' || *cp == '_')
00233 return 0;
00234
00235 for (; *cp != '\0'; cp++)
00236 if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= 'A' && *cp <= 'Z'))
00237 && *cp != '-'
00238 && *cp != '_')
00239 return 0;
00240 return 1;
00241 }
00242
00258 static player *get_player(player *p) {
00259 object *op = arch_to_object(get_player_archetype(NULL));
00260 int i;
00261
00262 if (!p) {
00263 player *tmp;
00264
00265 p = (player *)malloc(sizeof(player));
00266 if (p == NULL)
00267 fatal(OUT_OF_MEMORY);
00268
00269
00270
00271
00272
00273
00274
00275 tmp = first_player;
00276 while (tmp != NULL && tmp->next != NULL)
00277 tmp = tmp->next;
00278 if (tmp != NULL)
00279 tmp->next = p;
00280 else
00281 first_player = p;
00282
00283 p->next = NULL;
00284 } else {
00285
00286 clear_player(p);
00287 }
00288
00289
00290
00291
00292 memset((void *)((char *)p+offsetof(player, maplevel)), 0, sizeof(player)-offsetof(player, maplevel));
00293
00294
00295
00296
00297 p->party = NULL;
00298 p->rejoin_party = party_rejoin_if_exists;
00299 p->outputs_sync = 16;
00300 p->outputs_count = 1;
00301 p->unapply = unapply_nochoice;
00302 p->Swap_First = -1;
00303
00304 #ifdef AUTOSAVE
00305 p->last_save_tick = 9999999;
00306 #endif
00307
00308 strcpy(p->savebed_map, first_map_path);
00309
00310 op->contr = p;
00311 p->ob = op;
00312 op->speed_left = 0.5;
00313 op->speed = 1.0;
00314 op->direction = 5;
00315 op->stats.wc = 2;
00316 op->run_away = 25;
00317 p->socket.monitor_spells = 0;
00318
00319 roll_stats(op);
00320 p->state = ST_ROLL_STAT;
00321 clear_los(op);
00322
00323 p->gen_sp_armour = 10;
00324 p->last_speed = -1;
00325 p->shoottype = range_none;
00326 p->bowtype = bow_normal;
00327 p->petmode = pet_normal;
00328 p->listening = 10;
00329 p->last_weapon_sp = -1;
00330 p->peaceful = 1;
00331 p->do_los = 1;
00332 p->explore = 0;
00333 p->no_shout = 0;
00334 p->language = 0;
00335
00336 strncpy(p->title, op->arch->clone.name, sizeof(p->title)-1);
00337 p->title[sizeof(p->title)-1] = '\0';
00338 op->race = add_string(op->arch->clone.race);
00339
00340 CLEAR_FLAG(op, FLAG_READY_SKILL);
00341
00342
00343
00344
00345
00346
00347 for (i = 0; i < NUM_SKILLS; i++) {
00348 p->last_skill_exp[i] = -1;
00349 p->last_skill_ob[i] = NULL;
00350 }
00351 for (i = 0; i < NROFATTACKS; i++) {
00352 p->last_resist[i] = -1;
00353 }
00354 p->last_stats.exp = -1;
00355 p->last_weight = (uint32)-1;
00356
00357 p->socket.update_look = 0;
00358 p->socket.look_position = 0;
00359 return p;
00360 }
00361
00368 static void set_first_map(object *op) {
00369 strcpy(op->contr->maplevel, first_map_path);
00370 op->x = -1;
00371 op->y = -1;
00372 enter_exit(op, NULL);
00373 }
00374
00383 void add_player(socket_struct *ns) {
00384 player *p;
00385
00386 p = get_player(NULL);
00387 memcpy(&p->socket, ns, sizeof(socket_struct));
00388
00389
00390
00391
00392 ns->faces_sent = NULL;
00393
00394 if (p->socket.faces_sent == NULL)
00395 fatal(OUT_OF_MEMORY);
00396
00397
00398
00399
00400
00401 SockList_ResetRead(&p->socket.inbuf);
00402 set_first_map(p->ob);
00403
00404 CLEAR_FLAG(p->ob, FLAG_FRIENDLY);
00405 add_friendly_object(p->ob);
00406 send_rules(p->ob);
00407 send_news(p->ob);
00408 display_motd(p->ob);
00409 get_name(p->ob);
00410 }
00411
00422 static archetype *get_player_archetype(archetype *at) {
00423 archetype *start = at;
00424
00425 for (;;) {
00426 if (at == NULL || at->next == NULL)
00427 at = first_archetype;
00428 else
00429 at = at->next;
00430 if (at->clone.type == PLAYER)
00431 return at;
00432 if (at == start) {
00433 LOG(llevError, "No Player archetypes\n");
00434 exit(-1);
00435 }
00436 }
00437 }
00438
00447 object *get_nearest_player(object *mon) {
00448 object *op = NULL;
00449 player *pl = NULL;
00450 objectlink *ol;
00451 unsigned lastdist;
00452 rv_vector rv;
00453
00454 for (ol = first_friendly_object, lastdist = 1000; ol != NULL; ol = ol->next) {
00455
00456
00457
00458
00459
00460 while (QUERY_FLAG(ol->ob, FLAG_FREED) || !QUERY_FLAG(ol->ob, FLAG_FRIENDLY)) {
00461 object *tmp = ol->ob;
00462
00463
00464
00465
00466 LOG(llevDebug, "get_nearest_player: Found free/non friendly object on friendly list\n");
00467 ol = ol->next;
00468 remove_friendly_object(tmp);
00469 if (!ol)
00470 return op;
00471 }
00472
00473
00474
00475
00476
00477
00478
00479
00480 if (!can_detect_enemy(mon, ol->ob, &rv))
00481 continue;
00482
00483 if (lastdist > rv.distance) {
00484 op = ol->ob;
00485 lastdist = rv.distance;
00486 }
00487 }
00488 for (pl = first_player; pl != NULL; pl = pl->next) {
00489 if (can_detect_enemy(mon, pl->ob, &rv)) {
00490 if (lastdist > rv.distance) {
00491 op = pl->ob;
00492 lastdist = rv.distance;
00493 }
00494 }
00495 }
00496 return op;
00497 }
00498
00508 #define DETOUR_AMOUNT 2
00509
00523 #define MAX_SPACES 50
00524
00556 int path_to_player(object *mon, object *pl, unsigned mindiff) {
00557 rv_vector rv;
00558 sint16 x, y;
00559 int lastx, lasty, dir, i, diff, firstdir = 0, lastdir, max = MAX_SPACES, mflags, blocked;
00560 mapstruct *m, *lastmap;
00561
00562 get_rangevector(mon, pl, &rv, 0);
00563
00564 if (rv.distance < mindiff)
00565 return 0;
00566
00567 x = mon->x;
00568 y = mon->y;
00569 m = mon->map;
00570 dir = rv.direction;
00571 lastdir = firstdir = rv.direction;
00572 diff = MAX(FABS(rv.distance_x), FABS(rv.distance_y));
00573
00574 if (diff > max)
00575 return 0;
00576 while (diff > 1 && max > 0) {
00577 lastx = x;
00578 lasty = y;
00579 lastmap = m;
00580 x = lastx+freearr_x[dir];
00581 y = lasty+freearr_y[dir];
00582
00583 mflags = get_map_flags(m, &m, x, y, &x, &y);
00584 blocked = (mflags&P_OUT_OF_MAP) ? MOVE_ALL : GET_MAP_MOVE_BLOCK(m, x, y);
00585
00586
00587 if ((mflags&P_OUT_OF_MAP)
00588 || ((OB_TYPE_MOVE_BLOCK(mon, blocked) || (mflags&P_IS_ALIVE))
00589 && (m == mon->map && blocked_link(mon, m, x, y)))) {
00590
00591
00592
00593 get_rangevector_from_mapcoord(lastmap, lastx, lasty, pl, &rv, 0);
00594 if (rv.direction != dir) {
00595
00596
00597
00598 x = lastx;
00599 y = lasty;
00600 m = lastmap;
00601 dir = firstdir = rv.direction;
00602 } else {
00603
00604
00605
00606
00607
00608
00609
00610
00611 for (i = -DETOUR_AMOUNT; i <= DETOUR_AMOUNT; i++) {
00612 if (i == 0)
00613 continue;
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626 x = lastx+freearr_x[absdir(lastdir+i)];
00627 y = lasty+freearr_y[absdir(lastdir+i)];
00628 m = lastmap;
00629 mflags = get_map_flags(m, &m, x, y, &x, &y);
00630 if (mflags&P_OUT_OF_MAP)
00631 continue;
00632 blocked = GET_MAP_MOVE_BLOCK(m, x, y);
00633 if (OB_TYPE_MOVE_BLOCK(mon, blocked))
00634 continue;
00635 if (mflags&P_IS_ALIVE)
00636 continue;
00637
00638 if (m == mon->map && blocked_link(mon, m, x, y))
00639 break;
00640 }
00641
00642
00643
00644 if (i == (DETOUR_AMOUNT+1))
00645 return 0;
00646 diff--;
00647 lastdir = dir;
00648 max--;
00649 if (!firstdir)
00650 firstdir = dir+i;
00651 }
00652 }
00653 else {
00654
00655 diff--;
00656 max--;
00657 lastdir = dir;
00658 if (!firstdir)
00659 firstdir = dir;
00660 }
00661 if (diff <= 1) {
00662
00663
00664
00665 get_rangevector_from_mapcoord(m, x, y, pl, &rv, 0);
00666 diff = MAX(FABS(rv.distance_x), FABS(rv.distance_y));
00667 }
00668 if (diff > max)
00669 return 0;
00670 }
00671
00672 if (!max)
00673 return 0;
00674
00675 return firstdir;
00676 }
00677
00688 void give_initial_items(object *pl, treasurelist *items) {
00689 object *op, *next = NULL;
00690
00691 if (pl->randomitems != NULL)
00692 create_treasure(items, pl, GT_STARTEQUIP|GT_ONLY_GOOD, 1, 0);
00693
00694 for (op = pl->inv; op; op = next) {
00695 next = op->below;
00696
00697
00698
00699
00700 if (op->type == FORCE && !QUERY_FLAG(op, FLAG_NEUTRAL))
00701 SET_FLAG(op, FLAG_APPLIED);
00702
00703
00704
00705
00706 if (pl->type == PLAYER) {
00707 if ((!QUERY_FLAG(pl, FLAG_USE_ARMOUR) && IS_ARMOR(op))
00708 || (!QUERY_FLAG(pl, FLAG_USE_WEAPON) && IS_WEAPON(op))
00709 || (!QUERY_FLAG(pl, FLAG_USE_SHIELD) && IS_SHIELD(op))) {
00710 remove_ob(op);
00711 free_object(op);
00712 continue;
00713 }
00714 }
00715
00716
00717
00718
00719
00720
00721
00722 if (op->type == SPELLBOOK || op->type == SKILL) {
00723 object *tmp;
00724
00725 for (tmp = op->below; tmp; tmp = tmp->below)
00726 if (tmp->type == op->type && tmp->name == op->name)
00727 break;
00728
00729 if (tmp) {
00730 remove_ob(op);
00731 free_object(op);
00732 LOG(llevError, "give_initial_items: Removing duplicate object %s\n", tmp->name);
00733 continue;
00734 }
00735 if (op->nrof > 1)
00736 op->nrof = 1;
00737 }
00738
00739 if (op->type == SPELLBOOK && op->inv) {
00740 CLEAR_FLAG(op->inv, FLAG_STARTEQUIP);
00741 }
00742
00743
00744
00745
00746
00747 if (need_identify(op)) {
00748 SET_FLAG(op, FLAG_IDENTIFIED);
00749 CLEAR_FLAG(op, FLAG_CURSED);
00750 CLEAR_FLAG(op, FLAG_DAMNED);
00751 }
00752 if (op->type == SPELL) {
00753 remove_ob(op);
00754 free_object(op);
00755 continue;
00756 } else if (op->type == SKILL) {
00757 SET_FLAG(op, FLAG_CAN_USE_SKILL);
00758 op->stats.exp = 0;
00759 op->level = 1;
00760 }
00761
00762 else
00763 SET_FLAG(op, FLAG_INV_LOCKED);
00764 }
00765
00766
00767 link_player_skills(pl);
00768
00773 for (op = pl->inv; op; op = next) {
00774 next = op->below;
00775 if ((IS_ARMOR(op) || IS_WEAPON(op) || IS_SHIELD(op)) && !QUERY_FLAG(op, FLAG_APPLIED))
00776 manual_apply(pl, op, AP_NOPRINT);
00777 }
00778 }
00779
00786 void get_name(object *op) {
00787 op->contr->write_buf[0] = '\0';
00788 op->contr->state = ST_GET_NAME;
00789 send_query(&op->contr->socket, 0, "What is your name?\n:");
00790 }
00791
00798 void get_password(object *op) {
00799 op->contr->write_buf[0] = '\0';
00800 op->contr->state = ST_GET_PASSWORD;
00801 send_query(&op->contr->socket, CS_QUERY_HIDEINPUT, "What is your password?\n:");
00802 }
00803
00810 void play_again(object *op) {
00811 op->contr->state = ST_PLAY_AGAIN;
00812 op->chosen_skill = NULL;
00813 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Do you want to play again (a/q)?");
00814
00815
00816
00817
00818
00819
00820
00821
00822 if (!QUERY_FLAG(op, FLAG_REMOVED))
00823 remove_ob(op);
00824
00825
00826
00827
00828 op->map = NULL;
00829 }
00830
00839 void receive_play_again(object *op, char key) {
00840 if (key == 'q' || key == 'Q') {
00841 remove_friendly_object(op);
00842 leave(op->contr, 0);
00843 return;
00844 } else if (key == 'a' || key == 'A') {
00845 player *pl = op->contr;
00846 const char *name = op->name;
00847
00848 add_refcount(name);
00849 remove_friendly_object(op);
00850 free_object(op);
00851 pl = get_player(pl);
00852 op = pl->ob;
00853 add_friendly_object(op);
00854 op->contr->password[0] = '~';
00855 FREE_AND_CLEAR_STR(op->name);
00856 FREE_AND_CLEAR_STR(op->name_pl);
00857
00858
00859 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN,
00860 "\n", "\n");
00861 get_name(op);
00862 op->name = name;
00863 op->name_pl = add_string(name);
00864 set_first_map(op);
00865 } else {
00866
00867 play_again(op);
00868 }
00869 }
00870
00877 void confirm_password(object *op) {
00878 op->contr->write_buf[0] = '\0';
00879 op->contr->state = ST_CONFIRM_PASSWORD;
00880 send_query(&op->contr->socket, CS_QUERY_HIDEINPUT, "Please type your password again.\n:");
00881 }
00882
00891 void get_party_password(object *op, partylist *party) {
00892 if (party == NULL) {
00893 LOG(llevError, "get_party_password(): tried to make player %s join a NULL party\n", op->name);
00894 return;
00895 }
00896 op->contr->write_buf[0] = '\0';
00897 op->contr->state = ST_GET_PARTY_PASSWORD;
00898 op->contr->party_to_join = party;
00899 send_query(&op->contr->socket, CS_QUERY_HIDEINPUT, "What is the password?\n:");
00900 }
00901
00908 int roll_stat(void) {
00909 int a[4], i, j, k;
00910
00911 for (i = 0; i < 4; i++)
00912 a[i] = (int)RANDOM()%6+1;
00913
00914 for (i = 0, j = 0, k = 7; i < 4; i++)
00915 if (a[i] < k)
00916 k = a[i],
00917 j = i;
00918
00919 for (i = 0, k = 0; i < 4; i++) {
00920 if (i != j)
00921 k += a[i];
00922 }
00923 return k;
00924 }
00925
00932 void roll_stats(object *op) {
00933 int sum = 0;
00934 int i = 0, j = 0;
00935 int statsort[7];
00936
00937 do {
00938 op->stats.Str = roll_stat();
00939 op->stats.Dex = roll_stat();
00940 op->stats.Int = roll_stat();
00941 op->stats.Con = roll_stat();
00942 op->stats.Wis = roll_stat();
00943 op->stats.Pow = roll_stat();
00944 op->stats.Cha = roll_stat();
00945 sum = op->stats.Str+op->stats.Dex+op->stats.Int+op->stats.Con+op->stats.Wis+op->stats.Pow+op->stats.Cha;
00946 } while (sum != 105);
00947
00948
00949 statsort[0] = op->stats.Str;
00950 statsort[1] = op->stats.Dex;
00951 statsort[2] = op->stats.Int;
00952 statsort[3] = op->stats.Con;
00953 statsort[4] = op->stats.Wis;
00954 statsort[5] = op->stats.Pow;
00955 statsort[6] = op->stats.Cha;
00956
00957
00958 do {
00959 if (statsort[i] < statsort[i+1]) {
00960 j = statsort[i];
00961 statsort[i] = statsort[i+1];
00962 statsort[i+1] = j;
00963 i = 0;
00964 } else {
00965 i++;
00966 }
00967 } while (i < 6);
00968
00969 op->stats.Str = statsort[0];
00970 op->stats.Dex = statsort[1];
00971 op->stats.Con = statsort[2];
00972 op->stats.Int = statsort[3];
00973 op->stats.Wis = statsort[4];
00974 op->stats.Pow = statsort[5];
00975 op->stats.Cha = statsort[6];
00976
00977 op->contr->orig_stats.Str = op->stats.Str;
00978 op->contr->orig_stats.Dex = op->stats.Dex;
00979 op->contr->orig_stats.Int = op->stats.Int;
00980 op->contr->orig_stats.Con = op->stats.Con;
00981 op->contr->orig_stats.Wis = op->stats.Wis;
00982 op->contr->orig_stats.Pow = op->stats.Pow;
00983 op->contr->orig_stats.Cha = op->stats.Cha;
00984
00985 op->level = 1;
00986 op->stats.exp = 0;
00987 op->stats.ac = 0;
00988
00989 op->contr->levhp[1] = 9;
00990 op->contr->levsp[1] = 6;
00991 op->contr->levgrace[1] = 3;
00992
00993 fix_object(op);
00994 op->stats.hp = op->stats.maxhp;
00995 op->stats.sp = op->stats.maxsp;
00996 op->stats.grace = op->stats.maxgrace;
00997 op->contr->orig_stats = op->stats;
00998 }
00999
01006 void roll_again(object *op) {
01007 esrv_new_player(op->contr, 0);
01008 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "[y] to roll new stats [n] to use stats\n[1-7] [1-7] to swap stats.\nRoll again (y/n/1-7)? ");
01009 }
01010
01020 static void swap_stat(object *op, int Swap_Second) {
01021 signed char tmp;
01022
01023 if (op->contr->Swap_First == -1) {
01024 LOG(llevError, "player.c:swap_stat() - Swap_First is -1\n");
01025 return;
01026 }
01027
01028 tmp = get_attr_value(&op->contr->orig_stats, op->contr->Swap_First);
01029
01030 set_attr_value(&op->contr->orig_stats, op->contr->Swap_First, get_attr_value(&op->contr->orig_stats, Swap_Second));
01031
01032 set_attr_value(&op->contr->orig_stats, Swap_Second, tmp);
01033
01034 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01035 "%s done\n",
01036 "%s done\n",
01037 short_stat_name[Swap_Second]);
01038
01039 op->stats.Str = op->contr->orig_stats.Str;
01040 op->stats.Dex = op->contr->orig_stats.Dex;
01041 op->stats.Con = op->contr->orig_stats.Con;
01042 op->stats.Int = op->contr->orig_stats.Int;
01043 op->stats.Wis = op->contr->orig_stats.Wis;
01044 op->stats.Pow = op->contr->orig_stats.Pow;
01045 op->stats.Cha = op->contr->orig_stats.Cha;
01046 op->stats.ac = 0;
01047
01048 op->level = 1;
01049 op->stats.exp = 0;
01050 op->stats.ac = 0;
01051
01052 op->contr->levhp[1] = 9;
01053 op->contr->levsp[1] = 6;
01054 op->contr->levgrace[1] = 3;
01055
01056 fix_object(op);
01057 op->stats.hp = op->stats.maxhp;
01058 op->stats.sp = op->stats.maxsp;
01059 op->stats.grace = op->stats.maxgrace;
01060 op->contr->orig_stats = op->stats;
01061 op->contr->Swap_First = -1;
01062 }
01063
01079 void key_roll_stat(object *op, char key) {
01080 int keynum = key-'0';
01081 static const sint8 stat_trans[] = {
01082 -1,
01083 STR,
01084 DEX,
01085 CON,
01086 INT,
01087 WIS,
01088 POW,
01089 CHA
01090 };
01091
01092 if (keynum > 0 && keynum <= 7) {
01093 if (op->contr->Swap_First == -1) {
01094 op->contr->Swap_First = stat_trans[keynum];
01095 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01096 "%s ->",
01097 "%s ->",
01098 short_stat_name[stat_trans[keynum]]);
01099 } else
01100 swap_stat(op, stat_trans[keynum]);
01101
01102 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "");
01103 return;
01104 }
01105 switch (key) {
01106 case 'n':
01107 case 'N': {
01108 SET_FLAG(op, FLAG_WIZ);
01109 if (op->map == NULL) {
01110 LOG(llevError, "Map == NULL in state 2\n");
01111 break;
01112 }
01113
01114 SET_ANIMATION(op, 2);
01115
01116 add_statbonus(op);
01117 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Now choose a character.\nPress any key to change outlook.\nPress `d' when you're pleased.\n");
01118 op->contr->state = ST_CHANGE_CLASS;
01119 if (op->msg)
01120 draw_ext_info(NDI_BLUE, 0, op,
01121 MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01122 op->msg, op->msg);
01123 return;
01124 }
01125 case 'y':
01126 case 'Y':
01127 roll_stats(op);
01128 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "");
01129 return;
01130
01131 case 'q':
01132 case 'Q':
01133 play_again(op);
01134 return;
01135
01136 default:
01137 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Yes, No, Quit or 1-6. Roll again?");
01138 return;
01139 }
01140 return;
01141 }
01142
01156 void key_change_class(object *op, char key) {
01157 int tmp_loop;
01158
01159 if (key == 'q' || key == 'Q') {
01160 remove_ob(op);
01161 play_again(op);
01162 return;
01163 }
01164 if (key == 'd' || key == 'D') {
01165 char buf[MAX_BUF];
01166
01167
01168 esrv_new_player(op->contr, op->weight+op->carrying);
01169 create_treasure(find_treasurelist("starting_wealth"), op, 0, 0, 0);
01170
01171
01172 execute_global_event(EVENT_BORN, op);
01173
01174
01175 execute_global_event(EVENT_LOGIN, op->contr, op->contr->socket.host);
01176 op->contr->state = ST_PLAYING;
01177
01178 if (op->msg) {
01179 free_string(op->msg);
01180 op->msg = NULL;
01181 }
01182
01183
01184
01185
01186 snprintf(buf, sizeof(buf), "%s/%s/%s", settings.localdir, settings.playerdir, op->name);
01187 make_path_to_file(buf);
01188
01189 #ifdef AUTOSAVE
01190 op->contr->last_save_tick = pticks;
01191 #endif
01192 start_info(op);
01193 CLEAR_FLAG(op, FLAG_WIZ);
01194 give_initial_items(op, op->randomitems);
01195 link_player_skills(op);
01196 esrv_send_inventory(op, op);
01197 fix_object(op);
01198
01199
01200
01201
01202 if (*first_map_ext_path) {
01203 object *tmp;
01204 char mapname[MAX_BUF];
01205 mapstruct *oldmap;
01206
01207 oldmap = op->map;
01208
01209 snprintf(mapname, MAX_BUF-1, "%s/%s", first_map_ext_path, op->arch->name);
01210
01211 tmp = get_object();
01212 EXIT_PATH(tmp) = add_string(mapname);
01213 EXIT_X(tmp) = op->x;
01214 EXIT_Y(tmp) = op->y;
01215 enter_exit(op, tmp);
01216
01217 if (oldmap != op->map) {
01218
01219 op->contr->bed_x = op->x;
01220 op->contr->bed_y = op->y;
01221 snprintf(op->contr->savebed_map, sizeof(op->contr->savebed_map), "%s", mapname);
01222 }
01223
01224 free_object(tmp);
01225 } else {
01226 LOG(llevDebug, "first_map_ext_path not set\n");
01227 }
01228 return;
01229 }
01230
01231
01232
01233
01234
01235 tmp_loop = 0;
01236 while (!tmp_loop) {
01237 const char *name = add_string(op->name);
01238 int x = op->x, y = op->y;
01239
01240 remove_statbonus(op);
01241 remove_ob(op);
01242 op->arch = get_player_archetype(op->arch);
01243 copy_object(&op->arch->clone, op);
01244 op->stats = op->contr->orig_stats;
01245 free_string(op->name);
01246 op->name = name;
01247 free_string(op->name_pl);
01248 op->name_pl = add_string(name);
01249 op->x = x;
01250 op->y = y;
01251 SET_ANIMATION(op, 2);
01252 insert_ob_in_map(op, op->map, op, 0);
01253 strncpy(op->contr->title, op->arch->clone.name, sizeof(op->contr->title)-1);
01254 op->contr->title[sizeof(op->contr->title)-1] = '\0';
01255 add_statbonus(op);
01256 tmp_loop = allowed_class(op);
01257 }
01258 update_object(op, UP_OBJ_FACE);
01259 esrv_update_item(UPD_FACE, op, op);
01260 fix_object(op);
01261 op->stats.hp = op->stats.maxhp;
01262 op->stats.sp = op->stats.maxsp;
01263 op->stats.grace = 0;
01264 if (op->msg)
01265 draw_ext_info(NDI_BLUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01266 op->msg, op->msg);
01267 send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Press any key for the next race.\nPress `d' to play this race.\n");
01268 }
01269
01278 void key_confirm_quit(object *op, char key) {
01279 char buf[MAX_BUF];
01280
01281 if (key != 'y' && key != 'Y' && key != 'q' && key != 'Q') {
01282 op->contr->state = ST_PLAYING;
01283 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN,
01284 "OK, continuing to play.", NULL);
01285 return;
01286 }
01287
01288
01289 execute_global_event(EVENT_REMOVE, op);
01290 terminate_all_pets(op);
01291 remove_ob(op);
01292 op->direction = 0;
01293 draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
01294 "%s quits the game.",
01295 "%s quits the game.",
01296 op->name);
01297
01298 strcpy(op->contr->killer, "quit");
01299 check_score(op, 0);
01300 op->contr->party = NULL;
01301 if (settings.set_title == TRUE)
01302 op->contr->own_title[0] = '\0';
01303
01304 if (!QUERY_FLAG(op, FLAG_WAS_WIZ)) {
01305 mapstruct *mp, *next;
01306
01307
01308
01309
01310
01311 snprintf(buf, sizeof(buf), "%s/%s/%s/", settings.localdir, settings.playerdir, op->name);
01312 for (mp = first_map; mp != NULL; mp = next) {
01313 next = mp->next;
01314 if (!strncmp(mp->path, buf, strlen(buf)))
01315 delete_map(mp);
01316 }
01317
01318 delete_character(op->name);
01319 }
01320 play_again(op);
01321 }
01322
01329 static void flee_player(object *op) {
01330 int dir, diff;
01331 rv_vector rv;
01332
01333 if (op->stats.hp < 0) {
01334 LOG(llevDebug, "Fleeing player is dead.\n");
01335 CLEAR_FLAG(op, FLAG_SCARED);
01336 return;
01337 }
01338
01339 if (op->enemy == NULL) {
01340 LOG(llevDebug, "Fleeing player had no enemy.\n");
01341 CLEAR_FLAG(op, FLAG_SCARED);
01342 return;
01343 }
01344
01345
01346
01347
01348
01349 if (op->enemy->map == NULL) {
01350 CLEAR_FLAG(op, FLAG_SCARED);
01351 op->enemy = NULL;
01352 return;
01353 }
01354
01355 if (!(random_roll(0, 4, op, PREFER_LOW)) && did_make_save(op, op->level, 0)) {
01356 op->enemy = NULL;
01357 CLEAR_FLAG(op, FLAG_SCARED);
01358 return;
01359 }
01360 get_rangevector(op, op->enemy, &rv, 0);
01361
01362 dir = absdir(4+rv.direction);
01363 for (diff = 0; diff < 3; diff++) {
01364 int m = 1-(RANDOM()&2);
01365 if (move_ob(op, absdir(dir+diff*m), op)
01366 || (diff == 0 && move_ob(op, absdir(dir-diff*m), op))) {
01367 return;
01368 }
01369 }
01370
01371 CLEAR_FLAG(op, FLAG_SCARED);
01372 op->enemy = NULL;
01373 }
01374
01384 int check_pick(object *op) {
01385 object *tmp, *next;
01386 tag_t next_tag = 0, op_tag;
01387 int stop = 0;
01388 int j, k, wvratio;
01389 char putstring[128], tmpstr[16];
01390
01391
01392 if (op->move_type&MOVE_FLYING)
01393 return 1;
01394
01395 op_tag = op->count;
01396
01397 next = op->below;
01398 if (next)
01399 next_tag = next->count;
01400
01401
01402
01403 while (next && !was_destroyed(next, next_tag)) {
01404 tmp = next;
01405 next = tmp->below;
01406 if (next)
01407 next_tag = next->count;
01408
01409 if (was_destroyed(op, op_tag))
01410 return 0;
01411
01412 if (!can_pick(op, tmp))
01413 continue;
01414
01415 if (op->contr->search_str[0] != '\0' && settings.search_items == TRUE) {
01416 if (item_matched_string(op, tmp, op->contr->search_str))
01417 pick_up(op, tmp);
01418 continue;
01419 }
01420
01421
01422 if (!(op->contr->mode&PU_NEWMODE)) {
01423 switch (op->contr->mode) {
01424 case 0:
01425 return 1;
01426
01427 case 1:
01428 pick_up(op, tmp);
01429 return 1;
01430
01431 case 2:
01432 pick_up(op, tmp);
01433 return 0;
01434
01435 case 3:
01436 return 0;
01437
01438 case 4:
01439 pick_up(op, tmp);
01440 break;
01441
01442 case 5:
01443 pick_up(op, tmp);
01444 stop = 1;
01445 break;
01446
01447 case 6:
01448 if (QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL)
01449 && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED))
01450 pick_up(op, tmp);
01451 break;
01452
01453 case 7:
01454 if (tmp->type == MONEY || tmp->type == GEM)
01455 pick_up(op, tmp);
01456 break;
01457
01458 default:
01459
01460 if (!QUERY_FLAG(tmp, FLAG_UNPAID)
01461 && (query_cost(tmp, op, F_TRUE)*100/(tmp->weight*MAX(tmp->nrof, 1))) >= op->contr->mode)
01462 pick_up(op, tmp);
01463 }
01464 } else {
01465
01466 if (op->contr->mode&PU_DEBUG) {
01467
01468 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG,
01469 "item name: %s item type: %d weight/value: %d",
01470 "item name: %s item type: %d weight/value: %d",
01471 tmp->name ? tmp->name : tmp->arch->name, tmp->type,
01472 (int)(query_cost(tmp, op, F_TRUE)*100/(tmp->weight*MAX(tmp->nrof, 1))));
01473
01474
01475 snprintf(putstring, sizeof(putstring), "...flags: ");
01476 for (k = 0; k < 4; k++) {
01477 for (j = 0; j < 32; j++) {
01478 if ((tmp->flags[k]>>j)&0x01) {
01479 snprintf(tmpstr, sizeof(tmpstr), "%d ", k*32+j);
01480 strcat(putstring, tmpstr);
01481 }
01482 }
01483 }
01484 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
01485 putstring, putstring);
01486 }
01487
01488
01489
01490
01491
01492
01493
01494
01495
01496
01497
01498
01499
01500
01501
01502
01503
01504 if (op->contr->mode&PU_NOTHING)
01505 return 1;
01506
01507
01508
01509
01510
01511 if (op->contr->mode&PU_STOP)
01512 return 0;
01513
01514
01515
01516
01517 if (op->contr->mode&PU_INHIBIT)
01518 return 1;
01519
01520
01521 if (QUERY_FLAG(tmp, FLAG_UNPAID))
01522 continue;
01523
01524
01525 if (QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && op->contr->mode&PU_NOT_CURSED)
01526 continue;
01527
01528
01529
01530 if (op->contr->mode&PU_FOOD)
01531 if (tmp->type == FOOD) {
01532 pick_up(op, tmp);
01533 if (0)
01534 fprintf(stderr, "FOOD\n");
01535 continue;
01536 }
01537 if (op->contr->mode&PU_DRINK)
01538 if (tmp->type == DRINK || (tmp->type == POISON && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED))) {
01539 pick_up(op, tmp);
01540 if (0)
01541 fprintf(stderr, "DRINK\n");
01542 continue;
01543 }
01544
01545 if (op->contr->mode&PU_FLESH)
01546 if (tmp->type == FLESH) {
01547 pick_up(op, tmp);
01548 if (0)
01549 fprintf(stderr, "FLESH\n");
01550 continue;
01551 }
01552 if (op->contr->mode&PU_POTION)
01553 if (tmp->type == POTION) {
01554 pick_up(op, tmp);
01555 if (0)
01556 fprintf(stderr, "POTION\n");
01557 continue;
01558 }
01559
01560
01561 if (op->contr->mode&PU_SPELLBOOK)
01562 if (tmp->type == SPELLBOOK) {
01563 pick_up(op, tmp);
01564 if (0)
01565 fprintf(stderr, "SPELLBOOK\n");
01566 continue;
01567 }
01568 if (op->contr->mode&PU_SKILLSCROLL)
01569 if (tmp->type == SKILLSCROLL) {
01570 pick_up(op, tmp);
01571 if (0)
01572 fprintf(stderr, "SKILLSCROLL\n");
01573 continue;
01574 }
01575 if (op->contr->mode&PU_READABLES)
01576 if (tmp->type == BOOK || tmp->type == SCROLL) {
01577 pick_up(op, tmp);
01578 if (0)
01579 fprintf(stderr, "READABLES\n");
01580 continue;
01581 }
01582
01583
01584 if (op->contr->mode&PU_MAGIC_DEVICE)
01585 if (tmp->type == WAND || tmp->type == ROD || tmp->type == HORN) {
01586 pick_up(op, tmp);
01587 if (0)
01588 fprintf(stderr, "MAGIC_DEVICE\n");
01589 continue;
01590 }
01591
01592
01593 if (op->contr->mode&PU_MAGICAL)
01594 if (QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)) {
01595 pick_up(op, tmp);
01596 if (0)
01597 fprintf(stderr, "MAGICAL\n");
01598 continue;
01599 }
01600
01601 if (op->contr->mode&PU_VALUABLES) {
01602 if (tmp->type == MONEY || tmp->type == GEM) {
01603 pick_up(op, tmp);
01604 if (0)
01605 fprintf(stderr, "MONEY/GEM\n");
01606 continue;
01607 }
01608 }
01609
01610
01611 if (op->contr->mode&PU_JEWELS)
01612 if (tmp->type == RING || tmp->type == AMULET) {
01613 pick_up(op, tmp);
01614 if (0)
01615 fprintf(stderr, "JEWELS\n");
01616 continue;
01617 }
01618
01619
01620 if (op->contr->mode&PU_BOW)
01621 if (tmp->type == BOW) {
01622 pick_up(op, tmp);
01623 if (0)
01624 fprintf(stderr, "BOW\n");
01625 continue;
01626 }
01627 if (op->contr->mode&PU_ARROW)
01628 if (tmp->type == ARROW) {
01629 pick_up(op, tmp);
01630 if (0)
01631 fprintf(stderr, "ARROW\n");
01632 continue;
01633 }
01634
01635
01636 if (op->contr->mode&PU_ARMOUR)
01637 if (tmp->type == ARMOUR) {
01638 pick_up(op, tmp);
01639 if (0)
01640 fprintf(stderr, "ARMOUR\n");
01641 continue;
01642 }
01643 if (op->contr->mode&PU_HELMET)
01644 if (tmp->type == HELMET) {
01645 pick_up(op, tmp);
01646 if (0)
01647 fprintf(stderr, "HELMET\n");
01648 continue;
01649 }
01650 if (op->contr->mode&PU_SHIELD)
01651 if (tmp->type == SHIELD) {
01652 pick_up(op, tmp);
01653 if (0)
01654 fprintf(stderr, "SHIELD\n");
01655 continue;
01656 }
01657 if (op->contr->mode&PU_BOOTS)
01658 if (tmp->type == BOOTS) {
01659 pick_up(op, tmp);
01660 if (0)
01661 fprintf(stderr, "BOOTS\n");
01662 continue;
01663 }
01664 if (op->contr->mode&PU_GLOVES)
01665 if (tmp->type == GLOVES) {
01666 pick_up(op, tmp);
01667 if (0)
01668 fprintf(stderr, "GLOVES\n");
01669 continue;
01670 }
01671 if (op->contr->mode&PU_CLOAK)
01672 if (tmp->type == CLOAK) {
01673 pick_up(op, tmp);
01674 if (0)
01675 fprintf(stderr, "GLOVES\n");
01676 continue;
01677 }
01678
01679
01680 if (op->contr->mode&PU_MISSILEWEAPON)
01681 if (tmp->type == WEAPON && QUERY_FLAG(tmp, FLAG_IS_THROWN)) {
01682 pick_up(op, tmp);
01683 if (0)
01684 fprintf(stderr, "MISSILEWEAPON\n");
01685 continue;
01686 }
01687
01688
01689 if (op->contr->mode&PU_ALLWEAPON) {
01690 if (tmp->type == WEAPON && tmp->name != NULL) {
01691 if (strstr(tmp->name, "table") == NULL
01692 && strstr(tmp->arch->name, "table") == NULL
01693 && strstr(tmp->name, "chair")
01694 && strstr(tmp->arch->name, "chair") == NULL) {
01695 pick_up(op, tmp);
01696 if (0)
01697 fprintf(stderr, "WEAPON\n");
01698 continue;
01699 }
01700 }
01701 if (tmp->type == WEAPON && tmp->name == NULL) {
01702 if (strstr(tmp->arch->name, "table") == NULL
01703 && strstr(tmp->arch->name, "chair") == NULL) {
01704 pick_up(op, tmp);
01705 if (0)
01706 fprintf(stderr, "WEAPON\n");
01707 continue;
01708 }
01709 }
01710 }
01711
01712
01713 if (op->contr->mode&PU_KEY)
01714 if (tmp->type == KEY || tmp->type == SPECIAL_KEY) {
01715 pick_up(op, tmp);
01716 if (0)
01717 fprintf(stderr, "KEY\n");
01718 continue;
01719 }
01720
01721
01722
01723 if (op->contr->mode&PU_RATIO) {
01724
01725
01726
01727
01728 wvratio = (op->contr->mode&PU_RATIO)*5;
01729 if ((query_cost(tmp, op, F_TRUE)*100/(tmp->weight*MAX(tmp->nrof, 1))) >= wvratio) {
01730 pick_up(op, tmp);
01731 continue;
01732 }
01733 }
01734 }
01735 }
01736 return !stop;
01737 }
01738
01751 static object *find_arrow(object *op, const char *type) {
01752 object *tmp = NULL;
01753
01754 for (op = op->inv; op; op = op->below)
01755 if (!tmp
01756 && op->type == CONTAINER
01757 && op->race == type
01758 && QUERY_FLAG(op, FLAG_APPLIED))
01759 tmp = find_arrow(op, type);
01760 else if (op->type == ARROW && op->race == type)
01761 return op;
01762 return tmp;
01763 }
01764
01782 static object *find_better_arrow(object *op, object *target, const char *type, int *better) {
01783 object *tmp = NULL, *arrow, *ntmp;
01784 int attacknum, attacktype, betterby = 0, i;
01785
01786 if (!type)
01787 return NULL;
01788
01789 for (arrow = op->inv; arrow; arrow = arrow->below) {
01790 if (arrow->type == CONTAINER
01791 && arrow->race == type
01792 && QUERY_FLAG(arrow, FLAG_APPLIED)) {
01793 i = 0;
01794 ntmp = find_better_arrow(arrow, target, type, &i);
01795 if (i > betterby) {
01796 tmp = ntmp;
01797 betterby = i;
01798 }
01799 } else if (arrow->type == ARROW && arrow->race == type) {
01800
01801 if (target->race != NULL
01802 && arrow->slaying != NULL
01803 && strstr(arrow->slaying, target->race)) {
01804 if (arrow->attacktype&AT_DEATH) {
01805 if (better)
01806 *better = 100;
01807 return arrow;
01808 } else {
01809 tmp = arrow;
01810 betterby = (arrow->magic+arrow->stats.dam)*2;
01811 }
01812 } else {
01813 for (attacknum = 0; attacknum < NROFATTACKS; attacknum++) {
01814 attacktype = 1<<attacknum;
01815 if ((arrow->attacktype&attacktype) && (target->arch->clone.resist[attacknum]) < 0)
01816 if (((arrow->magic+arrow->stats.dam)*(100-target->arch->clone.resist[attacknum])/100) > betterby) {
01817 tmp = arrow;
01818 betterby = (arrow->magic+arrow->stats.dam)*(100-target->arch->clone.resist[attacknum])/100;
01819 }
01820 }
01821 if ((2+arrow->magic+arrow->stats.dam) > betterby) {
01822 tmp = arrow;
01823 betterby = 2+arrow->magic+arrow->stats.dam;
01824 }
01825 if (arrow->title && (1+arrow->magic+arrow->stats.dam) > betterby) {
01826 tmp = arrow;
01827 betterby = 1+arrow->magic+arrow->stats.dam;
01828 }
01829 }
01830 }
01831 }
01832 if (tmp == NULL && arrow == NULL)
01833 return find_arrow(op, type);
01834
01835 if (better)
01836 *better = betterby;
01837 return tmp;
01838 }
01839
01852 static object *pick_arrow_target(object *op, const char *type, int dir) {
01853 object *tmp = NULL;
01854 mapstruct *m;
01855 int i, mflags, found, number;
01856 sint16 x, y;
01857
01858 if (op->map == NULL)
01859 return find_arrow(op, type);
01860
01861
01862 number = (die_roll(2, 40, op, PREFER_LOW)-2)/2;
01863 if (number > (op->stats.Dex+(op->chosen_skill ? op->chosen_skill->level : op->level)))
01864 return find_arrow(op, type);
01865
01866 m = op->map;
01867 x = op->x;
01868 y = op->y;
01869
01870
01871 for (i = 0, found = 0; i < 20; i++) {
01872 x += freearr_x[dir];
01873 y += freearr_y[dir];
01874 mflags = get_map_flags(m, &m, x, y, &x, &y);
01875 if (mflags&P_OUT_OF_MAP || mflags&P_BLOCKSVIEW) {
01876 tmp = NULL;
01877 break;
01878 } else if (GET_MAP_MOVE_BLOCK(m, x, y) == MOVE_FLY_LOW) {
01879
01880
01881
01882 tmp = NULL;
01883 break;
01884 }
01885 if (mflags&P_IS_ALIVE) {
01886 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above)
01887 if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
01888 found++;
01889 break;
01890 }
01891 if (found)
01892 break;
01893 }
01894 }
01895 if (tmp == NULL)
01896 return find_arrow(op, type);
01897
01898 if (tmp->head)
01899 tmp = tmp->head;
01900
01901 return find_better_arrow(op, tmp, type, NULL);
01902 }
01903
01922 int fire_bow(object *op, object *arrow, int dir, int wc_mod, sint16 sx, sint16 sy) {
01923 object *left, *bow;
01924 tag_t left_tag, tag;
01925 int bowspeed, mflags;
01926 mapstruct *m;
01927
01928 if (!dir) {
01929 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01930 "You can't shoot yourself!", NULL);
01931 return 0;
01932 }
01933 if (op->type == PLAYER)
01934 bow = op->contr->ranges[range_bow];
01935 else {
01936 for (bow = op->inv; bow; bow = bow->below)
01937
01938
01939
01940 if (bow->type == BOW)
01941 break;
01942
01943 if (!bow) {
01944 LOG(llevError, "Range: bow without activated bow (%s).\n", op->name);
01945 return 0;
01946 }
01947 }
01948 if (!bow->race || !bow->skill) {
01949 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01950 "Your %s is broken.",
01951 "Your %s is broken.",
01952 bow->name);
01953 return 0;
01954 }
01955
01956 bowspeed = bow->stats.sp+dex_bonus[op->stats.Dex];
01957
01958
01959 if (op->type == PLAYER && op->contr->bowtype == bow_bestarrow)
01960 bowspeed -= dex_bonus[op->stats.Dex]+5;
01961 if (bowspeed < 1)
01962 bowspeed = 1;
01963
01964 if (arrow == NULL) {
01965 if ((arrow = find_arrow(op, bow->race)) == NULL) {
01966 if (op->type == PLAYER)
01967 draw_ext_info_format(NDI_UNIQUE, 0, op,
01968 MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01969 "You have no %s left.",
01970 "You have no %s left.",
01971 bow->race);
01972
01973 else
01974 CLEAR_FLAG(op, FLAG_READY_BOW);
01975 return 0;
01976 }
01977 }
01978 mflags = get_map_flags(op->map, &m, sx, sy, &sx, &sy);
01979 if (mflags&P_OUT_OF_MAP) {
01980 return 0;
01981 }
01982 if (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW) {
01983 return 0;
01984 }
01985
01986
01987 if (arrow->nrof == 0) {
01988 remove_ob(arrow);
01989 free_object(arrow);
01990 return 0;
01991 }
01992
01993 left = arrow;
01994
01995 left_tag = left->count;
01996 arrow = get_split_ob(arrow, 1, NULL, 0);
01997 if (arrow == NULL) {
01998 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01999 "You have no %s left.",
02000 "You have no %s left.",
02001 bow->race);
02002 return 0;
02003 }
02004 set_owner(arrow, op);
02005 if (arrow->skill)
02006 free_string(arrow->skill);
02007 arrow->skill = add_refcount(bow->skill);
02008
02009 arrow->direction = dir;
02010 arrow->x = sx;
02011 arrow->y = sy;
02012
02013 if (op->type == PLAYER) {
02014 op->speed_left = 0.01-(float)FABS(op->speed)*100/bowspeed;
02015 fix_object(op);
02016 }
02017
02018 SET_ANIMATION(arrow, arrow->direction);
02019 arrow->stats.sp = arrow->stats.wc;
02020 arrow->stats.hp = arrow->stats.dam;
02021 arrow->stats.grace = arrow->attacktype;
02022 if (arrow->slaying != NULL)
02023 arrow->spellarg = strdup_local(arrow->slaying);
02024
02025
02026
02027
02028
02029 arrow->stats.dam += (QUERY_FLAG(bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str])
02030 +bow->stats.dam
02031 +bow->magic
02032 +arrow->magic;
02033
02034
02035 arrow->speed = (float)((QUERY_FLAG(bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str])+bow->magic+arrow->magic)/5.0
02036 +(float)bow->stats.dam/7.0;
02037
02038 if (arrow->speed < 1.0)
02039 arrow->speed = 1.0;
02040 update_ob_speed(arrow);
02041 arrow->speed_left = 0;
02042
02043 if (op->type == PLAYER) {
02044
02045 int mod = bow->magic
02046 +arrow->magic
02047 +dex_bonus[op->stats.Dex]
02048 +thaco_bonus[op->stats.Str]
02049 +arrow->stats.wc
02050 +bow->stats.wc
02051 -wc_mod;
02052 int plmod = (op->chosen_skill ? op->chosen_skill->level : op->level);
02053 if (plmod+mod > 140)
02054 plmod = 140-mod;
02055 else if (plmod+mod < -100)
02056 plmod = -100-mod;
02057 arrow->stats.wc = 20-(sint8)plmod-(sint8)mod;
02058
02059 arrow->level = op->chosen_skill ? op->chosen_skill->level : op->level;
02060 } else {
02061 arrow->stats.wc = op->stats.wc
02062 -bow->magic
02063 -arrow->magic
02064 -arrow->stats.wc
02065 +wc_mod;
02066
02067 arrow->level = op->level;
02068 }
02069 if (arrow->attacktype == AT_PHYSICAL)
02070 arrow->attacktype |= bow->attacktype;
02071 if (bow->slaying != NULL)
02072 arrow->slaying = add_string(bow->slaying);
02073
02074 arrow->map = m;
02075
02076 arrow->move_type = MOVE_FLY_LOW;
02077 arrow->move_on = MOVE_FLY_LOW|MOVE_WALK;
02078
02079 tag = arrow->count;
02080 insert_ob_in_map(arrow, m, op, 0);
02081
02082 if (!was_destroyed(arrow, tag)) {
02083 play_sound_map(SOUND_TYPE_ITEM, arrow, arrow->direction, "fire");
02084 ob_process(arrow);
02085 }
02086
02087 return 1;
02088 }
02089
02106 static int player_fire_bow(object *op, int dir) {
02107 int ret = 0, wcmod = 0;
02108
02109 if (op->contr->bowtype == bow_bestarrow) {
02110 ret = fire_bow(op, pick_arrow_target(op, op->contr->ranges[range_bow]->race, dir), dir, 0, op->x, op->y);
02111 } else if (op->contr->bowtype >= bow_n && op->contr->bowtype <= bow_nw) {
02112 if (!similar_direction(dir, op->contr->bowtype-bow_n+1))
02113 wcmod = -1;
02114 ret = fire_bow(op, NULL, op->contr->bowtype-bow_n+1, wcmod, op->x, op->y);
02115 } else if (op->contr->bowtype == bow_threewide) {
02116 ret = fire_bow(op, NULL, dir, 0, op->x, op->y);
02117 ret |= fire_bow(op, NULL, dir, -5, op->x+freearr_x[absdir(dir+2)], op->y+freearr_y[absdir(dir+2)]);
02118 ret |= fire_bow(op, NULL, dir, -5, op->x+freearr_x[absdir(dir-2)], op->y+freearr_y[absdir(dir-2)]);
02119 } else if (op->contr->bowtype == bow_spreadshot) {
02120 ret |= fire_bow(op, NULL, dir, 0, op->x, op->y);
02121 ret |= fire_bow(op, NULL, absdir(dir-1), -5, op->x, op->y);
02122 ret |= fire_bow(op, NULL, absdir(dir+1), -5, op->x, op->y);
02123
02124 } else {
02125
02126 ret = fire_bow(op, NULL, dir, 0, op->x, op->y);
02127 }
02128 return ret;
02129 }
02130
02143 static void fire_misc_object(object *op, int dir) {
02144 object *item;
02145 char name[MAX_BUF];
02146
02147 if (!op->contr->ranges[range_misc]) {
02148 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02149 "You have no range item readied.", NULL);
02150 return;
02151 }
02152
02153 item = op->contr->ranges[range_misc];
02154 if (!item->inv) {
02155 LOG(llevError, "Object %s lacks a spell\n", item->name);
02156 return;
02157 }
02158 if (item->type == WAND) {
02159 if (item->stats.food <= 0) {
02160 play_sound_player_only(op->contr, SOUND_TYPE_ITEM, item, 0, "poof");
02161 query_base_name(item, 0, name, MAX_BUF);
02162 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
02163 "The %s goes poof.",
02164 "The %s goes poof.",
02165 name);
02166 return;
02167 }
02168 } else if (item->type == ROD || item->type == HORN) {
02169 if (item->stats.hp < SP_level_spellpoint_cost(item, item->inv, SPELL_HIGHEST)) {
02170 play_sound_player_only(op->contr, SOUND_TYPE_ITEM, item, 0, "poof");
02171 query_base_name(item, 0, name, MAX_BUF);
02172 if (item->type == ROD)
02173 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
02174 "The %s whines for a while, but nothing happens.",
02175 "The %s whines for a while, but nothing happens.",
02176 name);
02177 else
02178 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
02179 "The %s needs more time to charge.",
02180 "The %s needs more time to charge.",
02181 name);
02182 return;
02183 }
02184 }
02185
02186 if (cast_spell(op, item, dir, item->inv, NULL)) {
02187 SET_FLAG(op, FLAG_BEEN_APPLIED);
02188 if (item->type == WAND) {
02189 drain_wand_charge(item);
02190 } else if (item->type == ROD || item->type == HORN) {
02191 drain_rod_charge(item);
02192 }
02193 }
02194 }
02195
02204 void fire(object *op, int dir) {
02205 int spellcost = 0;
02206
02207
02208 if (action_makes_visible(op))
02209 make_visible(op);
02210
02211 switch (op->contr->shoottype) {
02212 case range_none:
02213 return;
02214
02215 case range_bow:
02216 player_fire_bow(op, dir);
02217 return;
02218
02219 case range_magic:
02220
02221 spellcost = (cast_spell(op, op, dir, op->contr->ranges[range_magic], op->contr->spellparam[0] ? op->contr->spellparam : NULL));
02222 return;
02223
02224 case range_misc:
02225 fire_misc_object(op, dir);
02226 return;
02227
02228 case range_golem:
02229 if (op->contr->ranges[range_golem] == NULL
02230 || op->contr->golem_count != op->contr->ranges[range_golem]->count) {
02231 op->contr->ranges[range_golem] = NULL;
02232 op->contr->shoottype = range_none;
02233 op->contr->golem_count = 0;
02234 } else
02235 control_golem(op->contr->ranges[range_golem], dir);
02236 return;
02237
02238 case range_skill:
02239 if (!op->chosen_skill) {
02240 if (op->type == PLAYER)
02241 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02242 "You have no applicable skill to use.", NULL);
02243 return;
02244 }
02245 (void)do_skill(op, op, op->chosen_skill, dir, NULL);
02246 return;
02247
02248 case range_builder:
02249 apply_map_builder(op, dir);
02250 return;
02251
02252 default:
02253 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02254 "Illegal shoot type.", NULL);
02255 return;
02256 }
02257 }
02258
02278 object *find_key(object *pl, object *container, object *door) {
02279 object *tmp, *key;
02280
02281
02282 if (container->inv == NULL)
02283 return NULL;
02284
02285
02286 for (tmp = container->inv; tmp != NULL; tmp = tmp->below) {
02287 if (door->type == DOOR && tmp->type == KEY)
02288 break;
02289
02290
02291
02292 if (tmp->slaying
02293 && tmp->type == SPECIAL_KEY
02294 && tmp->slaying == door->slaying)
02295 break;
02296 }
02297
02298
02299
02300
02301
02302 if (!tmp) {
02303 for (tmp = container->inv; tmp != NULL; tmp = tmp->below) {
02304
02305 if (tmp->type == CONTAINER && tmp->inv) {
02306 if ((key = find_key(pl, tmp, door)) != NULL)
02307 return key;
02308 }
02309 }
02310 if (!tmp)
02311 return NULL;
02312 }
02313
02314
02315
02316 if (pl != container) {
02317
02318 if (!pl->contr)
02319 return NULL;
02320
02321
02322
02323
02324
02325
02326
02327
02328
02329
02330
02331
02332
02333 if (pl->contr->usekeys == key_inventory
02334 || !QUERY_FLAG(container, FLAG_APPLIED)
02335 || (pl->contr->usekeys == keyrings && (!container->race || strcmp(container->race, "keys")))) {
02336 char name_tmp[MAX_BUF], name_cont[MAX_BUF];
02337
02338 query_name(tmp, name_tmp, MAX_BUF);
02339 query_name(container, name_cont, MAX_BUF);
02340 draw_ext_info_format(NDI_UNIQUE|NDI_BROWN, 0, pl,
02341 MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO,
02342 "The %s in your %s vibrates as you approach the door",
02343 "The %s in your %s vibrates as you approach the door",
02344 name_tmp, name_cont);
02345 return NULL;
02346 }
02347 }
02348 return tmp;
02349 }
02350
02361 static int player_attack_door(object *op, object *door) {
02362
02363
02364
02365
02366
02367 object *key = find_key(op, op, door);
02368
02369
02370 if (key) {
02371 object *container = key->env;
02372
02373 play_sound_map(SOUND_TYPE_GROUND, door, 0, "open");
02374 if (action_makes_visible(op))
02375 make_visible(op);
02376 if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP))
02377 spring_trap(door->inv, op);
02378 if (door->type == DOOR) {
02379 hit_player(door, 9998, op, AT_PHYSICAL, 1);
02380 } else if (door->type == LOCKED_DOOR) {
02381 char name[HUGE_BUF];
02382
02383 query_short_name(key, name, HUGE_BUF);
02384 draw_ext_info_format(NDI_UNIQUE, NDI_BROWN, op,
02385 MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
02386 "You open the door with the %s",
02387 "You open the door with the %s",
02388 name);
02389 remove_locked_door(door);
02390 }
02391
02392 decrease_ob(key);
02393
02394 if (container != op)
02395 esrv_update_item(UPD_WEIGHT, op, container);
02396 return 1;
02397 } else if (door->type == LOCKED_DOOR) {
02398
02399 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_NOKEY,
02400 door->msg, door->msg);
02401 return 1;
02402 }
02403 return 0;
02404 }
02405
02419 void move_player_attack(object *op, int dir) {
02420 object *tmp, *mon, *tpl, *mon_owner;
02421 sint16 nx, ny;
02422 int on_battleground;
02423 mapstruct *m;
02424
02425 if (op->contr->transport)
02426 tpl = op->contr->transport;
02427 else
02428 tpl = op;
02429 nx = freearr_x[dir]+tpl->x;
02430 ny = freearr_y[dir]+tpl->y;
02431
02432 on_battleground = op_on_battleground(tpl, NULL, NULL, NULL);
02433
02434
02435
02436
02437
02438
02439
02440
02441
02442
02443 if ((op->contr->braced || !move_ob(tpl, dir, tpl)) && !out_of_map(tpl->map, nx, ny)) {
02444 if (OUT_OF_REAL_MAP(tpl->map, nx, ny)) {
02445 m = get_map_from_coord(tpl->map, &nx, &ny);
02446 if (!m)
02447 return;
02448 } else
02449 m = tpl->map;
02450
02451 if ((tmp = GET_MAP_OB(m, nx, ny)) == NULL) {
02452
02453 return;
02454 }
02455
02456 mon = NULL;
02457
02458
02459
02460
02461
02462 while (tmp != NULL) {
02463 if (tmp == op) {
02464 tmp = tmp->above;
02465 continue;
02466 }
02467 if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
02468 mon = tmp;
02469
02470
02471
02472
02473 if (QUERY_FLAG(tmp, FLAG_MONSTER))
02474 break;
02475 }
02476 if (tmp->type == LOCKED_DOOR || QUERY_FLAG(tmp, FLAG_CAN_ROLL))
02477 mon = tmp;
02478 tmp = tmp->above;
02479 }
02480
02481 if (mon == NULL)
02482 return;
02483
02484 if (mon->head != NULL)
02485 mon = mon->head;
02486
02487 if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR))
02488 if (player_attack_door(op, mon))
02489 return;
02490
02491
02492
02493
02494
02495
02496
02497
02498
02499
02500
02501
02502
02503 mon_owner = get_owner(mon);
02504 if ((op->type == PLAYER)
02505 && (mon_owner == op || (mon_owner != NULL && mon_owner->type == PLAYER && mon_owner->contr->party != NULL && mon_owner->contr->party == op->contr->party))
02506 && (QUERY_FLAG(mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG(mon, FLAG_FRIENDLY))) {
02507
02508 if (op->contr->braced)
02509 return;
02510 play_sound_map(SOUND_TYPE_LIVING, mon, dir, "push");
02511 (void)push_ob(mon, dir, op);
02512 if (op->contr->tmp_invis || op->hide)
02513 make_visible(op);
02514 return;
02515 }
02516
02517
02518
02519
02520
02521
02522 if ((mon->type == PLAYER || mon->enemy != op)
02523 && (mon->type == PLAYER || QUERY_FLAG(mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG(mon, FLAG_FRIENDLY))
02524 && (op->contr->peaceful && !on_battleground)) {
02525 if (!op->contr->braced) {
02526 play_sound_map(SOUND_TYPE_LIVING, mon, dir, "push");
02527 (void)push_ob(mon, dir, op);
02528 } else {
02529 draw_ext_info(0, 0, op, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_NOATTACK,
02530 "You withhold your attack", NULL);
02531 }
02532 if (op->contr->tmp_invis || op->hide)
02533 make_visible(op);
02534 }
02535
02536
02537
02538
02539 else if (QUERY_FLAG(mon, FLAG_CAN_ROLL) && (!op->contr->braced)) {
02540 recursive_roll(mon, dir, op);
02541 if (action_makes_visible(op))
02542 make_visible(op);
02543
02544
02545
02546
02547
02548
02549
02550
02551 } else if ((mon->stats.hp >= 0)
02552 && QUERY_FLAG(mon, FLAG_ALIVE)
02553 && ((mon->type != PLAYER || op->contr->party == NULL || op->contr->party != mon->contr->party))) {
02554
02555
02556
02557
02558
02559 if (!op->contr->has_hit) {
02560 op->speed_left += op->speed / op->contr->weapon_sp;
02561
02562 op->contr->has_hit = 1;
02563 }
02564
02565 skill_attack(mon, op, 0, NULL, NULL);
02566
02567
02568
02569
02570
02571
02572 if (mon->type == PLAYER
02573 && mon->stats.hp >= 0
02574 && !mon->contr->has_hit
02575 && !on_battleground
02576 && !QUERY_FLAG(mon, FLAG_WIZ)) {
02577 short luck = mon->stats.luck;
02578 mon->contr->has_hit = 1;
02579 skill_attack(op, mon, 0, NULL, NULL);
02580 mon->stats.luck = luck;
02581 }
02582 if (action_makes_visible(op))
02583 make_visible(op);
02584 }
02585 }
02586 }
02587
02594 static void update_transport_block(object *transport, int dir) {
02595 object *part;
02596 int sx, sy, x, y;
02597
02598 get_multi_size(transport, &sx, &sy, NULL, NULL);
02599 assert(sx == sy);
02600
02601 if (dir == 1 || dir == 5) {
02602 part = transport;
02603 for (y = 0; y <= sy; y++) {
02604 for (x = 0; x < sx; x++) {
02605 part->move_type = transport->move_type;
02606 part = part->more;
02607 }
02608 part->move_type = 0;
02609 part = part->more;
02610 }
02611 } else if (dir == 3 || dir == 7) {
02612 part = transport;
02613 for (y = 0; y < sy; y++) {
02614 for (x = 0; x <= sx; x++) {
02615 part->move_type = transport->move_type;
02616 part = part->more;
02617 }
02618 }
02619 while (part) {
02620 part->move_type = 0;
02621 part = part->more;
02622 }
02623 } else {
02624 for (part = transport; part; part = part->more) {
02625 part->move_type = transport->move_type;
02626 }
02627 }
02628 }
02629
02639 static int turn_one_transport(object *transport, object *captain, int dir) {
02640 int x, y, scroll_dir = 0;
02641
02642 assert(transport->type == TRANSPORT);
02643
02644 x = transport->x;
02645 y = transport->y;
02646
02647 if (transport->direction == 1 && dir == 8) {
02648 x--;
02649 } else if (transport->direction == 2 && dir == 3) {
02650 y++;
02651 } else if (transport->direction == 3 && dir == 2) {
02652 y--;
02653 } else if (transport->direction == 5 && dir == 6) {
02654 x--;
02655 } else if (transport->direction == 6 && dir == 5) {
02656 x++;
02657 } else if (transport->direction == 7 && dir == 8) {
02658 y--;
02659 } else if (transport->direction == 8 && dir == 7) {
02660 y++;
02661 } else if (transport->direction == 8 && dir == 1) {
02662 x++;
02663 }
02664
02665 update_transport_block(transport, dir);
02666 remove_ob(transport);
02667 if (ob_blocked(transport, transport->map, x, y)) {
02668 update_transport_block(transport, transport->direction);
02669 insert_ob_in_map(transport, transport->map, NULL, 0);
02670 return 2;
02671 }
02672
02673 if (x != transport->x || y != transport->y) {
02674 object *pl;
02675
02676
02677
02678 for (pl = transport->inv; pl; pl = pl->below) {
02679 if (pl->type == PLAYER) {
02680 pl->contr->do_los = 1;
02681 pl->map = transport->map;
02682 pl->x = x;
02683 pl->y = y;
02684 esrv_map_scroll(&pl->contr->socket, freearr_x[scroll_dir], freearr_y[scroll_dir]);
02685 pl->contr->socket.update_look = 1;
02686 pl->contr->socket.look_position = 0;
02687 }
02688 }
02689 }
02690
02691 insert_ob_in_map_at(transport, transport->map, NULL, 0, x, y);
02692 transport->direction = dir;
02693 transport->facing = dir;
02694 animate_object(transport, dir);
02695 captain->direction = dir;
02696 return 1;
02697 }
02698
02711 static int turn_transport(object *transport, object *captain, int dir) {
02712 assert(transport->type == TRANSPORT);
02713
02714 if (get_ob_key_value(transport, "turnable_transport") == NULL) {
02715 transport->direction = dir;
02716 transport->facing = dir;
02717 animate_object(transport, dir);
02718 captain->direction = dir;
02719 return 0;
02720 }
02721
02722 if (transport->direction == dir)
02723 return 0;
02724
02725 if (absdir(transport->direction-dir) > 2)
02726 return turn_one_transport(transport, captain, absdir(transport->direction+1));
02727 else
02728 return turn_one_transport(transport, captain, absdir(transport->direction-1));
02729 }
02730
02741 int move_player(object *op, int dir) {
02742 int pick;
02743 object *transport = op->contr->transport;
02744
02745 if (!transport && (op->map == NULL || op->map->in_memory != MAP_IN_MEMORY))
02746 return 0;
02747
02748
02749 if ((dir < 0) || (dir >= 9)) {
02750 LOG(llevError, "move_player: invalid direction %d\n", dir);
02751 return 0;
02752 }
02753
02754
02755 if (QUERY_FLAG(op, FLAG_CONFUSED) && dir)
02756 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2);
02757
02758 op->facing = dir;
02759
02760 if (!transport && op->hide)
02761 do_hidden_move(op);
02762
02763 if (transport) {
02764 int turn;
02765
02766
02767
02768
02769 if (transport->contr != op->contr)
02770 return 0;
02771
02772
02773
02774
02775 if (transport->speed_left < 0.0) {
02776 return 0;
02777 }
02778
02779
02780
02781 transport->speed_left -= 1.0;
02782 if (op->speed_left < 0.0)
02783 op->speed_left = -0.01;
02784
02785 turn = turn_transport(transport, op, dir);
02786 if (turn != 0)
02787 return 0;
02788 } else {
02789
02790
02791 op->state++;
02792 animate_object(op, op->facing);
02793 }
02794
02795 if (op->contr->fire_on) {
02796 fire(op, dir);
02797 } else
02798 move_player_attack(op, dir);
02799
02800 pick = check_pick(op);
02801
02802
02803
02804
02805
02806 if (op->contr->fire_on || (op->contr->run_on && pick != 0)) {
02807 op->direction = dir;
02808 } else {
02809 op->direction = 0;
02810 }
02811 return 0;
02812 }
02813
02826 int handle_newcs_player(object *op) {
02827 if (op->contr->hidden) {
02828 op->invisible = 1000;
02829
02830
02831
02832
02833 if (pticks&2)
02834 op->invisible--;
02835 } else if (op->invisible && !(QUERY_FLAG(op, FLAG_MAKE_INVIS))) {
02836 op->invisible--;
02837 if (!op->invisible) {
02838 make_visible(op);
02839 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_END,
02840 "Your invisibility spell runs out.", NULL);
02841 }
02842 }
02843
02844 if (QUERY_FLAG(op, FLAG_SCARED)) {
02845 flee_player(op);
02846
02847 if (QUERY_FLAG(op, FLAG_SCARED)) {
02848 op->speed_left--;
02849 return 0;
02850 }
02851 }
02852
02853
02854
02855
02856
02857
02858 if (op->contr->ranges[range_golem]
02859 && ((op->contr->golem_count != op->contr->ranges[range_golem]->count) || QUERY_FLAG(op->contr->ranges[range_golem], FLAG_REMOVED))) {
02860 op->contr->ranges[range_golem] = NULL;
02861 op->contr->golem_count = 0;
02862 }
02863
02864
02865
02866
02867
02868 handle_client(&op->contr->socket, op->contr);
02869 if (op->speed_left < 0)
02870 return 0;
02871
02872 if (op->direction && (op->contr->run_on || op->contr->fire_on)) {
02873
02874 op->speed_left--;
02875
02876
02877
02878
02879
02880 move_player(op, op->direction);
02881 if (op->speed_left > 0)
02882 return 1;
02883 else
02884 return 0;
02885 }
02886 return 0;
02887 }
02888
02899 static int save_life(object *op) {
02900 object *tmp;
02901
02902 if (!QUERY_FLAG(op, FLAG_LIFESAVE))
02903 return 0;
02904
02905 for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
02906 if (QUERY_FLAG(tmp, FLAG_APPLIED) && QUERY_FLAG(tmp, FLAG_LIFESAVE)) {
02907 char name[MAX_BUF];
02908
02909 query_name(tmp, name, MAX_BUF);
02910 play_sound_map(SOUND_TYPE_ITEM, tmp, 0, "evaporate");
02911 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
02912 "Your %s vibrates violently, then evaporates.",
02913 "Your %s vibrates violently, then evaporates.",
02914 name);
02915 remove_ob(tmp);
02916 free_object(tmp);
02917 CLEAR_FLAG(op, FLAG_LIFESAVE);
02918 if (op->stats.hp < 0)
02919 op->stats.hp = op->stats.maxhp;
02920 if (op->stats.food < 0)
02921 op->stats.food = 999;
02922 fix_object(op);
02923 return 1;
02924 }
02925 LOG(llevError, "Error: LIFESAVE set without applied object.\n");
02926 CLEAR_FLAG(op, FLAG_LIFESAVE);
02927 enter_player_savebed(op);
02928 return 0;
02929 }
02930
02943 void remove_unpaid_objects(object *op, object *env, int free_items) {
02944 object *next;
02945
02946 while (op) {
02947 next = op->below;
02948
02949
02950 if (QUERY_FLAG(op, FLAG_UNPAID)) {
02951 remove_ob(op);
02952 if (free_items)
02953 free_object(op);
02954 else {
02955 op->x = env->x;
02956 op->y = env->y;
02957 insert_ob_in_map(op, env->map, NULL, 0);
02958 }
02959 } else if (op->inv)
02960 remove_unpaid_objects(op->inv, env, free_items);
02961 op = next;
02962 }
02963 }
02964
02982 static const char *gravestone_text(object *op, char *buf2, int len) {
02983 char buf[MAX_BUF];
02984 time_t now = time(NULL);
02985
02986 strncpy(buf2, " R.I.P.\n\n", len);
02987 if (op->type == PLAYER)
02988 snprintf(buf, sizeof(buf), "%s the %s\n", op->name, op->contr->title);
02989 else
02990 snprintf(buf, sizeof(buf), "%s\n", op->name);
02991 strncat(buf2, " ", 20-strlen(buf)/2);
02992 strncat(buf2, buf, len-strlen(buf2)-1);
02993 if (op->type == PLAYER)
02994 snprintf(buf, sizeof(buf), "who was in level %d when killed\n", op->level);
02995 else
02996 snprintf(buf, sizeof(buf), "who was in level %d when died.\n\n", op->level);
02997 strncat(buf2, " ", 20-strlen(buf)/2);
02998 strncat(buf2, buf, len-strlen(buf2)-1);
02999 if (op->type == PLAYER) {
03000 snprintf(buf, sizeof(buf), "by %s.\n\n", op->contr->killer);
03001 strncat(buf2, " ", 21-strlen(buf)/2);
03002 strncat(buf2, buf, len-strlen(buf2)-1);
03003 }
03004 strftime(buf, MAX_BUF, "%b %d %Y\n", localtime(&now));
03005 strncat(buf2, " ", 20-strlen(buf)/2);
03006 strncat(buf2, buf, len-strlen(buf2)-1);
03007 return buf2;
03008 }
03009
03017 void do_some_living(object *op) {
03018 int last_food = op->stats.food;
03019 int gen_hp, gen_sp, gen_grace;
03020 int over_hp, over_sp, over_grace;
03021 int i;
03022 int rate_hp = 1200;
03023 int rate_sp = 2500;
03024 int rate_grace = 2000;
03025 const int max_hp = 1;
03026 const int max_sp = 1;
03027 const int max_grace = 1;
03028
03029 if (op->contr->outputs_sync) {
03030 for (i = 0; i < NUM_OUTPUT_BUFS; i++)
03031 if (op->contr->outputs[i].buf != NULL
03032 && (op->contr->outputs[i].first_update+op->contr->outputs_sync) < pticks)
03033 flush_output_element(op, &op->contr->outputs[i]);
03034 }
03035
03036 if (op->contr->state == ST_PLAYING) {
03037
03038
03039 if (op->contr->gen_hp >= 0)
03040 gen_hp = (op->contr->gen_hp+1)*op->stats.maxhp;
03041 else {
03042 gen_hp = op->stats.maxhp;
03043 rate_hp -= rate_hp/2*op->contr->gen_hp;
03044 }
03045 if (op->contr->gen_sp >= 0)
03046 gen_sp = (op->contr->gen_sp+1)*op->stats.maxsp;
03047 else {
03048 gen_sp = op->stats.maxsp;
03049 rate_sp -= rate_sp/2*op->contr->gen_sp;
03050 }
03051 if (op->contr->gen_grace >= 0)
03052 gen_grace = (op->contr->gen_grace+1)*op->stats.maxgrace;
03053 else {
03054 gen_grace = op->stats.maxgrace;
03055 rate_grace -= rate_grace/2*op->contr->gen_grace;
03056 }
03057
03058
03059 if (op->contr->ranges[range_golem] == NULL && --op->last_sp < 0) {
03060 gen_sp = gen_sp*10/MAX(op->contr->gen_sp_armour, 10);
03061 if (op->stats.sp < op->stats.maxsp) {
03062 op->stats.sp++;
03063
03064 if (!QUERY_FLAG(op, FLAG_WIZ)) {
03065 op->stats.food--;
03066 if (op->contr->digestion < 0)
03067 op->stats.food += op->contr->digestion;
03068 else if (op->contr->digestion > 0
03069 && random_roll(0, op->contr->digestion, op, PREFER_HIGH))
03070 op->stats.food = last_food;
03071 }
03072 }
03073 if (max_sp > 1) {
03074 over_sp = (gen_sp+10)/rate_sp;
03075 if (over_sp > 0) {
03076 if (op->stats.sp < op->stats.maxsp) {
03077 op->stats.sp += MIN(over_sp, max_sp);
03078 if (random_roll(0, rate_sp-1, op, PREFER_LOW) > ((gen_sp+10)%rate_sp))
03079 op->stats.sp--;
03080 if (op->stats.sp > op->stats.maxsp)
03081 op->stats.sp = op->stats.maxsp;
03082 }
03083 op->last_sp = 0;
03084 } else {
03085 op->last_sp = rate_sp/(MAX(gen_sp, 20)+10);
03086 }
03087 } else {
03088 op->last_sp = rate_sp/(MAX(gen_sp, 20)+10);
03089 }
03090 }
03091
03092
03093
03094 if (--op->last_grace < 0) {
03095 if (op->stats.grace < op->stats.maxgrace/2)
03096 op->stats.grace++;
03097 if (max_grace > 1) {
03098 over_grace = (MAX(gen_grace, 20)+10)/rate_grace;
03099 if (over_grace > 0) {
03100 op->stats.sp += over_grace+(random_roll(0, rate_grace-1, op, PREFER_HIGH) > ((MAX(gen_grace, 20)+10)%rate_grace)) ? -1 : 0;
03101 op->last_grace = 0;
03102 } else {
03103 op->last_grace = rate_grace/(MAX(gen_grace, 20)+10);
03104 }
03105 } else {
03106 op->last_grace = rate_grace/(MAX(gen_grace, 20)+10);
03107 }
03108
03109 }
03110
03111
03112 if (--op->last_heal < 0 && !is_wraith_pl(op)) {
03113 if (op->stats.hp < op->stats.maxhp) {
03114 op->stats.hp++;
03115
03116 if (!QUERY_FLAG(op, FLAG_WIZ)) {
03117 op->stats.food--;
03118 if (op->contr->digestion < 0)
03119 op->stats.food += op->contr->digestion;
03120 else if (op->contr->digestion > 0
03121 && random_roll(0, op->contr->digestion, op, PREFER_HIGH))
03122 op->stats.food = last_food;
03123 }
03124 }
03125 if (max_hp > 1 && !is_wraith_pl(op)) {
03126 over_hp = (MAX(gen_hp, 20)+10)/rate_hp;
03127 if (over_hp > 0) {
03128 op->stats.sp += over_hp+(RANDOM()%rate_hp > ((MAX(gen_hp, 20)+10)%rate_hp)) ? -1 : 0;
03129 op->last_heal = 0;
03130 } else {
03131 op->last_heal = rate_hp/(MAX(gen_hp, 20)+10);
03132 }
03133 } else {
03134 op->last_heal = rate_hp/(MAX(gen_hp, 20)+10);
03135 }
03136 }
03137
03138
03139 if (--op->last_eat < 0) {
03140 int bonus = MAX(op->contr->digestion, 0);
03141 int penalty = MAX(-op->contr->digestion, 0);
03142 if (op->contr->gen_hp > 0)
03143 op->last_eat = 25*(1+bonus)/(op->contr->gen_hp+penalty+1);
03144 else
03145 op->last_eat = 25*(1+bonus)/(penalty+1);
03146
03147 if (!QUERY_FLAG(op, FLAG_WIZ))
03148 op->stats.food--;
03149 }
03150 }
03151
03152 if (op->contr->state == ST_PLAYING && op->stats.food < 0 && op->stats.hp >= 0) {
03153 if (is_wraith_pl(op))
03154 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE, "You feel a hunger for living flesh.", NULL);
03155 else {
03156 object *tmp, *flesh = NULL;
03157
03158 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
03159 if (!QUERY_FLAG(tmp, FLAG_UNPAID)) {
03160 if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON) {
03161 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
03162 "You blindly grab for a bite of food.", NULL);
03163 manual_apply(op, tmp, 0);
03164 if (op->stats.food >= 0 || op->stats.hp < 0)
03165 break;
03166 } else if (tmp->type == FLESH)
03167 flesh = tmp;
03168 }
03169 }
03170
03171
03172
03173 if (op->stats.food < 0 && op->stats.hp >= 0 && flesh) {
03174 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
03175 "You blindly grab for a bite of food.", NULL);
03176 manual_apply(op, flesh, 0);
03177 }
03178 }
03179 }
03180
03181 while (op->stats.food < 0 && op->stats.hp > 0)
03182 op->stats.food++,
03183 op->stats.hp--;
03184
03185 if (!op->contr->state && !QUERY_FLAG(op, FLAG_WIZ) && (op->stats.hp < 0 || op->stats.food < 0))
03186 kill_player(op);
03187 }
03188
03195 static void loot_object(object *op) {
03196 object *tmp, *tmp2, *next;
03197
03198 if (op->container) {
03199 apply_container(op, op->container);
03200 }
03201
03202 for (tmp = op->inv; tmp != NULL; tmp = next) {
03203 next = tmp->below;
03204 if (tmp->type == EXPERIENCE || tmp->invisible)
03205 continue;
03206 remove_ob(tmp);
03207 tmp->x = op->x,
03208 tmp->y = op->y;
03209 if (tmp->type == CONTAINER) {
03210 loot_object(tmp);
03211 }
03212 if (!QUERY_FLAG(tmp, FLAG_UNIQUE)
03213 && (QUERY_FLAG(tmp, FLAG_STARTEQUIP) || QUERY_FLAG(tmp, FLAG_NO_DROP) || !(RANDOM()%3))) {
03214 if (tmp->nrof > 1) {
03215 tmp2 = get_split_ob(tmp, 1+RANDOM()%(tmp->nrof-1), NULL, 0);
03216 free_object(tmp2);
03217 insert_ob_in_map(tmp, op->map, NULL, 0);
03218 } else
03219 free_object(tmp);
03220 } else
03221 insert_ob_in_map(tmp, op->map, NULL, 0);
03222 }
03223 }
03224
03238 void kill_player(object *op) {
03239 char buf[MAX_BUF];
03240 int x, y, i;
03241 mapstruct *map;
03242 int z;
03243 int num_stats_lose;
03244 int lost_a_stat;
03245 int lose_this_stat;
03246 int this_stat;
03247 int will_kill_again;
03248 archetype *at;
03249 object *tmp;
03250 archetype *trophy;
03251
03252 if (save_life(op))
03253 return;
03254
03255
03256
03257
03258
03259 if (op_on_battleground(op, &x, &y, &trophy)) {
03260 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03261 "You have been defeated in combat!\n"
03262 "Local medics have saved your life...",
03263 NULL);
03264
03265
03266 at = find_archetype("poisoning");
03267 tmp = present_arch_in_ob(at, op);
03268 if (tmp) {
03269 remove_ob(tmp);
03270 free_object(tmp);
03271 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03272 "Your body feels cleansed", NULL);
03273 }
03274
03275 at = find_archetype("confusion");
03276 tmp = present_arch_in_ob(at, op);
03277 if (tmp) {
03278 remove_ob(tmp);
03279 free_object(tmp);
03280 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03281 "Your mind feels clearer", NULL);
03282 }
03283
03284 cure_disease(op, NULL);
03285 op->stats.hp = op->stats.maxhp;
03286 if (op->stats.food <= 0)
03287 op->stats.food = 999;
03288
03289
03290 tmp = arch_to_object(trophy);
03291 if (tmp != NULL) {
03292 snprintf(buf, sizeof(buf), "%s's %s", op->name, tmp->name);
03293 tmp->name = add_string(buf);
03294 if (tmp->type == FLESH)
03295 snprintf(buf, sizeof(buf), " This %s has been cut off %s\n"
03296 " the %s, when he was defeated at\n"
03297 " level %d by %s.\n",
03298 tmp->name, op->name, op->contr->title,
03299 (int)(op->level), op->contr->killer);
03300 else
03301 snprintf(buf, sizeof(buf), " This %s has been taken from %s\n"
03302 " the %s, when he was defeated at\n"
03303 " level %d by %s.\n",
03304 tmp->name, op->name, op->contr->title,
03305 (int)(op->level), op->contr->killer);
03306 tmp->msg = add_string(buf);
03307 tmp->type = 0;
03308 tmp->value = 0;
03309 tmp->material = 0;
03310 tmp->materialname = NULL;
03311 tmp->x = op->x,
03312 tmp->y = op->y;
03313 insert_ob_in_map(tmp, op->map, op, 0);
03314 }
03315
03316
03317 transfer_ob(op, x, y, 0, NULL);
03318 op->contr->braced = 0;
03319 return;
03320 }
03321
03322
03323 if (execute_event(op, EVENT_DEATH, NULL, NULL, NULL, SCRIPT_FIX_ALL) != 0)
03324 return;
03325
03326
03327 execute_global_event(EVENT_PLAYER_DEATH, op);
03328 if (op->stats.food < 0) {
03329 if (op->contr->explore) {
03330 draw_ext_info(NDI_UNIQUE, 0, op,
03331 MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03332 "You would have starved, but you are "
03333 "in explore mode, so...", NULL);
03334 op->stats.food = 999;
03335 return;
03336 }
03337 snprintf(buf, sizeof(buf), "%s starved to death.", op->name);
03338 strcpy(op->contr->killer, "starvation");
03339 } else {
03340 if (op->contr->explore) {
03341 draw_ext_info(NDI_UNIQUE, 0, op,
03342 MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03343 "You would have died, but you are "
03344 "in explore mode, so...", NULL);
03345 op->stats.hp = op->stats.maxhp;
03346 return;
03347 }
03348 snprintf(buf, sizeof(buf), "%s died.", op->name);
03349 }
03350 play_sound_player_only(op->contr, SOUND_TYPE_LIVING, op, 0, "death");
03351
03352
03353 x = op->x;
03354 y = op->y;
03355 map = op->map;
03356
03357 if (settings.not_permadeth == TRUE) {
03358
03359
03360
03361
03362
03363
03364
03365
03366
03367
03368 if (settings.balanced_stat_loss) {
03369
03370
03371
03372
03373
03374
03375
03376 if (settings.stat_loss_on_death)
03377 num_stats_lose = 1;
03378 else
03379 num_stats_lose = 1+op->level/BALSL_NUMBER_LOSSES_RATIO;
03380 } else {
03381 num_stats_lose = 1;
03382 }
03383 lost_a_stat = 0;
03384
03385 for (z = 0; z < num_stats_lose; z++) {
03386 if (settings.stat_loss_on_death) {
03387
03388
03389
03390 i = RANDOM()%7;
03391 change_attr_value(&(op->stats), i, -1);
03392 check_stat_bounds(&(op->stats));
03393 change_attr_value(&(op->contr->orig_stats), i, -1);
03394 check_stat_bounds(&(op->contr->orig_stats));
03395 draw_ext_info(NDI_UNIQUE, 0, op,
03396 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_STAT_LOSS,
03397 lose_msg[i], lose_msg[i]);
03398 lost_a_stat = 1;
03399 } else {
03400
03401 archetype *deparch = find_archetype("depletion");
03402 object *dep;
03403
03404 i = RANDOM()%7;
03405 dep = present_arch_in_ob(deparch, op);
03406 if (!dep) {
03407 dep = arch_to_object(deparch);
03408 insert_ob_in_ob(dep, op);
03409 }
03410 lose_this_stat = 1;
03411 if (settings.balanced_stat_loss) {
03412
03413
03414 this_stat = get_attr_value(&(dep->stats), i);
03415 if (this_stat < 0) {
03416 int loss_chance = 1+op->level/BALSL_LOSS_CHANCE_RATIO;
03417 int keep_chance = this_stat*this_stat;
03418
03419 if (keep_chance < 1)
03420 keep_chance = 1;
03421
03422
03423 if (this_stat < -1-op->level/BALSL_MAX_LOSS_RATIO) {
03424 lose_this_stat = 0;
03425
03426
03427 } else {
03428 if (random_roll(0, loss_chance+keep_chance-1, op, PREFER_LOW) < keep_chance)
03429 lose_this_stat = 0;
03430
03431 }
03432 }
03433 }
03434
03435 if (lose_this_stat) {
03436 this_stat = get_attr_value(&(dep->stats), i);
03437
03438
03439
03440
03441
03442
03443 if (this_stat >= -50) {
03444 change_attr_value(&(dep->stats), i, -1);
03445 SET_FLAG(dep, FLAG_APPLIED);
03446 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_STAT_LOSS,
03447 lose_msg[i], lose_msg[i]);
03448 fix_object(op);
03449 lost_a_stat = 1;
03450 }
03451 }
03452 }
03453 }
03454
03455 if (!lost_a_stat) {
03456
03457 const char *god = determine_god(op);
03458
03459 if (god && (strcmp(god, "none")))
03460 draw_ext_info_format(NDI_UNIQUE, 0, op,
03461 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD,
03462 "For a brief moment you feel the holy presence of %s protecting you",
03463 "For a brief moment you feel the holy presence of %s protecting you",
03464 god);
03465 else
03466 draw_ext_info(NDI_UNIQUE, 0, op,
03467 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD,
03468 "For a brief moment you feel a holy presence protecting you.",
03469 NULL);
03470 }
03471
03472
03473
03474
03475 tmp = arch_to_object(find_archetype("gravestone"));
03476 snprintf(buf, sizeof(buf), "%s's gravestone", op->name);
03477 FREE_AND_COPY(tmp->name, buf);
03478 snprintf(buf, sizeof(buf), "%s's gravestones", op->name);
03479 FREE_AND_COPY(tmp->name_pl, buf);
03480 snprintf(buf, sizeof(buf), "RIP\nHere rests the hero %s the %s,\n"
03481 "who was killed\n"
03482 "by %s.\n",
03483 op->name, op->contr->title,
03484 op->contr->killer);
03485 tmp->msg = add_string(buf);
03486 tmp->x = op->x,
03487 tmp->y = op->y;
03488 insert_ob_in_map(tmp, op->map, NULL, 0);
03489
03490
03491
03492 at = find_archetype("poisoning");
03493 tmp = present_arch_in_ob(at, op);
03494 if (tmp) {
03495 remove_ob(tmp);
03496 free_object(tmp);
03497 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03498 "Your body feels cleansed", NULL);
03499 }
03500
03501 at = find_archetype("confusion");
03502 tmp = present_arch_in_ob(at, op);
03503 if (tmp) {
03504 remove_ob(tmp);
03505 free_object(tmp);
03506 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03507 "Your mind feels clearer", NULL);
03508 }
03509 cure_disease(op, NULL);
03510
03511
03512
03513
03514 apply_death_exp_penalty(op);
03515 if (op->stats.food < 100)
03516 op->stats.food = 900;
03517 op->stats.hp = op->stats.maxhp;
03518 op->stats.sp = MAX(op->stats.sp, op->stats.maxsp);
03519 op->stats.grace = MAX(op->stats.grace, op->stats.maxgrace);
03520
03521
03522
03523
03524
03525
03526
03527
03528 if (is_in_shop(op))
03529 remove_unpaid_objects(op->inv, op, 0);
03530 else
03531 remove_unpaid_objects(op->inv, op, 1);
03532
03533
03534 enter_player_savebed(op);
03535
03536
03537 op->contr->braced = 0;
03538 save_player(op, 1);
03539
03540
03541
03542
03543
03544
03545 will_kill_again = 0;
03546 for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp; tmp = tmp->above) {
03547 if (tmp->type == SPELL_EFFECT)
03548 will_kill_again |= tmp->attacktype;
03549 }
03550 if (will_kill_again) {
03551 object *force;
03552 int at;
03553
03554 force = create_archetype(FORCE_NAME);
03555
03556 force->speed = 0.1;
03557 force->speed_left = -5.0;
03558 SET_FLAG(force, FLAG_APPLIED);
03559 for (at = 0; at < NROFATTACKS; at++) {
03560 if (will_kill_again&(1<<at))
03561 force->resist[at] = 100;
03562 }
03563 insert_ob_in_ob(force, op);
03564 fix_object(op);
03565 }
03566
03567
03568 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03569 "YOU HAVE DIED.", NULL);
03570 return;
03571 }
03572 else {
03573
03574
03575
03576
03577 op->contr->party = NULL;
03578 if (settings.set_title == TRUE)
03579 op->contr->own_title[0] = '\0';
03580
03581
03582 draw_ext_info(NDI_UNIQUE|NDI_ALL, 0, NULL, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03583 buf, buf);
03584 check_score(op, 0);
03585 if (op->contr->ranges[range_golem] != NULL) {
03586 remove_friendly_object(op->contr->ranges[range_golem]);
03587 remove_ob(op->contr->ranges[range_golem]);
03588 free_object(op->contr->ranges[range_golem]);
03589 op->contr->ranges[range_golem] = NULL;
03590 op->contr->golem_count = 0;
03591 }
03592 loot_object(op);
03593 remove_ob(op);
03594 op->direction = 0;
03595
03596 if (!QUERY_FLAG(op, FLAG_WAS_WIZ) && op->stats.exp) {
03597 if (settings.resurrection == TRUE) {
03598
03599
03600
03601
03602 op->stats.hp = op->stats.maxhp;
03603 op->stats.food = 999;
03604
03605
03606
03607 strcpy(op->contr->maplevel, settings.emergency_mapname);
03608 if (op->map != NULL)
03609 op->map = NULL;
03610 op->x = settings.emergency_x;
03611 op->y = settings.emergency_y;
03612 save_player(op, 0);
03613 op->map = map;
03614
03615 dead_player(op);
03616 } else {
03617 delete_character(op->name);
03618 }
03619 }
03620 play_again(op);
03621
03622
03623 tmp = arch_to_object(find_archetype("corpse_pl"));
03624 snprintf(buf, sizeof(buf), "%s", op->name);
03625 FREE_AND_COPY(tmp->name, buf);
03626 FREE_AND_COPY(tmp->name_pl, buf);
03627 tmp->level = op->level;
03628 tmp->x = x;
03629 tmp->y = y;
03630 if (tmp->msg)
03631 free_string(tmp->msg);
03632 tmp->msg = add_string(gravestone_text(op, buf, sizeof(buf)));
03633 SET_FLAG(tmp, FLAG_UNIQUE);
03634 insert_ob_in_map(tmp, map, NULL, 0);
03635 }
03636 }
03637
03645 void fix_weight(void) {
03646 player *pl;
03647
03648 for (pl = first_player; pl != NULL; pl = pl->next) {
03649 int old = pl->ob->carrying, sum = sum_weight(pl->ob);
03650
03651 if (old == sum)
03652 continue;
03653 fix_object(pl->ob);
03654 LOG(llevDebug, "Fixed inventory in %s (%d -> %d)\n", pl->ob->name, old, sum);
03655 }
03656 }
03657
03661 void fix_luck(void) {
03662 player *pl;
03663
03664 for (pl = first_player; pl != NULL; pl = pl->next)
03665 if (!pl->ob->contr->state)
03666 change_luck(pl->ob, 0);
03667 }
03668
03669
03682 void cast_dust(object *op, object *throw_ob, int dir) {
03683 object *skop, *spob;
03684
03685 skop = find_skill_by_name(op, throw_ob->skill);
03686
03687
03688 if (op->type == PLAYER && throw_ob->type == POTION && !skop) {
03689 LOG(llevError, "Player %s lacks critical skill use_magic_item!\n", op->name);
03690 return;
03691 }
03692 spob = throw_ob->inv;
03693 if (op->type == PLAYER && spob)
03694 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
03695 "You cast %s.",
03696 "You cast %s.",
03697 spob->name);
03698
03699 cast_spell(op, throw_ob, dir, spob, NULL);
03700
03701 if (!QUERY_FLAG(throw_ob, FLAG_REMOVED))
03702 remove_ob(throw_ob);
03703 free_object(throw_ob);
03704 }
03705
03712 void make_visible(object *op) {
03713 op->hide = 0;
03714 op->invisible = 0;
03715 if (op->type == PLAYER) {
03716 op->contr->tmp_invis = 0;
03717 if (op->contr->invis_race)
03718 FREE_AND_CLEAR_STR(op->contr->invis_race);
03719 }
03720 update_object(op, UP_OBJ_FACE);
03721 }
03722
03731 int is_true_undead(object *op) {
03732 object *tmp = NULL;
03733
03734 if (QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
03735 return 1;
03736
03737 if (op->type == PLAYER)
03738 for (tmp = op->inv; tmp; tmp = tmp->below)
03739 if (tmp->type == EXPERIENCE && tmp->stats.Wis)
03740 if (QUERY_FLAG(tmp, FLAG_UNDEAD))
03741 return 1;
03742 return 0;
03743 }
03744
03755 int hideability(object *ob) {
03756 int i, level = 0, mflag;
03757 sint16 x, y;
03758
03759 if (!ob || !ob->map)
03760 return 0;
03761
03762
03763 level = ob->map->darkness-2;
03764
03765
03766
03767
03768
03769 if (has_carried_lights(ob))
03770 level = -(10+(2*ob->map->darkness));
03771
03772
03773 for (i = 0, x = ob->x, y = ob->y; i < 9; i++, x = ob->x+freearr_x[i], y = ob->y+freearr_y[i]) {
03774 mflag = get_map_flags(ob->map, NULL, x, y, NULL, NULL);
03775 if (mflag&P_OUT_OF_MAP) {
03776 continue;
03777 }
03778 if (mflag&P_BLOCKSVIEW)
03779 level += 2;
03780 else
03781 level -= 1;
03782 }
03783
03784 return level;
03785 }
03786
03796 void do_hidden_move(object *op) {
03797 int hide = 0, num = random_roll(0, 19, op, PREFER_LOW);
03798 object *skop;
03799
03800 if (!op || !op->map)
03801 return;
03802
03803 skop = find_obj_by_type_subtype(op, SKILL, SK_HIDING);
03804
03805
03806 if (op->type == PLAYER && op->contr->run_on) {
03807 if (!skop || num >= skop->level) {
03808 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
03809 "You ran too much! You are no longer hidden!", NULL);
03810 make_visible(op);
03811 return;
03812 } else
03813 num += 20;
03814 }
03815 num += op->map->difficulty;
03816 hide = hideability(op);
03817 num -= hide;
03818 if ((op->type == PLAYER && hide < -10)
03819 || ((op->invisible -= num) <= 0)) {
03820 make_visible(op);
03821 if (op->type == PLAYER)
03822 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
03823 "You moved out of hiding! You are visible!", NULL);
03824 } else if (op->type == PLAYER && skop) {
03825 change_exp(op, calc_skill_exp(op, NULL, skop), skop->skill, 0);
03826 }
03827 }
03828
03837 int stand_near_hostile(object *who) {
03838 object *tmp = NULL;
03839 int i, friendly = 0, player = 0, mflags;
03840 mapstruct *m;
03841 sint16 x, y;
03842
03843 if (!who)
03844 return 0;
03845
03846 if (who->type == PLAYER)
03847 player = 1;
03848 else
03849 friendly = QUERY_FLAG(who, FLAG_FRIENDLY);
03850
03851
03852 for (i = 1; i < 9; i++) {
03853 x = who->x+freearr_x[i];
03854 y = who->y+freearr_y[i];
03855 m = who->map;
03856 mflags = get_map_flags(m, &m, x, y, &x, &y);
03857
03858
03859
03860 if (mflags&P_OUT_OF_MAP)
03861 continue;
03862 if (OB_TYPE_MOVE_BLOCK(who, GET_MAP_MOVE_BLOCK(m, x, y)))
03863 continue;
03864
03865 for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
03866 if ((player || friendly)
03867 && QUERY_FLAG(tmp, FLAG_MONSTER)
03868 && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
03869 return 1;
03870 else if (tmp->type == PLAYER) {
03871
03872 if (!QUERY_FLAG(tmp, FLAG_WIZ) || tmp->contr->hidden == 0)
03873 return 1;
03874 }
03875 }
03876 }
03877 return 0;
03878 }
03879
03906 int player_can_view(object *pl, object *op) {
03907 rv_vector rv;
03908 int dx, dy;
03909
03910 if (pl->type != PLAYER) {
03911 LOG(llevError, "player_can_view() called for non-player object\n");
03912 return -1;
03913 }
03914 if (!pl || !op)
03915 return 0;
03916
03917 if (op->head) {
03918 op = op->head;
03919 }
03920 get_rangevector(pl, op, &rv, 0x1);
03921
03922
03923
03924
03925
03926
03927
03928 while (op) {
03929 dx = rv.distance_x+op->arch->clone.x;
03930 dy = rv.distance_y+op->arch->clone.y;
03931
03932
03933
03934
03935
03936 if (FABS(dx) <= (pl->contr->socket.mapx/2)
03937 && FABS(dy) <= (pl->contr->socket.mapy/2)
03938 && !pl->contr->blocked_los[dx+(pl->contr->socket.mapx/2)][dy+(pl->contr->socket.mapy/2)])
03939 return 1;
03940 op = op->more;
03941 }
03942 return 0;
03943 }
03944
03958 static int action_makes_visible(object *op) {
03959 if (op->invisible && QUERY_FLAG(op, FLAG_ALIVE)) {
03960 if (QUERY_FLAG(op, FLAG_MAKE_INVIS))
03961 return 0;
03962
03963 if (op->contr && op->contr->tmp_invis == 0)
03964 return 0;
03965
03966
03967 if (op->hide || !op->contr || (op->contr && op->contr->tmp_invis)) {
03968 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_MISC, MSG_SUBTYPE_NONE,
03969 "You become %s!",
03970 "You become %s!",
03971 op->hide ? "unhidden" : "visible");
03972 return 1;
03973 }
03974 }
03975 return 0;
03976 }
03977
04002 int op_on_battleground(object *op, int *x, int *y, archetype **trophy) {
04003 object *tmp;
04004
04005
04006
04007
04008
04009
04010
04011 for (tmp = op->below; tmp != NULL; tmp = tmp->below) {
04012 if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
04013 if (QUERY_FLAG(tmp, FLAG_NO_PICK)
04014 && strcmp(tmp->name, "battleground") == 0
04015 && tmp->type == BATTLEGROUND
04016 && EXIT_X(tmp)
04017 && EXIT_Y(tmp)) {
04018
04019 if (EXIT_ALT_X(tmp) && EXIT_ALT_Y(tmp) && EXIT_PATH(tmp)) {
04020 object *invtmp;
04021
04022 for (invtmp = op->inv; invtmp != NULL; invtmp = invtmp->below) {
04023 if (invtmp->type == FORCE
04024 && invtmp->slaying
04025 && !strcmp(EXIT_PATH(tmp), invtmp->slaying)) {
04026 if (x != NULL && y != NULL)
04027 *x = EXIT_ALT_X(tmp),
04028 *y = EXIT_ALT_Y(tmp);
04029 return 1;
04030 }
04031 }
04032 }
04033 if (x != NULL && y != NULL)
04034 *x = EXIT_X(tmp),
04035 *y = EXIT_Y(tmp);
04036 if (trophy != NULL) {
04037 if (tmp->other_arch)
04038 *trophy = tmp->other_arch;
04039 else
04040 *trophy = find_archetype("finger");
04041 }
04042 return 1;
04043 }
04044 }
04045 }
04046
04047 return 0;
04048 }
04049
04060 void dragon_ability_gain(object *who, int atnr, int level) {
04061 treasurelist *trlist = NULL;
04062 treasure *tr;
04063 object *tmp, *skop;
04064 object *item;
04065 char buf[MAX_BUF];
04066 int i = 0, j = 0;
04067
04068
04069 if (atnr == ATNR_FIRE)
04070 trlist = find_treasurelist("dragon_ability_fire");
04071 else if (atnr == ATNR_COLD)
04072 trlist = find_treasurelist("dragon_ability_cold");
04073 else if (atnr == ATNR_ELECTRICITY)
04074 trlist = find_treasurelist("dragon_ability_elec");
04075 else if (atnr == ATNR_POISON)
04076 trlist = find_treasurelist("dragon_ability_poison");
04077
04078 if (trlist == NULL || who->type != PLAYER)
04079 return;
04080
04081 for (i = 0, tr = trlist->items; tr != NULL && i < level-1; tr = tr->next, i++)
04082 ;
04083 if (tr == NULL || tr->item == NULL) {
04084
04085 return;
04086 }
04087
04088
04089 item = &(tr->item->clone);
04090
04091 if (item->type == SPELL) {
04092 if (check_spell_known(who, item->name))
04093 return;
04094
04095 draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who,
04096 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04097 "You gained the ability of %s",
04098 "You gained the ability of %s",
04099 item->name);
04100 do_learn_spell(who, item, 0);
04101 return;
04102 }
04103
04104
04105 if (item->type == SPELLBOOK) {
04106 if (!item->inv) {
04107 LOG(llevDebug, "dragon_ability_gain: Broken spellbook %s\n", item->name);
04108 return;
04109 }
04110 if (check_spell_known(who, item->inv->name))
04111 return;
04112 if (item->invisible) {
04113 draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who,
04114 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04115 "You gained the ability of %s",
04116 "You gained the ability of %s",
04117 item->inv->name);
04118 do_learn_spell(who, item->inv, 0);
04119 return;
04120 }
04121 } else if (item->type == SKILL_TOOL && item->invisible) {
04122 if (item->subtype == SK_CLAWING && (skop = find_skill_by_name(who, item->skill)) != NULL) {
04123
04124
04125
04126
04127
04128 if (!(skop->attacktype&item->attacktype)) {
04129
04130 skop->attacktype |= item->attacktype;
04131
04132
04133 skop->attacktype |= AT_PHYSICAL;
04134
04135 if (item->msg != NULL)
04136 draw_ext_info(NDI_UNIQUE|NDI_BLUE, 0, who,
04137 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04138 item->msg, item->msg);
04139
04140
04141 if (item->animation_id) {
04142 who->face = skop->face;
04143 who->animation_id = item->animation_id;
04144 who->anim_speed = item->anim_speed;
04145 who->last_anim = 0;
04146 who->state = 0;
04147 animate_object(who, who->direction);
04148 }
04149 }
04150 }
04151 } else if (item->type == FORCE) {
04152
04153 object *skin;
04154
04155
04156 for (skin = who->inv; skin != NULL && strcmp(skin->arch->name, "dragon_skin_force") != 0; skin = skin->below)
04157 ;
04158 if (skin == NULL)
04159 return;
04160
04161
04162 if (item->path_attuned > 0 && !(skin->path_attuned&item->path_attuned)) {
04163 skin->path_attuned |= item->path_attuned;
04164
04165
04166 snprintf(buf, sizeof(buf), "You feel attuned to ");
04167 for (i = 0, j = 0; i < NRSPELLPATHS; i++) {
04168 if (item->path_attuned&(1<<i)) {
04169 if (j)
04170 strcat(buf, " and ");
04171 else
04172 j = 1;
04173 strcat(buf, spellpathnames[i]);
04174 }
04175 }
04176 strcat(buf, ".");
04177 draw_ext_info(NDI_UNIQUE|NDI_BLUE, 0, who,
04178 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04179 buf, buf);
04180 }
04181
04182
04183 if (QUERY_FLAG(item, FLAG_XRAYS))
04184 SET_FLAG(skin, FLAG_XRAYS);
04185 if (QUERY_FLAG(item, FLAG_STEALTH))
04186 SET_FLAG(skin, FLAG_STEALTH);
04187 if (QUERY_FLAG(item, FLAG_SEE_IN_DARK))
04188 SET_FLAG(skin, FLAG_SEE_IN_DARK);
04189
04190
04191 if (item->msg != NULL)
04192 draw_ext_info(NDI_UNIQUE|NDI_BLUE, 0, who,
04193 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04194 item->msg, item->msg);
04195 } else {
04196
04197 char name[HUGE_BUF];
04198
04199 tmp = arch_to_object(tr->item);
04200 query_short_name(tmp, name, HUGE_BUF);
04201 draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who,
04202 MSG_TYPE_ITEM, MSG_TYPE_ITEM_ADD,
04203 "You gained %s",
04204 "You gained %s",
04205 name);
04206 tmp = insert_ob_in_ob(tmp, who);
04207 if (who->type == PLAYER)
04208 esrv_send_item(who, tmp);
04209 }
04210 }
04211
04221 void player_unready_range_ob(player *pl, object *ob) {
04222 rangetype i;
04223
04224 for (i = 0; i < range_size; i++) {
04225 if (pl->ranges[i] == ob) {
04226 pl->ranges[i] = NULL;
04227 if (pl->shoottype == i) {
04228 pl->shoottype = range_none;
04229 }
04230 }
04231 }
04232 }