|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00030 #include <global.h> 00031 00032 static archetype *get_player_archetype(archetype *at); 00033 static int save_life(object *op); 00034 static void remove_unpaid_objects(object *op, object *env); 00035 00040 player *find_player(const char *plname) 00041 { 00042 player *pl; 00043 00044 for (pl = first_player; pl; pl = pl->next) 00045 { 00046 if (pl->ob && pl->state == ST_PLAYING && !strncasecmp(pl->ob->name, plname, MAX_NAME)) 00047 { 00048 return pl; 00049 } 00050 } 00051 00052 return NULL; 00053 } 00054 00061 void display_motd(object *op) 00062 { 00063 char buf[MAX_BUF]; 00064 FILE *fp; 00065 00066 snprintf(buf, sizeof(buf), "%s/motd_custom", settings.localdir); 00067 00068 if ((fp = fopen(buf, "r")) == NULL) 00069 { 00070 snprintf(buf, sizeof(buf), "%s/motd", settings.localdir); 00071 00072 if ((fp = fopen(buf, "r")) == NULL) 00073 { 00074 return; 00075 } 00076 } 00077 00078 while (fgets(buf, MAX_BUF, fp) != NULL) 00079 { 00080 char *cp; 00081 00082 if (buf[0] == '#' || buf[0] == '\n') 00083 { 00084 continue; 00085 } 00086 00087 cp = strchr(buf, '\n'); 00088 00089 if (cp != NULL) 00090 { 00091 *cp = '\0'; 00092 } 00093 00094 new_draw_info(0, COLOR_WHITE, op, buf); 00095 } 00096 00097 fclose(fp); 00098 new_draw_info(0, COLOR_WHITE, op, " "); 00099 } 00100 00106 int playername_ok(char *cp) 00107 { 00108 for (; *cp != '\0'; cp++) 00109 { 00110 if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= 'A' && *cp <= 'Z')) && *cp != '-' && *cp != '_') 00111 { 00112 return 0; 00113 } 00114 } 00115 00116 return 1; 00117 } 00118 00124 static player *get_player(player *p) 00125 { 00126 object *op = arch_to_object(get_player_archetype(NULL)); 00127 int i; 00128 00129 if (!p) 00130 { 00131 p = (player *) get_poolchunk(pool_player); 00132 memset(p, 0, sizeof(player)); 00133 00134 if (p == NULL) 00135 { 00136 LOG(llevError, "get_player(): Out of memory\n"); 00137 } 00138 00139 if (!last_player) 00140 { 00141 first_player = last_player = p; 00142 } 00143 else 00144 { 00145 last_player->next = p; 00146 p->prev = last_player; 00147 last_player = p; 00148 } 00149 } 00150 else 00151 { 00152 /* Clears basically the entire player structure except 00153 * for next and socket. */ 00154 memset((void *) ((char *) p + offsetof(player, maplevel)), 0, sizeof(player) - offsetof(player, maplevel)); 00155 } 00156 00157 #ifdef AUTOSAVE 00158 p->last_save_tick = 9999999; 00159 #endif 00160 00161 /* Init. respawn position */ 00162 strcpy(p->savebed_map, first_map_path); 00163 00164 p->firemode_type = p->firemode_tag1 = p->firemode_tag2 = -1; 00165 /* This is where we set up initial CONTR(op) */ 00166 op->custom_attrset = p; 00167 p->ob = op; 00168 op->speed_left = 0.5; 00169 op->speed = 1.0; 00170 op->run_away = 0; 00171 00172 p->state = ST_ROLL_STAT; 00173 00174 p->target_hp = -1; 00175 p->target_hp_p = -1; 00176 p->gen_sp_armour = 0; 00177 p->last_speed = -1; 00178 p->shoottype = range_none; 00179 p->last_weapon_sp = -1; 00180 p->update_los = 1; 00181 00182 FREE_AND_COPY_HASH(op->race, op->arch->clone.race); 00183 00184 /* Would be better if '0' was not a defined spell */ 00185 for (i = 0; i < NROFREALSPELLS; i++) 00186 { 00187 p->known_spells[i] = -1; 00188 } 00189 00190 for (i = 0; i < MAX_QUICKSLOT; i++) 00191 { 00192 p->spell_quickslots[i] = SP_NO_SPELL; 00193 } 00194 00195 p->chosen_spell = -1; 00196 00197 /* We need to clear these to -1 and not zero - otherwise, if a player 00198 * quits and starts a new character, we won't send new values to the 00199 * client, as things like exp start at zero. */ 00200 for (i = 0; i < MAX_EXP_CAT; i++) 00201 { 00202 p->last_skill_exp[i] = -1; 00203 p->last_skill_level[i] = -1; 00204 } 00205 00206 /* Quick skill reminder for select hand weapon */ 00207 p->set_skill_weapon = NO_SKILL_READY; 00208 p->set_skill_archery = NO_SKILL_READY; 00209 p->last_skill_index = -1; 00210 p->last_stats.exp = -1; 00211 00212 return p; 00213 } 00214 00219 void free_player(player *pl) 00220 { 00221 if (pl->ob) 00222 { 00223 SET_FLAG(pl->ob, FLAG_NO_FIX_PLAYER); 00224 00225 if (!QUERY_FLAG(pl->ob, FLAG_REMOVED)) 00226 { 00227 remove_ob(pl->ob); 00228 check_walk_off(pl->ob, NULL, MOVE_APPLY_VANISHED); 00229 } 00230 } 00231 00232 /* Free command permissions. */ 00233 if (pl->cmd_permissions) 00234 { 00235 int i; 00236 00237 for (i = 0; i < pl->num_cmd_permissions; i++) 00238 { 00239 if (pl->cmd_permissions[i]) 00240 { 00241 free(pl->cmd_permissions[i]); 00242 } 00243 } 00244 00245 free(pl->cmd_permissions); 00246 } 00247 00248 if (pl->faction_ids) 00249 { 00250 int i; 00251 00252 for (i = 0; i < pl->num_faction_ids; i++) 00253 { 00254 FREE_ONLY_HASH(pl->faction_ids[i]); 00255 } 00256 00257 free(pl->faction_ids); 00258 } 00259 00260 if (pl->faction_reputation) 00261 { 00262 free(pl->faction_reputation); 00263 } 00264 00265 player_path_clear(pl); 00266 00267 /* Now remove from list of players. */ 00268 if (pl->prev) 00269 { 00270 pl->prev->next = pl->next; 00271 } 00272 else 00273 { 00274 first_player = pl->next; 00275 } 00276 00277 if (pl->next) 00278 { 00279 pl->next->prev = pl->prev; 00280 } 00281 else 00282 { 00283 last_player = pl->prev; 00284 } 00285 00286 free_newsocket(&pl->socket); 00287 00288 if (pl->ob) 00289 { 00290 destroy_object(pl->ob); 00291 } 00292 } 00293 00301 int add_player(socket_struct *ns) 00302 { 00303 player *p = get_player(NULL); 00304 memcpy(&p->socket, ns, sizeof(socket_struct)); 00305 00306 /* now, we start the login procedure! */ 00307 p->socket.status = Ns_Login; 00308 p->socket.below_clear = 0; 00309 p->socket.update_tile = 0; 00310 p->socket.look_position = 0; 00311 p->socket.inbuf.len = 0; 00312 00313 get_name(p->ob); 00314 00315 /* Avoid gc of the player */ 00316 insert_ob_in_ob(p->ob, &void_container); 00317 00318 return 0; 00319 } 00320 00327 static archetype *get_player_archetype(archetype *at) 00328 { 00329 archetype *start = at; 00330 00331 for (; ;) 00332 { 00333 if (at == NULL || at->next == NULL) 00334 { 00335 at = first_archetype; 00336 } 00337 else 00338 { 00339 at = at->next; 00340 } 00341 00342 if (at->clone.type == PLAYER) 00343 { 00344 return at; 00345 } 00346 00347 if (at == start) 00348 { 00349 LOG(llevError, "No player achetypes\n"); 00350 exit(-1); 00351 } 00352 } 00353 } 00354 00360 void give_initial_items(object *pl, treasurelist *items) 00361 { 00362 object *op, *next = NULL; 00363 00364 if (pl->randomitems) 00365 { 00366 create_treasure(items, pl, GT_ONLY_GOOD | GT_NO_VALUE, 1, T_STYLE_UNSET, ART_CHANCE_UNSET, 0, NULL); 00367 } 00368 00369 for (op = pl->inv; op; op = next) 00370 { 00371 next = op->below; 00372 00373 /* Forces get applied by default */ 00374 if (op->type == FORCE) 00375 { 00376 SET_FLAG(op, FLAG_APPLIED); 00377 } 00378 00379 /* We never give weapons/armour if they cannot be used by this 00380 * player due to race restrictions */ 00381 if (pl->type == PLAYER) 00382 { 00383 if ((!QUERY_FLAG(pl, FLAG_USE_ARMOUR) && (op->type == ARMOUR || op->type == BOOTS || op->type == CLOAK || op->type == HELMET || op->type == SHIELD || op->type == GLOVES || op->type == BRACERS || op->type == GIRDLE)) || (!QUERY_FLAG(pl, FLAG_USE_WEAPON) && op->type == WEAPON)) 00384 { 00385 /* Inventory action */ 00386 remove_ob(op); 00387 continue; 00388 } 00389 } 00390 00391 /* Give starting characters identified, uncursed, and undamned 00392 * items. Just don't identify gold or silver, or it won't be 00393 * merged properly. */ 00394 if (need_identify(op)) 00395 { 00396 SET_FLAG(op, FLAG_IDENTIFIED); 00397 CLEAR_FLAG(op, FLAG_CURSED); 00398 CLEAR_FLAG(op, FLAG_DAMNED); 00399 } 00400 00401 /* Apply initial armor */ 00402 if (IS_ARMOR(op)) 00403 { 00404 manual_apply(pl, op, 0); 00405 } 00406 00407 if (op->type == ABILITY) 00408 { 00409 CONTR(pl)->known_spells[CONTR(pl)->nrofknownspells++] = op->stats.sp; 00410 remove_ob(op); 00411 continue; 00412 } 00413 } 00414 } 00415 00419 void get_name(object *op) 00420 { 00421 CONTR(op)->write_buf[0] = '\0'; 00422 CONTR(op)->state = ST_GET_NAME; 00423 send_query(&CONTR(op)->socket, 0, "What is your name?\n:"); 00424 } 00425 00429 void get_password(object *op) 00430 { 00431 CONTR(op)->write_buf[0] = '\0'; 00432 CONTR(op)->state = ST_GET_PASSWORD; 00433 send_query(&CONTR(op)->socket, CS_QUERY_HIDEINPUT, "What is your password?\n:"); 00434 } 00435 00439 void confirm_password(object *op) 00440 { 00441 CONTR(op)->write_buf[0] = '\0'; 00442 CONTR(op)->state = ST_CONFIRM_PASSWORD; 00443 send_query(&CONTR(op)->socket, CS_QUERY_HIDEINPUT, "Please type your password again.\n:"); 00444 } 00445 00452 object *find_arrow(object *op, const char *type) 00453 { 00454 object *tmp = NULL; 00455 00456 for (op = op->inv; op; op = op->below) 00457 { 00458 if (!tmp && op->type == CONTAINER && op->race == type && QUERY_FLAG(op, FLAG_APPLIED)) 00459 { 00460 tmp = find_arrow(op, type); 00461 } 00462 else if (op->type == ARROW && op->race == type) 00463 { 00464 return op; 00465 } 00466 } 00467 00468 return tmp; 00469 } 00470 00475 void fire(object *op, int dir) 00476 { 00477 object *weap = NULL; 00478 int spellcost = 0; 00479 00480 /* A check for players, make sure things are groovy. This routine 00481 * will change the skill of the player as appropriate in order to 00482 * fire whatever is requested. In the case of spells (range_magic) 00483 * it handles whether cleric or mage spell is requested to be 00484 * cast. */ 00485 if (op->type == PLAYER) 00486 { 00487 if (CONTR(op)->firemode_type == FIRE_MODE_NONE) 00488 { 00489 return; 00490 } 00491 00492 if (CONTR(op)->firemode_type == FIRE_MODE_BOW) 00493 { 00494 CONTR(op)->shoottype = range_bow; 00495 } 00496 else if (CONTR(op)->firemode_type == FIRE_MODE_THROW) 00497 { 00498 object *tmp; 00499 00500 /* Insert here test for more throwing skills */ 00501 if (!change_skill(op, SK_THROWING)) 00502 { 00503 return; 00504 } 00505 00506 /* Special case - we must redirect the fire cmd to throwing something */ 00507 tmp = find_throw_tag(op, (tag_t) CONTR(op)->firemode_tag1); 00508 00509 if (tmp) 00510 { 00511 if (!check_skill_action_time(op, op->chosen_skill)) 00512 { 00513 return; 00514 } 00515 00516 do_throw(op, tmp, dir); 00517 get_skill_time(op, op->chosen_skill->stats.sp); 00518 CONTR(op)->action_timer = (float) (CONTR(op)->action_range - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f; 00519 00520 if (CONTR(op)->last_action_timer > 0) 00521 { 00522 CONTR(op)->action_timer *= -1; 00523 } 00524 } 00525 00526 return; 00527 } 00528 else if (CONTR(op)->firemode_type == FIRE_MODE_SPELL) 00529 { 00530 CONTR(op)->shoottype = range_magic; 00531 } 00532 else if (CONTR(op)->firemode_type == FIRE_MODE_WAND) 00533 { 00534 /* We do a jump in fire wand if we haven one */ 00535 CONTR(op)->shoottype = range_wand; 00536 } 00537 else if (CONTR(op)->firemode_type == FIRE_MODE_SKILL) 00538 { 00539 command_rskill(op, CONTR(op)->firemode_name); 00540 CONTR(op)->shoottype = range_skill; 00541 } 00542 else if (CONTR(op)->firemode_type == FIRE_MODE_SUMMON) 00543 { 00544 CONTR(op)->shoottype = range_scroll; 00545 } 00546 else 00547 { 00548 CONTR(op)->shoottype = range_none; 00549 } 00550 00551 if (!check_skill_to_fire(op)) 00552 { 00553 return; 00554 } 00555 } 00556 00557 switch (CONTR(op)->shoottype) 00558 { 00559 case range_none: 00560 return; 00561 00562 case range_bow: 00563 if (CONTR(op)->firemode_tag2 != -1 || CONTR(op)->socket.socket_version >= 1048) 00564 { 00565 /* Still need to recover from range action? */ 00566 if (!check_skill_action_time(op, op->chosen_skill)) 00567 { 00568 return; 00569 } 00570 00571 bow_fire(op, dir); 00572 get_skill_time(op, op->chosen_skill->stats.sp); 00573 CONTR(op)->action_timer = (float) (CONTR(op)->action_range - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f; 00574 00575 if (CONTR(op)->last_action_timer > 0) 00576 { 00577 CONTR(op)->action_timer *= -1; 00578 } 00579 } 00580 00581 return; 00582 00583 /* Casting spells */ 00584 case range_magic: 00585 if (!check_skill_action_time(op, op->chosen_skill)) 00586 { 00587 return; 00588 } 00589 00590 spellcost = cast_spell(op, op, dir, CONTR(op)->chosen_spell, 0, CAST_NORMAL, NULL); 00591 00592 if (spells[CONTR(op)->chosen_spell].type == SPELL_TYPE_PRIEST) 00593 { 00594 op->stats.grace -= spellcost; 00595 } 00596 else 00597 { 00598 op->stats.sp -= spellcost; 00599 } 00600 00601 /* Only change the action timer if the spell required mana/grace cost (ie, was successful). */ 00602 if (spellcost) 00603 { 00604 CONTR(op)->action_casting = ROUND_TAG + spells[CONTR(op)->chosen_spell].time; 00605 CONTR(op)->action_timer = (float) (CONTR(op)->action_casting - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f; 00606 00607 if (CONTR(op)->last_action_timer > 0) 00608 { 00609 CONTR(op)->action_timer *= -1; 00610 } 00611 } 00612 00613 return; 00614 00615 case range_wand: 00616 for (weap = op->inv; weap != NULL; weap = weap->below) 00617 { 00618 if (weap->type == WAND && QUERY_FLAG(weap, FLAG_APPLIED)) 00619 { 00620 break; 00621 } 00622 } 00623 00624 if (weap == NULL) 00625 { 00626 CONTR(op)->shoottype = range_rod; 00627 goto trick_jump; 00628 } 00629 00630 if (!check_skill_action_time(op, op->chosen_skill)) 00631 { 00632 return; 00633 } 00634 00635 if (weap->stats.food <= 0) 00636 { 00637 play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "rod.ogg", 0, 0, 0, 0); 00638 new_draw_info(0, COLOR_WHITE, op, "The wand says poof."); 00639 return; 00640 } 00641 00642 new_draw_info(0, COLOR_WHITE, op, "fire wand"); 00643 00644 if (cast_spell(op, weap, dir, weap->stats.sp, 0, CAST_WAND, NULL)) 00645 { 00646 /* You now know something about it */ 00647 SET_FLAG(op, FLAG_BEEN_APPLIED); 00648 00649 if (!(--weap->stats.food)) 00650 { 00651 object *tmp; 00652 00653 if (weap->arch) 00654 { 00655 CLEAR_FLAG(weap, FLAG_ANIMATE); 00656 weap->face = weap->arch->clone.face; 00657 weap->speed = 0; 00658 update_ob_speed(weap); 00659 } 00660 00661 if ((tmp = is_player_inv(weap))) 00662 { 00663 esrv_update_item(UPD_ANIM, tmp, weap); 00664 } 00665 } 00666 } 00667 00668 get_skill_time(op, op->chosen_skill->stats.sp); 00669 CONTR(op)->action_timer = (float) (CONTR(op)->action_range - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f; 00670 00671 if (CONTR(op)->last_action_timer > 0) 00672 { 00673 CONTR(op)->action_timer *= -1; 00674 } 00675 00676 return; 00677 00678 case range_rod: 00679 case range_horn: 00680 trick_jump: 00681 for (weap = op->inv; weap != NULL; weap = weap->below) 00682 { 00683 if (QUERY_FLAG(weap, FLAG_APPLIED) && weap->type == (CONTR(op)->shoottype == range_rod ? ROD : HORN)) 00684 { 00685 break; 00686 } 00687 } 00688 00689 if (weap == NULL) 00690 { 00691 if (CONTR(op)->shoottype == range_rod) 00692 { 00693 CONTR(op)->shoottype = range_horn; 00694 goto trick_jump; 00695 } 00696 else 00697 { 00698 new_draw_info_format(0, COLOR_WHITE, op, "You have no tool readied."); 00699 return; 00700 } 00701 } 00702 00703 if (!check_skill_action_time(op, op->chosen_skill)) 00704 { 00705 return; 00706 } 00707 00708 /* If the device level is higher than player's magic skill, 00709 * don't allow using the device. */ 00710 if (!CONTR(op)->exp_ptr[EXP_MAGICAL] || weap->level > CONTR(op)->exp_ptr[EXP_MAGICAL]->level + settings.magic_devices_level) 00711 { 00712 new_draw_info_format(0, COLOR_WHITE, op, "The %s is impossible to handle for you.", weap->name); 00713 return; 00714 } 00715 00716 if (weap->stats.hp < spells[weap->stats.sp].sp) 00717 { 00718 play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "rod.ogg", 0, 0, 0, 0); 00719 00720 if (CONTR(op)->shoottype == range_rod) 00721 { 00722 new_draw_info(0, COLOR_WHITE, op, "The rod whines for a while, but nothing happens."); 00723 } 00724 else 00725 { 00726 new_draw_info(0, COLOR_WHITE, op, "No matter how hard you try you can't get another note out."); 00727 } 00728 00729 return; 00730 } 00731 00732 if (cast_spell(op, weap, dir, weap->stats.sp, 0, CONTR(op)->shoottype == range_rod ? CAST_ROD : CAST_HORN, NULL)) 00733 { 00734 /* You now know something about it */ 00735 SET_FLAG(op, FLAG_BEEN_APPLIED); 00736 drain_rod_charge(weap); 00737 } 00738 00739 get_skill_time(op, op->chosen_skill->stats.sp); 00740 CONTR(op)->action_timer = (float) (CONTR(op)->action_range - global_round_tag) / (1000000 / MAX_TIME) * 1000.0f; 00741 00742 if (CONTR(op)->last_action_timer > 0) 00743 { 00744 CONTR(op)->action_timer *= -1; 00745 } 00746 00747 return; 00748 00749 /* Control summoned monsters from scrolls */ 00750 case range_scroll: 00751 CONTR(op)->shoottype = range_none; 00752 CONTR(op)->chosen_spell = -1; 00753 return; 00754 00755 case range_skill: 00756 if (!op->chosen_skill) 00757 { 00758 if (op->type == PLAYER) 00759 new_draw_info(0, COLOR_WHITE, op, "You have no applicable skill to use."); 00760 return; 00761 } 00762 00763 if (op->chosen_skill->sub_type != ST1_SKILL_USE) 00764 { 00765 new_draw_info(0, COLOR_WHITE, op, "You can't use this skill in this way."); 00766 } 00767 else 00768 { 00769 do_skill(op, dir, NULL); 00770 } 00771 00772 return; 00773 00774 default: 00775 new_draw_info(0, COLOR_WHITE, op, "Illegal shoot type."); 00776 return; 00777 } 00778 } 00779 00785 int move_player(object *op, int dir) 00786 { 00787 int ret = 0; 00788 00789 CONTR(op)->praying = 0; 00790 00791 if (op->map == NULL || op->map->in_memory != MAP_IN_MEMORY) 00792 { 00793 return 0; 00794 } 00795 00796 if (dir) 00797 { 00798 op->facing = dir; 00799 } 00800 00801 if (QUERY_FLAG(op, FLAG_CONFUSED) && dir) 00802 { 00803 dir = get_randomized_dir(dir); 00804 } 00805 00806 op->anim_moving_dir = -1; 00807 op->anim_enemy_dir = -1; 00808 op->anim_last_facing = -1; 00809 00810 /* firemode is set from client command fire xx xx xx */ 00811 if (CONTR(op)->firemode_type != -1) 00812 { 00813 fire(op, dir); 00814 00815 if (dir) 00816 { 00817 op->anim_enemy_dir = dir; 00818 } 00819 else 00820 { 00821 op->anim_enemy_dir = op->facing; 00822 } 00823 00824 CONTR(op)->fire_on = 0; 00825 } 00826 else 00827 { 00828 ret = move_ob(op, dir, op); 00829 00830 if (!ret) 00831 { 00832 op->anim_enemy_dir = dir; 00833 } 00834 else 00835 { 00836 op->anim_moving_dir = dir; 00837 } 00838 } 00839 00840 if (QUERY_FLAG(op, FLAG_ANIMATE)) 00841 { 00842 if (op->anim_enemy_dir == -1 && op->anim_moving_dir == -1) 00843 { 00844 op->anim_last_facing = dir; 00845 } 00846 00847 animate_object(op, 0); 00848 } 00849 00850 return ret; 00851 } 00852 00863 int handle_newcs_player(player *pl) 00864 { 00865 if (!pl->ob || !OBJECT_ACTIVE(pl->ob)) 00866 { 00867 return -1; 00868 } 00869 00870 handle_client(&pl->socket, pl); 00871 00872 if (!pl->ob || !OBJECT_ACTIVE(pl->ob) || pl->socket.status == Ns_Dead) 00873 { 00874 return -1; 00875 } 00876 00877 /* Check speed. */ 00878 if (pl->ob->speed_left < 0.0f) 00879 { 00880 return 0; 00881 } 00882 00883 /* If we are here, we're never paralyzed anymore. */ 00884 CLEAR_FLAG(pl->ob, FLAG_PARALYZED); 00885 00886 if (pl->ob->direction && (CONTR(pl->ob)->run_on || CONTR(pl->ob)->fire_on)) 00887 { 00888 /* All move commands take 1 tick, at least for now. */ 00889 pl->ob->speed_left--; 00890 move_player(pl->ob, pl->ob->direction); 00891 00892 if (pl->ob->speed_left > 0) 00893 { 00894 return 1; 00895 } 00896 else 00897 { 00898 return 0; 00899 } 00900 } 00901 00902 return 0; 00903 } 00904 00911 static int save_life(object *op) 00912 { 00913 object *tmp; 00914 00915 if (!QUERY_FLAG(op, FLAG_LIFESAVE)) 00916 { 00917 return 0; 00918 } 00919 00920 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) 00921 { 00922 if (QUERY_FLAG(tmp, FLAG_APPLIED) && QUERY_FLAG(tmp, FLAG_LIFESAVE)) 00923 { 00924 play_sound_map(op->map, CMD_SOUND_EFFECT, "explosion.ogg", op->x, op->y, 0, 0); 00925 new_draw_info_format(0, COLOR_WHITE, op, "Your %s vibrates violently, then evaporates.", query_name(tmp, NULL)); 00926 00927 if (CONTR(op)) 00928 { 00929 esrv_del_item(CONTR(op), tmp->count, tmp->env); 00930 } 00931 00932 remove_ob(tmp); 00933 CLEAR_FLAG(op, FLAG_LIFESAVE); 00934 00935 if (op->stats.hp < 0) 00936 { 00937 op->stats.hp = op->stats.maxhp; 00938 } 00939 00940 if (op->stats.food < 0) 00941 { 00942 op->stats.food = 999; 00943 } 00944 00945 /* Bring him home. */ 00946 enter_player_savebed(op); 00947 return 1; 00948 } 00949 } 00950 00951 LOG(llevBug, "save_life(): LIFESAVE set without applied object.\n"); 00952 CLEAR_FLAG(op, FLAG_LIFESAVE); 00953 /* Bring him home. */ 00954 enter_player_savebed(op); 00955 return 0; 00956 } 00957 00964 static void remove_unpaid_objects(object *op, object *env) 00965 { 00966 object *next; 00967 00968 while (op) 00969 { 00970 /* Make sure we have a good value, in case 00971 * we remove object 'op' */ 00972 next = op->below; 00973 00974 if (QUERY_FLAG(op, FLAG_UNPAID)) 00975 { 00976 remove_ob(op); 00977 op->x = env->x; 00978 op->y = env->y; 00979 insert_ob_in_map(op, env->map, NULL, 0); 00980 } 00981 else if (op->inv) 00982 { 00983 remove_unpaid_objects(op->inv, env); 00984 } 00985 00986 op = next; 00987 } 00988 } 00989 00995 static int get_regen_amount(uint16 regen, uint16 *remainder) 00996 { 00997 int ret = 0; 00998 float division; 00999 01000 /* Check whether it's time to update the remainder variable (which 01001 * will distribute the remainder evenly over time). */ 01002 if (pticks % 8 == 0) 01003 { 01004 *remainder += regen; 01005 } 01006 01007 /* First check if we can distribute it evenly, if not, try to remove 01008 * leftovers, if any. */ 01009 for (division = (float) 1000000 / MAX_TIME; ; division = 1.0f) 01010 { 01011 if (*remainder / 10.0f / division >= 1.0f) 01012 { 01013 int add = (int) *remainder / 10.0f / division; 01014 01015 ret += add; 01016 *remainder -= add * 10; 01017 break; 01018 } 01019 01020 if (division == 1.0f) 01021 { 01022 break; 01023 } 01024 } 01025 01026 return ret; 01027 } 01028 01035 void do_some_living(object *op) 01036 { 01037 int last_food = op->stats.food; 01038 int gen_hp, gen_sp, gen_grace; 01039 int rate_hp = 2000; 01040 int rate_sp = 1200; 01041 int rate_grace = 400; 01042 int add; 01043 01044 if (CONTR(op)->state != ST_PLAYING) 01045 { 01046 return; 01047 } 01048 01049 gen_hp = (CONTR(op)->gen_hp * (rate_hp / 20)) + (op->stats.maxhp / 4); 01050 gen_sp = (CONTR(op)->gen_sp * (rate_sp / 20)) + op->stats.maxsp; 01051 gen_grace = (CONTR(op)->gen_grace * (rate_grace / 20)) + op->stats.maxgrace; 01052 01053 gen_sp = gen_sp * 10 / MAX(CONTR(op)->gen_sp_armour, 10); 01054 01055 /* Update client's regen rates. */ 01056 CONTR(op)->gen_client_hp = ((float) (1000000 / MAX_TIME) / ((float) rate_hp / (MAX(gen_hp, 20) + 10))) * 10.0f; 01057 CONTR(op)->gen_client_sp = ((float) (1000000 / MAX_TIME) / ((float) rate_sp / (MAX(gen_sp, 20) + 10))) * 10.0f; 01058 CONTR(op)->gen_client_grace = ((float) (1000000 / MAX_TIME) / ((float) rate_grace / (MAX(gen_grace, 20) + 10))) * 10.0f; 01059 01060 /* Regenerate hit points. */ 01061 if (op->stats.hp < op->stats.maxhp && op->stats.food) 01062 { 01063 add = get_regen_amount(CONTR(op)->gen_client_hp, &CONTR(op)->gen_hp_remainder); 01064 01065 if (add) 01066 { 01067 op->stats.hp += add; 01068 CONTR(op)->stat_hp_regen += add; 01069 01070 if (op->stats.hp > op->stats.maxhp) 01071 { 01072 op->stats.hp = op->stats.maxhp; 01073 } 01074 01075 /* DMs do not consume food. */ 01076 if (!QUERY_FLAG(op, FLAG_WIZ)) 01077 { 01078 op->stats.food--; 01079 01080 if (CONTR(op)->digestion < 0) 01081 { 01082 op->stats.food += CONTR(op)->digestion; 01083 } 01084 else if (CONTR(op)->digestion > 0 && rndm(0, CONTR(op)->digestion)) 01085 { 01086 op->stats.food = last_food; 01087 } 01088 } 01089 } 01090 } 01091 else 01092 { 01093 CONTR(op)->gen_hp_remainder = 0; 01094 } 01095 01096 /* Regenerate mana. */ 01097 if (op->stats.sp < op->stats.maxsp && op->stats.food) 01098 { 01099 add = get_regen_amount(CONTR(op)->gen_client_sp, &CONTR(op)->gen_sp_remainder); 01100 01101 if (add) 01102 { 01103 op->stats.sp += add; 01104 CONTR(op)->stat_sp_regen += add; 01105 01106 if (op->stats.sp > op->stats.maxsp) 01107 { 01108 op->stats.sp = op->stats.maxsp; 01109 } 01110 01111 /* DMs do not consume food. */ 01112 if (!QUERY_FLAG(op, FLAG_WIZ)) 01113 { 01114 op->stats.food--; 01115 01116 if (CONTR(op)->digestion < 0) 01117 { 01118 op->stats.food += CONTR(op)->digestion; 01119 } 01120 else if (CONTR(op)->digestion > 0 && rndm(0, CONTR(op)->digestion)) 01121 { 01122 op->stats.food = last_food; 01123 } 01124 } 01125 } 01126 } 01127 else 01128 { 01129 CONTR(op)->gen_sp_remainder = 0; 01130 } 01131 01132 /* Stop and pray. */ 01133 if (CONTR(op)->praying && !CONTR(op)->was_praying) 01134 { 01135 if (op->stats.grace < op->stats.maxgrace) 01136 { 01137 object *god = find_god(determine_god(op)); 01138 01139 if (god) 01140 { 01141 if (CONTR(op)->combat_mode) 01142 { 01143 new_draw_info_format(0, COLOR_WHITE, op, "You stop combat and start praying to %s...", god->name); 01144 CONTR(op)->combat_mode = 0; 01145 send_target_command(CONTR(op)); 01146 } 01147 else 01148 { 01149 new_draw_info_format(0, COLOR_WHITE, op, "You start praying to %s...", god->name); 01150 } 01151 01152 CONTR(op)->was_praying = 1; 01153 } 01154 else 01155 { 01156 new_draw_info(0, COLOR_WHITE, op, "You worship no deity to pray to!"); 01157 CONTR(op)->praying = 0; 01158 } 01159 } 01160 else 01161 { 01162 CONTR(op)->praying = 0; 01163 CONTR(op)->was_praying = 0; 01164 } 01165 } 01166 else if (!CONTR(op)->praying && CONTR(op)->was_praying) 01167 { 01168 new_draw_info(0, COLOR_WHITE, op, "You stop praying."); 01169 CONTR(op)->was_praying = 0; 01170 } 01171 01172 /* Regenerate grace. */ 01173 if (CONTR(op)->praying || op->stats.grace < op->stats.maxgrace / 3) 01174 { 01175 if (op->stats.grace < op->stats.maxgrace) 01176 { 01177 add = get_regen_amount(!CONTR(op)->praying ? CONTR(op)->gen_client_grace / 10 : CONTR(op)->gen_client_grace, &CONTR(op)->gen_grace_remainder); 01178 01179 if (add) 01180 { 01181 op->stats.grace += add; 01182 CONTR(op)->stat_grace_regen += add; 01183 01184 if (op->stats.grace > op->stats.maxgrace) 01185 { 01186 op->stats.grace = op->stats.maxgrace; 01187 } 01188 } 01189 } 01190 else 01191 { 01192 CONTR(op)->gen_grace_remainder = 0; 01193 } 01194 01195 if (op->stats.grace >= op->stats.maxgrace) 01196 { 01197 op->stats.grace = op->stats.maxgrace; 01198 new_draw_info(0, COLOR_WHITE, op, "You are full of grace and stop praying."); 01199 CONTR(op)->was_praying = 0; 01200 } 01201 } 01202 01203 /* Digestion */ 01204 if (--op->last_eat < 0) 01205 { 01206 int bonus = MAX(CONTR(op)->digestion, 0); 01207 int penalty = MAX(-CONTR(op)->digestion, 0); 01208 01209 if (CONTR(op)->gen_hp > 0) 01210 { 01211 op->last_eat = 25 * (1 + bonus) / (CONTR(op)->gen_hp + penalty + 1); 01212 } 01213 else 01214 { 01215 op->last_eat = 25 * (1 + bonus) / (penalty + 1); 01216 } 01217 01218 /* DMs do not consume food. */ 01219 if (!QUERY_FLAG(op, FLAG_WIZ)) 01220 { 01221 op->stats.food--; 01222 } 01223 } 01224 01225 if (op->stats.food < 0 && op->stats.hp >= 0) 01226 { 01227 object *tmp, *flesh = NULL; 01228 01229 for (tmp = op->inv; tmp; tmp = tmp->below) 01230 { 01231 if (!QUERY_FLAG(tmp, FLAG_UNPAID)) 01232 { 01233 if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON) 01234 { 01235 new_draw_info(0, COLOR_WHITE, op, "You blindly grab for a bite of food."); 01236 manual_apply(op, tmp, 0); 01237 01238 if (op->stats.food >= 0 || op->stats.hp < 0) 01239 { 01240 break; 01241 } 01242 } 01243 else if (tmp->type == FLESH) 01244 { 01245 flesh = tmp; 01246 } 01247 } 01248 } 01249 01250 /* If player is still starving, it means they don't have any food, so 01251 * eat flesh instead. */ 01252 if (op->stats.food < 0 && op->stats.hp >= 0 && flesh) 01253 { 01254 new_draw_info(0, COLOR_WHITE, op, "You blindly grab for a bite of food."); 01255 manual_apply(op, flesh, 0); 01256 } 01257 } 01258 01259 while (op->stats.food < 0 && op->stats.hp > 0) 01260 { 01261 op->stats.food++; 01262 op->stats.hp--; 01263 } 01264 01265 if ((op->stats.hp <= 0 || op->stats.food < 0) && !QUERY_FLAG(op, FLAG_WIZ)) 01266 { 01267 new_draw_info_format(NDI_ALL, COLOR_WHITE, NULL, "%s starved to death.", op->name); 01268 strcpy(CONTR(op)->killer, "starvation"); 01269 kill_player(op); 01270 } 01271 } 01272 01278 void kill_player(object *op) 01279 { 01280 char buf[HUGE_BUF]; 01281 int i; 01282 object *tmp; 01283 int z; 01284 int num_stats_lose; 01285 int lost_a_stat; 01286 int lose_this_stat; 01287 int this_stat; 01288 01289 if (pvp_area(NULL, op)) 01290 { 01291 new_draw_info(0, COLOR_NAVY, op, "You have been defeated in combat!\nLocal medics have saved your life..."); 01292 01293 /* Restore player */ 01294 cast_heal(op, MAXLEVEL, op, SP_CURE_POISON); 01295 /* Remove any disease */ 01296 cure_disease(op, NULL); 01297 op->stats.hp = op->stats.maxhp; 01298 op->stats.sp = op->stats.maxsp; 01299 op->stats.grace = op->stats.maxgrace; 01300 01301 if (op->stats.food <= 0) 01302 { 01303 op->stats.food = 999; 01304 } 01305 01306 /* Create a bodypart-trophy to make the winner happy */ 01307 tmp = arch_to_object(find_archetype("finger")); 01308 01309 if (tmp) 01310 { 01311 char race[MAX_BUF]; 01312 01313 snprintf(buf, sizeof(buf), "%s's finger", op->name); 01314 FREE_AND_COPY_HASH(tmp->name, buf); 01315 snprintf(buf, sizeof(buf), "This finger has been cut off %s the %s, when %s was defeated at level %d by %s.", op->name, player_get_race_class(op, race, sizeof(race)), gender_subjective[object_get_gender(op)], op->level, CONTR(op)->killer[0] == '\0' ? "something nasty" : CONTR(op)->killer); 01316 FREE_AND_COPY_HASH(tmp->msg, buf); 01317 tmp->value = 0; 01318 tmp->material = 0; 01319 tmp->type = 0; 01320 tmp->x = op->x, tmp->y = op->y; 01321 insert_ob_in_map(tmp, op->map, op, 0); 01322 } 01323 01324 CONTR(op)->killer[0] = '\0'; 01325 01326 /* Teleport defeated player to new destination */ 01327 transfer_ob(op, MAP_ENTER_X(op->map), MAP_ENTER_Y(op->map), 0, NULL, NULL); 01328 return; 01329 } 01330 01331 if (save_life(op)) 01332 { 01333 return; 01334 } 01335 01336 /* Trigger the DEATH event */ 01337 if (trigger_event(EVENT_DEATH, NULL, op, NULL, NULL, 0, 0, 0, SCRIPT_FIX_ALL)) 01338 { 01339 return; 01340 } 01341 01342 CONTR(op)->stat_deaths++; 01343 01344 /* Trigger the global GDEATH event */ 01345 trigger_global_event(GEVENT_PLAYER_DEATH, NULL, op); 01346 01347 play_sound_player_only(CONTR(op), CMD_SOUND_EFFECT, "playerdead.ogg", 0, 0, 0, 0); 01348 01349 /* Basically two ways to go - remove a stat permanently, or just 01350 * make it depletion. This bunch of code deals with that aspect 01351 * of death. */ 01352 if (settings.balanced_stat_loss) 01353 { 01354 /* If stat loss is permanent, lose one stat only. */ 01355 /* Lower level chars don't lose as many stats because they suffer more 01356 if they do. */ 01357 if (settings.stat_loss_on_death) 01358 { 01359 num_stats_lose = 1; 01360 } 01361 else 01362 { 01363 num_stats_lose = 1 + op->level / BALSL_NUMBER_LOSSES_RATIO; 01364 } 01365 } 01366 else 01367 { 01368 num_stats_lose = 1; 01369 } 01370 01371 lost_a_stat = 0; 01372 01373 /* Only decrease stats if you are level 3 or higher. */ 01374 for (z = 0; z < num_stats_lose; z++) 01375 { 01376 if (settings.stat_loss_on_death && op->level > 3) 01377 { 01378 /* Pick a random stat and take a point off it. Tell the 01379 * player what he lost. */ 01380 i = rndm(1, NUM_STATS) - 1; 01381 change_attr_value(&(op->stats), i, -1); 01382 check_stat_bounds(&(op->stats)); 01383 change_attr_value(&(CONTR(op)->orig_stats), i, -1); 01384 check_stat_bounds(&(CONTR(op)->orig_stats)); 01385 new_draw_info(0, COLOR_WHITE, op, lose_msg[i]); 01386 lost_a_stat = 1; 01387 } 01388 else if (op->level > 3) 01389 { 01390 /* Deplete a stat */ 01391 archetype *deparch = find_archetype("depletion"); 01392 object *dep; 01393 01394 i = rndm(1, NUM_STATS) - 1; 01395 dep = present_arch_in_ob(deparch, op); 01396 01397 if (!dep) 01398 { 01399 dep = arch_to_object(deparch); 01400 insert_ob_in_ob(dep, op); 01401 } 01402 01403 lose_this_stat = 1; 01404 01405 if (settings.balanced_stat_loss) 01406 { 01407 /* Get the stat that we're about to deplete. */ 01408 this_stat = get_attr_value(&(dep->stats), i); 01409 01410 if (this_stat < 0) 01411 { 01412 int loss_chance = 1 + op->level / BALSL_LOSS_CHANCE_RATIO; 01413 int keep_chance = this_stat * this_stat; 01414 01415 /* Yes, I am paranoid. Sue me. */ 01416 if (keep_chance < 1) 01417 { 01418 keep_chance = 1; 01419 } 01420 01421 /* There is a maximum depletion total per level. */ 01422 if (this_stat < -1 - op->level / BALSL_MAX_LOSS_RATIO) 01423 { 01424 lose_this_stat = 0; 01425 } 01426 else 01427 { 01428 /* Take loss chance vs keep chance to see if we retain the stat. */ 01429 if (rndm(0, loss_chance + keep_chance - 1) < keep_chance) 01430 { 01431 lose_this_stat = 0; 01432 } 01433 } 01434 } 01435 } 01436 01437 if (lose_this_stat) 01438 { 01439 this_stat = get_attr_value(&(dep->stats), i); 01440 01441 /* We could try to do something clever like find another 01442 * stat to reduce if this fails. But chances are, if 01443 * stats have been depleted to -50, all are pretty low 01444 * and should be roughly the same, so it shouldn't make a 01445 * difference. */ 01446 if (this_stat >= -50) 01447 { 01448 change_attr_value(&(dep->stats), i, -1); 01449 SET_FLAG(dep, FLAG_APPLIED); 01450 new_draw_info(0, COLOR_WHITE, op, lose_msg[i]); 01451 fix_player(op); 01452 lost_a_stat = 1; 01453 } 01454 } 01455 } 01456 } 01457 01458 /* If no stat lost, tell the player. */ 01459 if (!lost_a_stat) 01460 { 01461 const char *god = determine_god(op); 01462 01463 if (god && god != shstr_cons.none) 01464 { 01465 new_draw_info_format(0, COLOR_WHITE, op, "For a brief moment you feel the holy presence of %s protecting you.", god); 01466 } 01467 else 01468 { 01469 new_draw_info(0, COLOR_WHITE, op, "For a brief moment you feel a holy presence protecting you."); 01470 } 01471 } 01472 01473 /* Put a gravestone up where the character 'almost' died. */ 01474 tmp = arch_to_object(find_archetype("gravestone")); 01475 snprintf(buf, sizeof(buf), "%s's gravestone", op->name); 01476 FREE_AND_COPY_HASH(tmp->name, buf); 01477 FREE_AND_COPY_HASH(tmp->msg, gravestone_text(op)); 01478 tmp->x = op->x; 01479 tmp->y = op->y; 01480 insert_ob_in_map(tmp, op->map, NULL, 0); 01481 01482 /* Subtract the experience points, if we died because of food give us 01483 * food, and reset HP... */ 01484 01485 /* Remove any poisoning the character may be suffering. */ 01486 cast_heal(op, MAXLEVEL, op, SP_CURE_POISON); 01487 /* Remove any disease */ 01488 cure_disease(op, NULL); 01489 01490 /* Apply death experience penalty. */ 01491 apply_death_exp_penalty(op); 01492 01493 if (op->stats.food <= 0) 01494 { 01495 op->stats.food = 999; 01496 } 01497 01498 op->stats.hp = op->stats.maxhp; 01499 op->stats.sp = op->stats.maxsp; 01500 op->stats.grace = op->stats.maxgrace; 01501 01502 hiscore_check(op, 1); 01503 01504 /* Otherwise the highscore can get entries like 'xxx was killed by pudding 01505 * on map Wilderness' even if they were killed in a dungeon. */ 01506 CONTR(op)->killer[0] = '\0'; 01507 01508 /* Check to see if the player is in a shop. Ii so, then check to see 01509 * if the player has any unpaid items. If so, remove them and put 01510 * them back in the map. */ 01511 tmp = get_map_ob(op->map, op->x, op->y); 01512 01513 if (tmp && tmp->type == SHOP_FLOOR) 01514 { 01515 remove_unpaid_objects(op->inv, op); 01516 } 01517 01518 /* Move player to his current respawn position (last savebed). */ 01519 enter_player_savebed(op); 01520 01521 /* Show a nasty message */ 01522 new_draw_info(0, COLOR_WHITE, op, "YOU HAVE DIED."); 01523 save_player(op, 1); 01524 } 01525 01533 void cast_dust(object *op, object *throw_ob, int dir) 01534 { 01535 archetype *arch = NULL; 01536 01537 if (!(spells[throw_ob->stats.sp].flags & SPELL_DESC_DIRECTION)) 01538 { 01539 LOG(llevBug, "Warning, dust %s is not AE spell!!\n", query_name(throw_ob, NULL)); 01540 return; 01541 } 01542 01543 if (spells[throw_ob->stats.sp].archname) 01544 { 01545 arch = find_archetype(spells[throw_ob->stats.sp].archname); 01546 } 01547 01548 /* Casting POTION 'dusts' is really use_magic_item skill */ 01549 if (op->type == PLAYER && throw_ob->type == POTION && !change_skill(op, SK_USE_MAGIC_ITEM)) 01550 { 01551 return; 01552 } 01553 01554 if (throw_ob->type == POTION && arch != NULL) 01555 { 01556 cast_cone(op, throw_ob, dir, 10, throw_ob->stats.sp, arch); 01557 } 01558 /* dust_effect */ 01559 else if ((arch = find_archetype("dust_effect")) != NULL) 01560 { 01561 cast_cone(op, throw_ob, dir, 1, 0, arch); 01562 } 01563 /* Problem occurred! */ 01564 else 01565 { 01566 LOG(llevBug, "cast_dust() can't find an archetype to use!\n"); 01567 } 01568 01569 if (op->type == PLAYER && arch) 01570 { 01571 new_draw_info_format(0, COLOR_WHITE, op, "You cast %s.", query_name(throw_ob, NULL)); 01572 } 01573 01574 if (!QUERY_FLAG(throw_ob, FLAG_REMOVED)) 01575 { 01576 destruct_ob(throw_ob); 01577 } 01578 } 01579 01590 int pvp_area(object *attacker, object *victim) 01591 { 01592 /* No attacking of party members. */ 01593 if (attacker && victim && attacker->type == PLAYER && victim->type == PLAYER && CONTR(attacker)->party != NULL && CONTR(victim)->party != NULL && CONTR(attacker)->party == CONTR(victim)->party) 01594 { 01595 return 0; 01596 } 01597 01598 if (attacker && victim && attacker == victim) 01599 { 01600 return 0; 01601 } 01602 01603 if (attacker && attacker->map) 01604 { 01605 if (!(attacker->map->map_flags & MAP_FLAG_PVP) || GET_MAP_FLAGS(attacker->map, attacker->x, attacker->y) & P_NO_PVP || GET_MAP_SPACE_PTR(attacker->map, attacker->x, attacker->y)->extra_flags & MSP_EXTRA_NO_PVP) 01606 { 01607 return 0; 01608 } 01609 } 01610 01611 if (victim && victim->map) 01612 { 01613 if (!(victim->map->map_flags & MAP_FLAG_PVP) || GET_MAP_FLAGS(victim->map, victim->x, victim->y) & P_NO_PVP || GET_MAP_SPACE_PTR(victim->map, victim->x, victim->y)->extra_flags & MSP_EXTRA_NO_PVP) 01614 { 01615 return 0; 01616 } 01617 } 01618 01619 return 1; 01620 } 01621 01626 int player_exists(char *player_name) 01627 { 01628 FILE *fp; 01629 char filename[HUGE_BUF]; 01630 01631 snprintf(filename, sizeof(filename), "%s/%s/%s/%s.pl", settings.localdir, settings.playerdir, player_name, player_name); 01632 fp = fopen(filename, "r"); 01633 01634 if (fp) 01635 { 01636 fclose(fp); 01637 return 1; 01638 } 01639 01640 return 0; 01641 } 01642 01648 object *find_skill(object *op, int skillnr) 01649 { 01650 object *tmp; 01651 01652 for (tmp = op->inv; tmp; tmp = tmp->below) 01653 { 01654 if (tmp->type == SKILL && tmp->stats.sp == skillnr) 01655 { 01656 return tmp; 01657 } 01658 } 01659 01660 return NULL; 01661 } 01662 01668 int player_can_carry(object *pl, uint32 weight) 01669 { 01670 uint32 effective_weight_limit; 01671 01672 if (pl->stats.Str <= MAX_STAT) 01673 { 01674 effective_weight_limit = weight_limit[pl->stats.Str]; 01675 } 01676 else 01677 { 01678 effective_weight_limit = weight_limit[MAX_STAT]; 01679 } 01680 01681 return (pl->carrying + weight) < effective_weight_limit; 01682 } 01683 01690 char *player_get_race_class(object *op, char *buf, size_t size) 01691 { 01692 strncpy(buf, op->race, size - 1); 01693 01694 if (CONTR(op)->class_ob) 01695 { 01696 shstr *name_female; 01697 01698 strncat(buf, " ", size - strlen(buf) - 1); 01699 01700 if (object_get_gender(op) == GENDER_FEMALE && (name_female = object_get_value(CONTR(op)->class_ob, "name_female"))) 01701 { 01702 strncat(buf, name_female, size - strlen(buf) - 1); 01703 } 01704 else 01705 { 01706 strncat(buf, CONTR(op)->class_ob->name, size - strlen(buf) - 1); 01707 } 01708 } 01709 01710 return buf; 01711 } 01712 01719 void player_path_add(player *pl, mapstruct *map, sint16 x, sint16 y) 01720 { 01721 player_path *path = malloc(sizeof(player_path)); 01722 01723 /* Initialize the values. */ 01724 path->map = map; 01725 path->x = x; 01726 path->y = y; 01727 path->next = NULL; 01728 path->fails = 0; 01729 01730 if (!pl->move_path) 01731 { 01732 pl->move_path = pl->move_path_end = path; 01733 } 01734 else 01735 { 01736 pl->move_path_end->next = path; 01737 pl->move_path_end = path; 01738 } 01739 } 01740 01744 void player_path_clear(player *pl) 01745 { 01746 player_path *path, *next; 01747 01748 if (!pl->move_path) 01749 { 01750 return; 01751 } 01752 01753 for (path = pl->move_path; path; path = next) 01754 { 01755 next = path->next; 01756 free(path); 01757 } 01758 01759 pl->move_path = NULL; 01760 pl->move_path_end = NULL; 01761 } 01762 01766 void player_path_handle(player *pl) 01767 { 01768 while (pl->ob->speed_left >= 0.0f && pl->move_path) 01769 { 01770 player_path *tmp = pl->move_path; 01771 rv_vector rv; 01772 01773 /* Make sure the map exists and is loaded, then get the range vector. */ 01774 if (!tmp->map || tmp->map->in_memory != MAP_IN_MEMORY || !get_rangevector_from_mapcoords(pl->ob->map, pl->ob->x, pl->ob->y, tmp->map, tmp->x, tmp->y, &rv, 0)) 01775 { 01776 /* Something went wrong (map not loaded or we got teleported 01777 * somewhere), clear all queued paths. */ 01778 player_path_clear(pl); 01779 return; 01780 } 01781 else 01782 { 01783 int success = 0, dir = rv.direction; 01784 01785 /* Can the player move there directly? */ 01786 if (move_player(pl->ob, dir)) 01787 { 01788 success = 1; 01789 } 01790 else 01791 { 01792 int diff; 01793 01794 /* Try to move around corners otherwise. */ 01795 for (diff = 1; diff <= 2; diff++) 01796 { 01797 /* Try left or right first? */ 01798 int m = 1 - (RANDOM() & 2); 01799 01800 if (move_player(pl->ob, absdir(dir + diff * m)) || move_player(pl->ob, absdir(dir - diff * m))) 01801 { 01802 success = 1; 01803 break; 01804 } 01805 } 01806 } 01807 01808 /* See if we succeeded in moving where we wanted. */ 01809 if (pl->ob->map == tmp->map && pl->ob->x == tmp->x && pl->ob->y == tmp->y) 01810 { 01811 pl->move_path = tmp->next; 01812 free(tmp); 01813 } 01814 /* Clear all paths if we above check failed: this can happen 01815 * if we got teleported somewhere else by a teleporter or a 01816 * shop mat, in which case the player most likely doesn't want 01817 * to move to the original destination. Also see if we failed 01818 * to move to destination too many times already. */ 01819 else if ((rv.distance <= 1 && success) || tmp->fails > PLAYER_PATH_MAX_FAILS) 01820 { 01821 player_path_clear(pl); 01822 return; 01823 } 01824 /* Not any of the above; we failed to move where we wanted. */ 01825 else 01826 { 01827 tmp->fails++; 01828 } 01829 01830 pl->ob->speed_left--; 01831 } 01832 } 01833 } 01834 01840 sint64 player_faction_reputation(player *pl, shstr *faction) 01841 { 01842 int i; 01843 01844 for (i = 0; i < pl->num_faction_ids; i++) 01845 { 01846 if (pl->faction_ids[i] == faction) 01847 { 01848 return pl->faction_reputation[i]; 01849 } 01850 } 01851 01852 return 0; 01853 } 01854 01861 void player_faction_reputation_update(player *pl, shstr *faction, sint64 add) 01862 { 01863 int i; 01864 01865 for (i = 0; i < pl->num_faction_ids; i++) 01866 { 01867 if (pl->faction_ids[i] == faction) 01868 { 01869 pl->faction_reputation[i] += add; 01870 return; 01871 } 01872 } 01873 01874 pl->faction_ids = realloc(pl->faction_ids, sizeof(*pl->faction_ids) * (pl->num_faction_ids + 1)); 01875 pl->faction_reputation = realloc(pl->faction_reputation, sizeof(*pl->faction_reputation) * (pl->num_faction_ids + 1)); 01876 pl->faction_ids[pl->num_faction_ids] = add_string(faction); 01877 pl->faction_reputation[pl->num_faction_ids] = add; 01878 pl->num_faction_ids++; 01879 }
1.7.4