|
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 00036 #include <global.h> 00037 00038 #define GET_CLIENT_FLAGS(_O_) ((_O_)->flags[0] & 0x7f) 00039 #define NO_FACE_SEND (-1) 00040 00046 static void parse_srv_setup(char *param, char *cmdback, int type) 00047 { 00048 char *cp; 00049 size_t x = 0; 00050 unsigned long y = 0; 00051 00052 /* is x our files len and y the crc */ 00053 for (cp = param; *cp != '\0'; cp++) 00054 { 00055 if (*cp == '|') 00056 { 00057 *cp = '\0'; 00058 x = atoi(param); 00059 y = strtoul(cp + 1, NULL, 16); 00060 break; 00061 } 00062 } 00063 00064 if (SrvClientFiles[type].len_ucomp != x || SrvClientFiles[type].crc != y) 00065 { 00066 char tmpbuf[MAX_BUF]; 00067 00068 snprintf(tmpbuf, sizeof(tmpbuf), "%"FMT64U"|%lx", (uint64) SrvClientFiles[type].len_ucomp, SrvClientFiles[type].crc); 00069 strcat(cmdback, tmpbuf); 00070 } 00071 else 00072 { 00073 strcat(cmdback, "OK"); 00074 } 00075 } 00076 00085 void SetUp(char *buf, int len, socket_struct *ns) 00086 { 00087 int s; 00088 char *cmd, *param, tmpbuf[MAX_BUF], cmdback[HUGE_BUF]; 00089 00090 if (!buf || !len) 00091 { 00092 return; 00093 } 00094 00095 LOG(llevInfo, "Get SetupCmd:: %s\n", buf); 00096 cmdback[0] = BINARY_CMD_SETUP; 00097 cmdback[1] = 0; 00098 00099 for (s = 0; s < len; ) 00100 { 00101 cmd = &buf[s]; 00102 00103 /* Find the next space, and put a null there */ 00104 for (; s < len && buf[s] && buf[s] != ' '; s++) 00105 { 00106 } 00107 00108 buf[s++] = 0; 00109 00110 while (s < len && buf[s] == ' ') 00111 { 00112 s++; 00113 } 00114 00115 if (s >= len) 00116 { 00117 break; 00118 } 00119 00120 param = &buf[s]; 00121 00122 for (;s < len && buf[s] && buf[s] != ' '; s++) 00123 { 00124 } 00125 00126 buf[s++] = 0; 00127 00128 while (s < len && buf[s] == ' ') 00129 { 00130 s++; 00131 } 00132 00133 strcat(cmdback, " "); 00134 strcat(cmdback, cmd); 00135 strcat(cmdback, " "); 00136 00137 if (!strcmp(cmd, "sound")) 00138 { 00139 ns->sound = atoi(param); 00140 strcat(cmdback, param); 00141 } 00142 else if (!strcmp(cmd, "faceset")) 00143 { 00144 int q = atoi(param); 00145 00146 if (is_valid_faceset(q)) 00147 { 00148 ns->faceset = q; 00149 } 00150 00151 snprintf(tmpbuf, sizeof(tmpbuf), "%d", ns->faceset); 00152 strcat(cmdback, tmpbuf); 00153 } 00154 else if (!strcmp(cmd, "mapsize")) 00155 { 00156 int x, y = 0; 00157 char *cp; 00158 00159 x = atoi(param); 00160 00161 for (cp = param; *cp != 0; cp++) 00162 { 00163 if (*cp == 'x' || *cp == 'X') 00164 { 00165 y = atoi(cp + 1); 00166 00167 break; 00168 } 00169 } 00170 00171 if (x < 9 || y < 9 || x > MAP_CLIENT_X || y > MAP_CLIENT_Y) 00172 { 00173 snprintf(tmpbuf, sizeof(tmpbuf), " %dx%d", MAP_CLIENT_X, MAP_CLIENT_Y); 00174 strcat(cmdback, tmpbuf); 00175 } 00176 else 00177 { 00178 ns->mapx = x; 00179 ns->mapy = y; 00180 ns->mapx_2 = x / 2; 00181 ns->mapy_2 = y / 2; 00182 00183 /* better to send back what we are really using and not 00184 * the param as given to us in case it gets parsed 00185 * differently. */ 00186 snprintf(tmpbuf, sizeof(tmpbuf), "%dx%d", x, y); 00187 strcat(cmdback, tmpbuf); 00188 } 00189 } 00190 else if (!strcmp(cmd, "skf")) 00191 { 00192 parse_srv_setup(param, cmdback, SRV_CLIENT_SKILLS); 00193 } 00194 else if (!strcmp(cmd, "spf")) 00195 { 00196 parse_srv_setup(param, cmdback, SRV_CLIENT_SPELLS); 00197 } 00198 else if (!strcmp(cmd, "spfv2")) 00199 { 00200 parse_srv_setup(param, cmdback, SRV_FILE_SPELLS_V2); 00201 } 00202 else if (!strcmp(cmd, "skfv2")) 00203 { 00204 parse_srv_setup(param, cmdback, SRV_CLIENT_SKILLS_V2); 00205 } 00206 else if (!strcmp(cmd, "stf")) 00207 { 00208 parse_srv_setup(param, cmdback, SRV_CLIENT_SETTINGS); 00209 } 00210 else if (!strcmp(cmd, "bpf")) 00211 { 00212 parse_srv_setup(param, cmdback, SRV_CLIENT_BMAPS); 00213 } 00214 else if (!strcmp(cmd, "amf")) 00215 { 00216 parse_srv_setup(param, cmdback, SRV_CLIENT_ANIMS); 00217 } 00218 else if (!strcmp(cmd, "amfv2")) 00219 { 00220 parse_srv_setup(param, cmdback, SRV_CLIENT_ANIMS_V2); 00221 } 00222 else if (!strcmp(cmd, "hpf")) 00223 { 00224 parse_srv_setup(param, cmdback, SRV_CLIENT_HFILES); 00225 } 00226 else if (!strcmp(cmd, "upf")) 00227 { 00228 parse_srv_setup(param, cmdback, SRV_FILE_UPDATES); 00229 } 00230 else if (!strcmp(cmd, "ssf")) 00231 { 00232 parse_srv_setup(param, cmdback, SRV_SERVER_SETTINGS); 00233 } 00234 else if (!strcmp(cmd, "eff")) 00235 { 00236 parse_srv_setup(param, cmdback, SRV_CLIENT_EFFECTS); 00237 } 00238 else if (!strcmp(cmd, "bot")) 00239 { 00240 int is_bot = atoi(param); 00241 00242 if (is_bot != 0 && is_bot != 1) 00243 { 00244 strcat(cmdback, "FALSE"); 00245 } 00246 else 00247 { 00248 ns->is_bot = is_bot; 00249 snprintf(tmpbuf, sizeof(tmpbuf), "%d", is_bot); 00250 strcat(cmdback, tmpbuf); 00251 } 00252 } 00253 else 00254 { 00255 /* Didn't get a setup command we understood - report a 00256 * failure to the client. */ 00257 strcat(cmdback, "FALSE"); 00258 } 00259 } 00260 00261 Write_String_To_Socket(ns, BINARY_CMD_SETUP, cmdback, strlen(cmdback)); 00262 } 00263 00269 void AddMeCmd(char *buf, int len, socket_struct *ns) 00270 { 00271 Settings oldsettings; 00272 char cmd_buf[2] = "X"; 00273 oldsettings = settings; 00274 00275 (void) buf; 00276 (void) len; 00277 00278 if (ns->status != Ns_Add || add_player(ns)) 00279 { 00280 Write_String_To_Socket(ns, BINARY_CMD_ADDME_FAIL, cmd_buf, 1); 00281 ns->status = Ns_Dead; 00282 } 00283 else 00284 { 00285 /* Basically, the add_player copies the socket structure into 00286 * the player structure, so this one (which is from init_sockets) 00287 * is not needed anymore. */ 00288 ns->addme = 1; 00289 /* Reset idle counter */ 00290 ns->login_count = 0; 00291 ns->keepalive = 0; 00292 socket_info.nconns--; 00293 ns->status = Ns_Avail; 00294 } 00295 00296 settings = oldsettings; 00297 } 00298 00305 void PlayerCmd(char *buf, int len, player *pl) 00306 { 00307 char command[MAX_BUF]; 00308 00309 if (!buf || len < 1) 00310 { 00311 return; 00312 } 00313 00314 if (len >= MAX_BUF) 00315 { 00316 len = MAX_BUF - 1; 00317 } 00318 00319 strncpy(command, buf, len); 00320 command[len] = '\0'; 00321 00322 /* The following should never happen with a proper or honest client. 00323 * Therefore, the error message doesn't have to be too clear - if 00324 * someone is playing with a hacked/non working client, this gives 00325 * them an idea of the problem, but they deserve what they get. */ 00326 if (pl->state != ST_PLAYING) 00327 { 00328 new_draw_info_format(0, COLOR_WHITE, pl->ob, "You can not issue commands - state is not ST_PLAYING (%s)", buf); 00329 return; 00330 } 00331 00332 execute_newserver_command(pl->ob, command); 00333 } 00334 00337 void ReplyCmd(char *buf, int len, player *pl) 00338 { 00339 (void) len; 00340 00341 if (!buf || pl->socket.status == Ns_Dead) 00342 { 00343 return; 00344 } 00345 00346 strcpy(pl->write_buf, ":"); 00347 strncat(pl->write_buf, buf, 250); 00348 pl->write_buf[250] = 0; 00349 pl->socket.ext_title_flag = 1; 00350 00351 switch (pl->state) 00352 { 00353 case ST_PLAYING: 00354 pl->socket.status = Ns_Dead; 00355 LOG(llevBug, "Got reply message with ST_PLAYING input state (player %s)\n", query_name(pl->ob, NULL)); 00356 break; 00357 00358 case ST_GET_NAME: 00359 receive_player_name(pl->ob); 00360 break; 00361 00362 case ST_GET_PASSWORD: 00363 case ST_CONFIRM_PASSWORD: 00364 receive_player_password(pl->ob); 00365 break; 00366 00367 default: 00368 pl->socket.status = Ns_Dead; 00369 LOG(llevBug, "Unknown input state: %d\n", pl->state); 00370 break; 00371 } 00372 } 00373 00377 static void version_mismatch_msg(socket_struct *ns) 00378 { 00379 send_socket_message(COLOR_RED, ns, "This is Atrinik Server.\nYour client version is outdated!\nGo to http://www.atrinik.org and download the latest Atrinik client!\nGoodbye."); 00380 } 00381 00384 void RequestFileCmd(char *buf, int len, socket_struct *ns) 00385 { 00386 int id; 00387 00388 /* *only* allow this command between the first login and the "addme" command! */ 00389 if (ns->status != Ns_Add || !buf || !len) 00390 { 00391 ns->status = Ns_Dead; 00392 return; 00393 } 00394 00395 id = atoi(buf); 00396 00397 if (id < 0 || id >= SRV_CLIENT_FILES) 00398 { 00399 ns->status = Ns_Dead; 00400 return; 00401 } 00402 00403 switch (id) 00404 { 00405 case SRV_CLIENT_SKILLS: 00406 case SRV_CLIENT_SKILLS_V2: 00407 if (ns->rf_skills) 00408 { 00409 ns->status = Ns_Dead; 00410 return; 00411 } 00412 else 00413 { 00414 ns->rf_skills = 1; 00415 } 00416 00417 break; 00418 00419 case SRV_CLIENT_SPELLS: 00420 case SRV_FILE_SPELLS_V2: 00421 if (ns->rf_spells) 00422 { 00423 ns->status = Ns_Dead; 00424 return; 00425 } 00426 else 00427 { 00428 ns->rf_spells = 1; 00429 } 00430 00431 break; 00432 00433 case SRV_CLIENT_SETTINGS: 00434 case SRV_SERVER_SETTINGS: 00435 if (ns->rf_settings) 00436 { 00437 ns->status = Ns_Dead; 00438 return; 00439 } 00440 else 00441 { 00442 ns->rf_settings = 1; 00443 } 00444 00445 break; 00446 00447 case SRV_CLIENT_BMAPS: 00448 if (ns->rf_bmaps) 00449 { 00450 ns->status = Ns_Dead; 00451 return; 00452 } 00453 else 00454 { 00455 ns->rf_bmaps = 1; 00456 } 00457 00458 break; 00459 00460 case SRV_CLIENT_ANIMS: 00461 case SRV_CLIENT_ANIMS_V2: 00462 if (ns->rf_anims) 00463 { 00464 ns->status = Ns_Dead; 00465 return; 00466 } 00467 else 00468 { 00469 ns->rf_anims = 1; 00470 } 00471 00472 break; 00473 00474 case SRV_CLIENT_HFILES: 00475 if (ns->rf_hfiles) 00476 { 00477 ns->status = Ns_Dead; 00478 return; 00479 } 00480 else 00481 { 00482 ns->rf_hfiles = 1; 00483 } 00484 00485 break; 00486 } 00487 00488 LOG(llevDebug, "Client %s rf #%d\n", ns->host, id); 00489 send_srv_file(ns, id); 00490 } 00491 00494 void VersionCmd(char *buf, int len, socket_struct *ns) 00495 { 00496 char *cp; 00497 00498 if (!buf || !len || ns->version) 00499 { 00500 version_mismatch_msg(ns); 00501 ns->status = Ns_Dead; 00502 return; 00503 } 00504 00505 ns->version = 1; 00506 ns->socket_version = atoi(buf); 00507 cp = strchr(buf + 1, ' '); 00508 00509 if (!cp) 00510 { 00511 version_mismatch_msg(ns); 00512 LOG(llevDebug, "VersionCmd(): Connection from false client (invalid name)\n"); 00513 ns->status = Ns_Zombie; 00514 return; 00515 } 00516 00517 if (ns->socket_version == 991017 || ns->socket_version < 1045) 00518 { 00519 version_mismatch_msg(ns); 00520 ns->status = Ns_Zombie; 00521 return; 00522 } 00523 00524 if (ns->socket_version > SOCKET_VERSION) 00525 { 00526 send_socket_message(COLOR_RED, ns, "This Atrinik server is outdated and incompatible with your client's version. Try another server."); 00527 ns->status = Ns_Zombie; 00528 return; 00529 } 00530 } 00531 00540 void MoveCmd(char *buf, int len, player *pl) 00541 { 00542 int vals[3]; 00543 00544 if (!buf || !len) 00545 { 00546 return; 00547 } 00548 00549 if (sscanf(buf, "%d %d %d", &vals[0], &vals[1], &vals[2]) != 3) 00550 { 00551 return; 00552 } 00553 00554 esrv_move_object(pl->ob, vals[0], vals[1], vals[2]); 00555 } 00556 00562 void send_query(socket_struct *ns, uint8 flags, char *text) 00563 { 00564 char buf[MAX_BUF]; 00565 00566 snprintf(buf, sizeof(buf), "X%d %s", flags, text ? text : ""); 00567 00568 Write_String_To_Socket(ns, BINARY_CMD_QUERY, buf, strlen(buf)); 00569 } 00570 00571 #define AddIfInt(Old, New, Type) \ 00572 if (Old != New) \ 00573 { \ 00574 Old = New; \ 00575 SockList_AddChar(&sl, (char) (Type)); \ 00576 SockList_AddInt(&sl, (uint32) (New)); \ 00577 } 00578 00579 #define AddIfInt64(Old, New, Type) \ 00580 if (Old != New) \ 00581 { \ 00582 Old = New; \ 00583 SockList_AddChar(&sl, Type); \ 00584 SockList_AddInt64(&sl, New); \ 00585 } 00586 00587 #define AddIfShort(Old, New, Type) \ 00588 if (Old != New) \ 00589 { \ 00590 Old = New; \ 00591 SockList_AddChar(&sl, (char) (Type)); \ 00592 SockList_AddShort(&sl, (uint16) (New)); \ 00593 } 00594 00595 #define AddIfChar(Old, New, Type) \ 00596 if (Old != New) \ 00597 { \ 00598 Old = New; \ 00599 SockList_AddChar(&sl, (char) (Type)); \ 00600 SockList_AddChar(&sl, (char) (New)); \ 00601 } 00602 00603 #define AddIfFloat(Old, New, Type) \ 00604 if (Old != New) \ 00605 { \ 00606 Old = New; \ 00607 SockList_AddChar(&sl, (char) Type); \ 00608 SockList_AddInt(&sl, (long) (New * FLOAT_MULTI)); \ 00609 } 00610 00611 #define AddIfString(Old, New, Type) \ 00612 if (Old == NULL || strcmp(Old, New)) \ 00613 { \ 00614 if (Old) \ 00615 { \ 00616 free(Old); \ 00617 } \ 00618 \ 00619 Old = strdup_local(New); \ 00620 SockList_AddChar(&sl, (char) Type); \ 00621 SockList_AddChar(&sl, (char) strlen(New)); \ 00622 strcpy((char *) sl.buf + sl.len, New); \ 00623 sl.len += strlen(New); \ 00624 } 00625 00632 void add_skill_to_skilllist(object *skill, StringBuffer *sb) 00633 { 00634 /* Normal skills */ 00635 if (skill->last_eat == 1) 00636 { 00637 stringbuffer_append_printf(sb, "/%s|%d|%"FMT64, skill->name, skill->level, skill->stats.exp); 00638 } 00639 /* 'Buy level' skills */ 00640 else if (skill->last_eat == 2) 00641 { 00642 stringbuffer_append_printf(sb, "/%s|%d|-2", skill->name, skill->level); 00643 } 00644 /* No level skills */ 00645 else 00646 { 00647 stringbuffer_append_printf(sb, "/%s|%d|-1", skill->name, skill->level); 00648 } 00649 } 00650 00654 void esrv_update_skills(player *pl) 00655 { 00656 int i; 00657 StringBuffer *sb = stringbuffer_new(); 00658 char *cp; 00659 size_t cp_len; 00660 00661 stringbuffer_append_printf(sb, "X%d ", SPLIST_MODE_UPDATE); 00662 00663 for (i = 0; i < NROFSKILLS; i++) 00664 { 00665 if (pl->skill_ptr[i] && pl->skill_ptr[i]->last_eat) 00666 { 00667 object *tmp = pl->skill_ptr[i]; 00668 00669 /* Send only when something has changed */ 00670 if (tmp->stats.exp != pl->skill_exp[i] || tmp->level != pl->skill_level[i]) 00671 { 00672 add_skill_to_skilllist(tmp, sb); 00673 pl->skill_exp[i] = tmp->stats.exp; 00674 pl->skill_level[i] = tmp->level; 00675 } 00676 } 00677 } 00678 00679 cp_len = sb->pos; 00680 cp = stringbuffer_finish(sb); 00681 Write_String_To_Socket(&pl->socket, BINARY_CMD_SKILL_LIST, cp, cp_len); 00682 free(cp); 00683 } 00684 00694 void esrv_update_stats(player *pl) 00695 { 00696 static char sock_buf[HUGE_BUF]; 00697 SockList sl; 00698 int i; 00699 uint16 flags; 00700 00701 sl.buf = (unsigned char *) sock_buf; 00702 SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_STATS); 00703 00704 /* small trick: we want send the hp bar of our target to the player. 00705 * We want send a char with x% the target has of full hp. 00706 * To avoid EVERY time the % calculation, we store the real HP 00707 * - if it has changed, we calc the % and use them normal. 00708 * this simple compare will not deal in speed but we safe 00709 * some unneeded calculations. */ 00710 00711 /* never send our own status - client will sort this out */ 00712 if (pl->target_object != pl->ob) 00713 { 00714 /* we don't care about count - target function will readjust itself */ 00715 if (pl->target_object && pl->target_object->stats.hp != pl->target_hp) 00716 { 00717 char hp = (char) MAX(1, (((float) pl->target_object->stats.hp / (float) pl->target_object->stats.maxhp) * 100.0f)); 00718 00719 pl->target_hp = pl->target_object->stats.hp; 00720 AddIfChar(pl->target_hp_p, hp, CS_STAT_TARGET_HP); 00721 } 00722 } 00723 00724 AddIfShort(pl->last_gen_hp, pl->gen_client_hp, CS_STAT_REG_HP); 00725 AddIfShort(pl->last_gen_sp, pl->gen_client_sp, CS_STAT_REG_MANA); 00726 AddIfShort(pl->last_gen_grace,pl->gen_client_grace, CS_STAT_REG_GRACE); 00727 AddIfChar(pl->last_level, pl->ob->level, CS_STAT_LEVEL); 00728 AddIfFloat(pl->last_speed, pl->ob->speed, CS_STAT_SPEED); 00729 AddIfInt(pl->last_weight_limit, weight_limit[pl->ob->stats.Str], CS_STAT_WEIGHT_LIM); 00730 AddIfChar(pl->last_weapon_sp, pl->weapon_sp, CS_STAT_WEAP_SP); 00731 00732 if (pl->ob) 00733 { 00734 object *arrow; 00735 00736 AddIfInt(pl->last_stats.hp, pl->ob->stats.hp, CS_STAT_HP); 00737 AddIfInt(pl->last_stats.maxhp, pl->ob->stats.maxhp, CS_STAT_MAXHP); 00738 AddIfShort(pl->last_stats.sp, pl->ob->stats.sp, CS_STAT_SP); 00739 AddIfShort(pl->last_stats.maxsp, pl->ob->stats.maxsp, CS_STAT_MAXSP); 00740 AddIfShort(pl->last_stats.grace, pl->ob->stats.grace, CS_STAT_GRACE); 00741 AddIfShort(pl->last_stats.maxgrace, pl->ob->stats.maxgrace, CS_STAT_MAXGRACE); 00742 AddIfChar(pl->last_stats.Str, pl->ob->stats.Str, CS_STAT_STR); 00743 AddIfChar(pl->last_stats.Int, pl->ob->stats.Int, CS_STAT_INT); 00744 AddIfChar(pl->last_stats.Pow, pl->ob->stats.Pow, CS_STAT_POW); 00745 AddIfChar(pl->last_stats.Wis, pl->ob->stats.Wis, CS_STAT_WIS); 00746 AddIfChar(pl->last_stats.Dex, pl->ob->stats.Dex, CS_STAT_DEX); 00747 AddIfChar(pl->last_stats.Con, pl->ob->stats.Con, CS_STAT_CON); 00748 AddIfChar(pl->last_stats.Cha, pl->ob->stats.Cha, CS_STAT_CHA); 00749 AddIfInt64(pl->last_stats.exp, pl->ob->stats.exp, CS_STAT_EXP); 00750 AddIfShort(pl->last_stats.wc, pl->ob->stats.wc, CS_STAT_WC); 00751 AddIfShort(pl->last_stats.ac, pl->ob->stats.ac, CS_STAT_AC); 00752 AddIfShort(pl->last_stats.dam, pl->client_dam, CS_STAT_DAM); 00753 AddIfShort(pl->last_stats.food, pl->ob->stats.food, CS_STAT_FOOD); 00754 AddIfInt(pl->last_action_timer, pl->action_timer, CS_STAT_ACTION_TIME); 00755 00756 if (pl->socket.socket_version >= 1050) 00757 { 00758 if (pl->equipment[PLAYER_EQUIP_BOW] && (arrow = arrow_find(pl->ob, pl->equipment[PLAYER_EQUIP_BOW]->race, -1))) 00759 { 00760 AddIfShort(pl->last_ranged_dam, arrow_get_damage(pl->ob, pl->equipment[PLAYER_EQUIP_BOW], arrow), CS_STAT_RANGED_DAM); 00761 AddIfShort(pl->last_ranged_wc, arrow_get_wc(pl->ob, pl->equipment[PLAYER_EQUIP_BOW], arrow), CS_STAT_RANGED_WC); 00762 AddIfInt(pl->last_ranged_ws, bow_get_ws(pl->equipment[PLAYER_EQUIP_BOW], arrow), CS_STAT_RANGED_WS); 00763 } 00764 else 00765 { 00766 AddIfShort(pl->last_ranged_dam, 0, CS_STAT_RANGED_DAM); 00767 AddIfShort(pl->last_ranged_wc, 0, CS_STAT_RANGED_WC); 00768 AddIfInt(pl->last_ranged_ws, 0, CS_STAT_RANGED_WS); 00769 } 00770 } 00771 } 00772 00773 for (i = 0; i < pl->last_skill_index; i++) 00774 { 00775 AddIfInt64(pl->last_skill_exp[i], pl->last_skill_ob[i]->stats.exp, pl->last_skill_id[i]); 00776 AddIfChar(pl->last_skill_level[i], (pl->last_skill_ob[i]->level), pl->last_skill_id[i] + 1); 00777 } 00778 00779 flags = 0; 00780 00781 /* TODO: remove fire and run server sided mode */ 00782 if (pl->fire_on) 00783 { 00784 flags |= SF_FIREON; 00785 } 00786 00787 if (pl->run_on) 00788 { 00789 flags |= SF_RUNON; 00790 } 00791 00792 /* we add additional player status flags - in old style, you got a msg 00793 * in the text windows when you get xray of get blinded - we will skip 00794 * this and add the info here, so the client can track it down and make 00795 * it the user visible in it own, server independent way. */ 00796 00797 /* player is blind */ 00798 if (QUERY_FLAG(pl->ob, FLAG_BLIND)) 00799 { 00800 flags |= SF_BLIND; 00801 } 00802 00803 /* player has xray */ 00804 if (QUERY_FLAG(pl->ob, FLAG_XRAYS)) 00805 { 00806 flags |= SF_XRAYS; 00807 } 00808 00809 /* player has infravision */ 00810 if (QUERY_FLAG(pl->ob, FLAG_SEE_IN_DARK)) 00811 { 00812 flags |= SF_INFRAVISION; 00813 } 00814 00815 AddIfShort(pl->last_flags, flags, CS_STAT_FLAGS); 00816 00817 for (i = 0; i < NROFATTACKS; i++) 00818 { 00819 /* If there are more attacks, but we reached CS_STAT_PROT_END, 00820 * we stop now. */ 00821 if (CS_STAT_PROT_START + i > CS_STAT_PROT_END) 00822 { 00823 break; 00824 } 00825 00826 AddIfChar(pl->last_protection[i], pl->ob->protection[i], CS_STAT_PROT_START + i); 00827 } 00828 00829 if (pl->socket.ext_title_flag) 00830 { 00831 generate_ext_title(pl); 00832 SockList_AddChar(&sl, (char) CS_STAT_EXT_TITLE); 00833 i = (int) strlen(pl->ext_title); 00834 SockList_AddChar(&sl, (char) i); 00835 strcpy((char *) sl.buf + sl.len, pl->ext_title); 00836 sl.len += i; 00837 pl->socket.ext_title_flag = 0; 00838 } 00839 00840 /* Only send it away if we have some actual data */ 00841 if (sl.len > 1) 00842 { 00843 Send_With_Handling(&pl->socket, &sl); 00844 } 00845 } 00846 00849 void esrv_new_player(player *pl, uint32 weight) 00850 { 00851 SockList sl; 00852 00853 sl.buf = malloc(MAXSOCKBUF); 00854 00855 SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_PLAYER); 00856 SockList_AddInt(&sl, pl->ob->count); 00857 SockList_AddInt(&sl, weight); 00858 SockList_AddInt(&sl, pl->ob->face->number); 00859 00860 SockList_AddChar(&sl, (char) strlen(pl->ob->name)); 00861 strcpy((char *) sl.buf + sl.len, pl->ob->name); 00862 sl.len += strlen(pl->ob->name); 00863 00864 Send_With_Handling(&pl->socket, &sl); 00865 free(sl.buf); 00866 } 00867 00869 #define map_clearcell(_cell_) \ 00870 memset((_cell_), 0, sizeof(MapCell)); \ 00871 (_cell_)->count = -1 00872 00878 static inline int get_tiled_map_id(player *pl, struct mapdef *map) 00879 { 00880 int i; 00881 00882 if (!pl->last_update) 00883 { 00884 return 0; 00885 } 00886 00887 for (i = 0; i < TILED_MAPS; i++) 00888 { 00889 if (pl->last_update->tile_path[i] == map->path) 00890 { 00891 return i+1; 00892 } 00893 } 00894 00895 return 0; 00896 } 00897 00903 static inline void copy_lastmap(socket_struct *ns, int dx, int dy) 00904 { 00905 struct Map newmap; 00906 int x, y; 00907 00908 for (x = 0; x < ns->mapx; x++) 00909 { 00910 for (y = 0; y < ns->mapy; y++) 00911 { 00912 if (x + dx < 0 || x + dx >= ns->mapx || y + dy < 0 || y + dy >= ns->mapy) 00913 { 00914 memset(&(newmap.cells[x][y]), 0, sizeof(MapCell)); 00915 continue; 00916 } 00917 00918 memcpy(&(newmap.cells[x][y]), &(ns->lastmap.cells[x + dx][y + dy]), sizeof(MapCell)); 00919 } 00920 } 00921 00922 memcpy(&(ns->lastmap), &newmap, sizeof(struct Map)); 00923 } 00924 00928 void draw_client_map(object *pl) 00929 { 00930 int redraw_below = 0; 00931 00932 if (pl->type != PLAYER) 00933 { 00934 LOG(llevBug, "draw_client_map(): Called with non-player: %s\n", pl->name); 00935 return; 00936 } 00937 00938 /* IF player is just joining the game, he isn't on a map, 00939 * If so, don't try to send them a map. All will 00940 * be OK once they really log in. */ 00941 if (!pl->map || pl->map->in_memory != MAP_IN_MEMORY) 00942 { 00943 return; 00944 } 00945 00946 CONTR(pl)->map_update_cmd = MAP_UPDATE_CMD_SAME; 00947 00948 /* If we changed somewhere the map, prepare map data */ 00949 if (CONTR(pl)->last_update != pl->map) 00950 { 00951 int tile_map = get_tiled_map_id(CONTR(pl), pl->map); 00952 00953 /* Are we on a new map? */ 00954 if (!CONTR(pl)->last_update || !tile_map) 00955 { 00956 CONTR(pl)->map_update_cmd = MAP_UPDATE_CMD_NEW; 00957 memset(&(CONTR(pl)->socket.lastmap), 0, sizeof(struct Map)); 00958 CONTR(pl)->last_update = pl->map; 00959 redraw_below = 1; 00960 } 00961 else 00962 { 00963 CONTR(pl)->map_update_cmd = MAP_UPDATE_CMD_CONNECTED; 00964 CONTR(pl)->map_update_tile = tile_map; 00965 redraw_below = 1; 00966 00967 /* We have moved to a tiled map. Let's calculate the offsets. */ 00968 switch (tile_map - 1) 00969 { 00970 case 0: 00971 CONTR(pl)->map_off_x = pl->x - CONTR(pl)->map_tile_x; 00972 CONTR(pl)->map_off_y = -(CONTR(pl)->map_tile_y + (MAP_HEIGHT(pl->map) - pl->y)); 00973 break; 00974 00975 case 1: 00976 CONTR(pl)->map_off_y = pl->y - CONTR(pl)->map_tile_y; 00977 CONTR(pl)->map_off_x = (MAP_WIDTH(pl->map) - CONTR(pl)->map_tile_x) + pl->x; 00978 break; 00979 00980 case 2: 00981 CONTR(pl)->map_off_x = pl->x - CONTR(pl)->map_tile_x; 00982 CONTR(pl)->map_off_y = (MAP_HEIGHT(pl->map) - CONTR(pl)->map_tile_y) + pl->y; 00983 break; 00984 00985 case 3: 00986 CONTR(pl)->map_off_y = pl->y - CONTR(pl)->map_tile_y; 00987 CONTR(pl)->map_off_x = -(CONTR(pl)->map_tile_x + (MAP_WIDTH(pl->map) - pl->x)); 00988 break; 00989 00990 case 4: 00991 CONTR(pl)->map_off_y = -(CONTR(pl)->map_tile_y + (MAP_HEIGHT(pl->map) - pl->y)); 00992 CONTR(pl)->map_off_x = (MAP_WIDTH(pl->map) - CONTR(pl)->map_tile_x) + pl->x; 00993 break; 00994 00995 case 5: 00996 CONTR(pl)->map_off_x = (MAP_WIDTH(pl->map) - CONTR(pl)->map_tile_x) + pl->x; 00997 CONTR(pl)->map_off_y = (MAP_HEIGHT(pl->map) - CONTR(pl)->map_tile_y) + pl->y; 00998 break; 00999 01000 case 6: 01001 CONTR(pl)->map_off_y = (MAP_HEIGHT(pl->map) - CONTR(pl)->map_tile_y) + pl->y; 01002 CONTR(pl)->map_off_x = -(CONTR(pl)->map_tile_x + (MAP_WIDTH(pl->map) - pl->x)); 01003 break; 01004 01005 case 7: 01006 CONTR(pl)->map_off_x = -(CONTR(pl)->map_tile_x + (MAP_WIDTH(pl->map) - pl->x)); 01007 CONTR(pl)->map_off_y = -(CONTR(pl)->map_tile_y + (MAP_HEIGHT(pl->map) - pl->y)); 01008 break; 01009 } 01010 01011 copy_lastmap(&CONTR(pl)->socket, CONTR(pl)->map_off_x, CONTR(pl)->map_off_y); 01012 CONTR(pl)->last_update = pl->map; 01013 } 01014 } 01015 else 01016 { 01017 if (CONTR(pl)->map_tile_x != pl->x || CONTR(pl)->map_tile_y != pl->y) 01018 { 01019 copy_lastmap(&CONTR(pl)->socket, pl->x - CONTR(pl)->map_tile_x, pl->y - CONTR(pl)->map_tile_y); 01020 redraw_below = 1; 01021 } 01022 } 01023 01024 /* Redraw below window and backbuffer new positions? */ 01025 if (redraw_below) 01026 { 01027 /* Backbuffer position so we can determine whether we have moved or not */ 01028 CONTR(pl)->map_tile_x = pl->x; 01029 CONTR(pl)->map_tile_y = pl->y; 01030 CONTR(pl)->socket.below_clear = 1; 01031 /* Redraw it */ 01032 CONTR(pl)->socket.update_tile = 0; 01033 CONTR(pl)->socket.look_position = 0; 01034 } 01035 01036 /* Do LOS after calls to update_position */ 01037 if (!QUERY_FLAG(pl, FLAG_WIZ) && CONTR(pl)->update_los) 01038 { 01039 update_los(pl); 01040 CONTR(pl)->update_los = 0; 01041 } 01042 01043 draw_client_map2(pl); 01044 01045 /* If we moved on the same map, check for map name/music to update. */ 01046 if (redraw_below && CONTR(pl)->map_update_cmd == MAP_UPDATE_CMD_SAME) 01047 { 01048 SockList sl; 01049 unsigned char sock_buf[MAXSOCKBUF]; 01050 MapSpace *msp; 01051 01052 /* Newer clients support the MAPSTATS command, which is specially 01053 * for things like this. */ 01054 if (CONTR(pl)->socket.socket_version >= 1046) 01055 { 01056 sl.buf = sock_buf; 01057 SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_MAPSTATS); 01058 msp = GET_MAP_SPACE_PTR(pl->map, pl->x, pl->y); 01059 01060 /* Is there a map info object on this square? */ 01061 if (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count)) 01062 { 01063 /* Check if there is map info name, but only update if it hasn't changed. */ 01064 if (msp->map_info->race && strcmp(msp->map_info->race, CONTR(pl)->map_info_name)) 01065 { 01066 SockList_AddChar(&sl, CMD_MAPSTATS_NAME); 01067 SockList_AddMapName(&sl, pl, pl->map, msp->map_info); 01068 01069 strncpy(CONTR(pl)->map_info_name, msp->map_info->race, sizeof(CONTR(pl)->map_info_name) - 1); 01070 CONTR(pl)->map_info_name[sizeof(CONTR(pl)->map_info_name) - 1] = '\0'; 01071 } 01072 01073 /* Likewise for map info music. */ 01074 if (msp->map_info->slaying && strcmp(msp->map_info->slaying, CONTR(pl)->map_info_music)) 01075 { 01076 SockList_AddChar(&sl, CMD_MAPSTATS_MUSIC); 01077 SockList_AddMapMusic(&sl, pl, pl->map, msp->map_info); 01078 01079 strncpy(CONTR(pl)->map_info_music, msp->map_info->slaying, sizeof(CONTR(pl)->map_info_music) - 1); 01080 CONTR(pl)->map_info_music[sizeof(CONTR(pl)->map_info_music) - 1] = '\0'; 01081 } 01082 01083 /* And weather... */ 01084 if (msp->map_info->title && strcmp(msp->map_info->title, CONTR(pl)->map_info_weather)) 01085 { 01086 SockList_AddChar(&sl, CMD_MAPSTATS_WEATHER); 01087 SockList_AddMapWeather(&sl, pl, pl->map, msp->map_info); 01088 01089 strncpy(CONTR(pl)->map_info_weather, msp->map_info->title, sizeof(CONTR(pl)->map_info_weather) - 1); 01090 CONTR(pl)->map_info_weather[sizeof(CONTR(pl)->map_info_weather) - 1] = '\0'; 01091 } 01092 } 01093 /* There isn't map info object, check if we need to update previously 01094 * overriden values. */ 01095 else 01096 { 01097 /* Update map name... */ 01098 if (CONTR(pl)->map_info_name[0] != '\0') 01099 { 01100 SockList_AddChar(&sl, CMD_MAPSTATS_NAME); 01101 SockList_AddMapName(&sl, pl, pl->map, NULL); 01102 01103 CONTR(pl)->map_info_name[0] = '\0'; 01104 } 01105 01106 /* Update map music... */ 01107 if (CONTR(pl)->map_info_music[0] != '\0') 01108 { 01109 SockList_AddChar(&sl, CMD_MAPSTATS_MUSIC); 01110 SockList_AddMapMusic(&sl, pl, pl->map, NULL); 01111 01112 CONTR(pl)->map_info_music[0] = '\0'; 01113 } 01114 01115 /* Update map weather... */ 01116 if (CONTR(pl)->map_info_weather[0] != '\0') 01117 { 01118 SockList_AddChar(&sl, CMD_MAPSTATS_WEATHER); 01119 SockList_AddMapWeather(&sl, pl, pl->map, NULL); 01120 01121 CONTR(pl)->map_info_weather[0] = '\0'; 01122 } 01123 } 01124 01125 /* Anything to send? */ 01126 if (sl.len > 1) 01127 { 01128 Send_With_Handling(&CONTR(pl)->socket, &sl); 01129 } 01130 } 01131 /* Backwards compatibility... */ 01132 else if (CONTR(pl)->socket.socket_version >= 1044) 01133 { 01134 sl.buf = sock_buf; 01135 SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_MAP2); 01136 msp = GET_MAP_SPACE_PTR(pl->map, pl->x, pl->y); 01137 01138 SockList_AddChar(&sl, MAP_UPDATE_CMD_NEW); 01139 01140 if (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count)) 01141 { 01142 if ((msp->map_info->race && strcmp(msp->map_info->race, CONTR(pl)->map_info_name)) || (msp->map_info->slaying && strcmp(msp->map_info->slaying, CONTR(pl)->map_info_music))) 01143 { 01144 SockList_AddMapName(&sl, pl, pl->map, msp->map_info); 01145 SockList_AddMapMusic(&sl, pl, pl->map, msp->map_info); 01146 01147 if (msp->map_info->race) 01148 { 01149 strncpy(CONTR(pl)->map_info_name, msp->map_info->race, sizeof(CONTR(pl)->map_info_name) - 1); 01150 CONTR(pl)->map_info_name[sizeof(CONTR(pl)->map_info_name) - 1] = '\0'; 01151 } 01152 01153 if (msp->map_info->slaying) 01154 { 01155 strncpy(CONTR(pl)->map_info_music, msp->map_info->slaying, sizeof(CONTR(pl)->map_info_music) - 1); 01156 CONTR(pl)->map_info_music[sizeof(CONTR(pl)->map_info_music) - 1] = '\0'; 01157 } 01158 } 01159 } 01160 else 01161 { 01162 if (CONTR(pl)->map_info_name[0] != '\0' || CONTR(pl)->map_info_music[0] != '\0') 01163 { 01164 SockList_AddMapName(&sl, pl, pl->map, NULL); 01165 SockList_AddMapMusic(&sl, pl, pl->map, NULL); 01166 CONTR(pl)->map_info_name[0] = '\0'; 01167 CONTR(pl)->map_info_music[0] = '\0'; 01168 } 01169 } 01170 01171 SockList_AddChar(&sl, 0); 01172 SockList_AddChar(&sl, 0); 01173 SockList_AddChar(&sl, pl->x); 01174 SockList_AddChar(&sl, pl->y); 01175 01176 if (sl.len > 6) 01177 { 01178 Send_With_Handling(&CONTR(pl)->socket, &sl); 01179 } 01180 } 01181 } 01182 } 01183 01192 static const char *get_playername_color(object *pl, object *op) 01193 { 01194 if (CONTR(pl)->party != NULL && CONTR(op)->party != NULL && CONTR(pl)->party == CONTR(op)->party) 01195 { 01196 return COLOR_GREEN; 01197 } 01198 else if (pl != op && pvp_area(pl, op)) 01199 { 01200 return COLOR_RED; 01201 } 01202 01203 return COLOR_WHITE; 01204 } 01205 01207 static int darkness_table[] = {0, 10, 30, 60, 120, 260, 480, 960}; 01208 01210 void draw_client_map2(object *pl) 01211 { 01212 static uint32 map2_count = 0; 01213 MapCell *mp; 01214 MapSpace *msp; 01215 mapstruct *m; 01216 int x, y, ax, ay, d, nx, ny; 01217 int x_start, dm_light = 0; 01218 int special_vision; 01219 uint16 mask; 01220 SockList sl, sl_layer; 01221 unsigned char sock_buf[MAXSOCKBUF], sock_buf_layer[MAXSOCKBUF]; 01222 int wdark; 01223 int inv_flag = QUERY_FLAG(pl, FLAG_SEE_INVISIBLE); 01224 int layer, dark; 01225 int anim_value, anim_type, ext_flags; 01226 int num_layers; 01227 int oldlen, outdoor; 01228 object *mirror = NULL; 01229 01230 /* Do we have dm_light? */ 01231 if (CONTR(pl)->dm_light) 01232 { 01233 dm_light = global_darkness_table[MAX_DARKNESS]; 01234 } 01235 01236 wdark = darkness_table[world_darkness]; 01237 /* Any kind of special vision? */ 01238 special_vision = (QUERY_FLAG(pl, FLAG_XRAYS) ? 1 : 0) | (QUERY_FLAG(pl, FLAG_SEE_IN_DARK) ? 2 : 0); 01239 map2_count++; 01240 01241 sl.buf = sock_buf; 01242 SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_MAP2); 01243 01244 /* Marker */ 01245 SockList_AddChar(&sl, (char) CONTR(pl)->map_update_cmd); 01246 01247 if (CONTR(pl)->map_update_cmd != MAP_UPDATE_CMD_SAME) 01248 { 01249 msp = GET_MAP_SPACE_PTR(pl->map, pl->x, pl->y); 01250 01251 SockList_AddMapName(&sl, pl, pl->map, msp->map_info); 01252 SockList_AddMapMusic(&sl, pl, pl->map, msp->map_info); 01253 SockList_AddMapWeather(&sl, pl, pl->map, msp->map_info); 01254 01255 if (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count)) 01256 { 01257 if (msp->map_info->race) 01258 { 01259 strncpy(CONTR(pl)->map_info_name, msp->map_info->race, sizeof(CONTR(pl)->map_info_name) - 1); 01260 CONTR(pl)->map_info_name[sizeof(CONTR(pl)->map_info_name) - 1] = '\0'; 01261 } 01262 01263 if (msp->map_info->slaying) 01264 { 01265 strncpy(CONTR(pl)->map_info_music, msp->map_info->slaying, sizeof(CONTR(pl)->map_info_music) - 1); 01266 CONTR(pl)->map_info_music[sizeof(CONTR(pl)->map_info_music) - 1] = '\0'; 01267 } 01268 01269 if (msp->map_info->title) 01270 { 01271 strncpy(CONTR(pl)->map_info_weather, msp->map_info->title, sizeof(CONTR(pl)->map_info_weather) - 1); 01272 CONTR(pl)->map_info_weather[sizeof(CONTR(pl)->map_info_weather) - 1] = '\0'; 01273 } 01274 } 01275 01276 if (CONTR(pl)->map_update_cmd == MAP_UPDATE_CMD_CONNECTED) 01277 { 01278 SockList_AddChar(&sl, (char) CONTR(pl)->map_update_tile); 01279 SockList_AddChar(&sl, (char) CONTR(pl)->map_off_x); 01280 SockList_AddChar(&sl, (char) CONTR(pl)->map_off_y); 01281 } 01282 else 01283 { 01284 SockList_AddChar(&sl, (char) pl->map->width); 01285 SockList_AddChar(&sl, (char) pl->map->height); 01286 } 01287 } 01288 01289 SockList_AddChar(&sl, (char) pl->x); 01290 SockList_AddChar(&sl, (char) pl->y); 01291 01292 x_start = (pl->x + (CONTR(pl)->socket.mapx + 1) / 2) - 1; 01293 01294 for (ay = CONTR(pl)->socket.mapy - 1, y = (pl->y + (CONTR(pl)->socket.mapy + 1) / 2) - 1; y >= pl->y - CONTR(pl)->socket.mapy_2; y--, ay--) 01295 { 01296 ax = CONTR(pl)->socket.mapx - 1; 01297 01298 for (x = x_start; x >= pl->x - CONTR(pl)->socket.mapx_2; x--, ax--) 01299 { 01300 d = CONTR(pl)->blocked_los[ax][ay]; 01301 /* Form the data packet for x and y positions. */ 01302 mask = (ax & 0x1f) << 11 | (ay & 0x1f) << 6; 01303 01304 /* Space is out of map or blocked. Update space and clear values if needed. */ 01305 if (d & (BLOCKED_LOS_OUT_OF_MAP | BLOCKED_LOS_BLOCKED)) 01306 { 01307 if (CONTR(pl)->socket.lastmap.cells[ax][ay].count != -1) 01308 { 01309 SockList_AddShort(&sl, mask | MAP2_MASK_CLEAR); 01310 map_clearcell(&CONTR(pl)->socket.lastmap.cells[ax][ay]); 01311 } 01312 01313 continue; 01314 } 01315 01316 nx = x; 01317 ny = y; 01318 01319 if (!(m = get_map_from_coord(pl->map, &nx, &ny))) 01320 { 01321 if (!QUERY_FLAG(pl, FLAG_WIZ)) 01322 { 01323 LOG(llevDebug, "draw_client_map2() get_map_from_coord for player <%s> map: %s (%d, %d)\n", query_name(pl, NULL), pl->map->path ? pl->map->path : "<no path?>", x, y); 01324 } 01325 01326 if (CONTR(pl)->socket.lastmap.cells[ax][ay].count != -1) 01327 { 01328 SockList_AddShort(&sl, mask | MAP2_MASK_CLEAR); 01329 map_clearcell(&CONTR(pl)->socket.lastmap.cells[ax][ay]); 01330 } 01331 01332 continue; 01333 } 01334 01335 msp = GET_MAP_SPACE_PTR(m, nx, ny); 01336 01337 /* Border tile, we can ignore every LOS change */ 01338 if (!(d & BLOCKED_LOS_IGNORE)) 01339 { 01340 /* Tile has blocksview set? */ 01341 if (msp->flags & P_BLOCKSVIEW) 01342 { 01343 if (!d) 01344 { 01345 CONTR(pl)->update_los = 1; 01346 } 01347 } 01348 else 01349 { 01350 if (d & BLOCKED_LOS_BLOCKSVIEW) 01351 { 01352 CONTR(pl)->update_los = 1; 01353 } 01354 } 01355 } 01356 01357 outdoor = MAP_OUTDOORS(m) || (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count) && msp->map_info->item_power == -2); 01358 01359 /* Calculate the darkness/light value for this tile. */ 01360 if (((outdoor && !(GET_MAP_FLAGS(m, nx, ny) & P_OUTDOOR)) || (!outdoor && GET_MAP_FLAGS(m, nx, ny) & P_OUTDOOR)) && (!msp->map_info || !OBJECT_VALID(msp->map_info, msp->map_info_count) || msp->map_info->item_power < 0)) 01361 { 01362 d = msp->light_value + wdark + dm_light; 01363 } 01364 else 01365 { 01366 /* Check if map info object bound to this tile has a darkness. */ 01367 if (msp->map_info && OBJECT_VALID(msp->map_info, msp->map_info_count) && msp->map_info->item_power != -1) 01368 { 01369 int dark_value = msp->map_info->item_power; 01370 01371 if (dark_value < 0 || dark_value > MAX_DARKNESS) 01372 { 01373 dark_value = MAX_DARKNESS; 01374 } 01375 01376 d = global_darkness_table[dark_value] + msp->light_value + dm_light; 01377 } 01378 else 01379 { 01380 d = m->light_value + msp->light_value + dm_light; 01381 } 01382 } 01383 01384 if (GET_MAP_FLAGS(m, nx, ny) & P_MAGIC_MIRROR) 01385 { 01386 object *mirror_tmp; 01387 magic_mirror_struct *m_data; 01388 mapstruct *mirror_map; 01389 01390 /* Try to find the magic mirror, but only search on layer 0. */ 01391 for (mirror_tmp = GET_MAP_OB(m, nx, ny); mirror_tmp && mirror_tmp->layer == LAYER_SYS; mirror_tmp = mirror_tmp->above) 01392 { 01393 if (mirror_tmp->type == MAGIC_MIRROR) 01394 { 01395 mirror = mirror_tmp; 01396 break; 01397 } 01398 } 01399 01400 m_data = MMIRROR(mirror); 01401 01402 if (m_data && (mirror_map = magic_mirror_get_map(mirror)) && !OUT_OF_REAL_MAP(mirror_map, m_data->x, m_data->y)) 01403 { 01404 MapSpace *mirror_msp = GET_MAP_SPACE_PTR(mirror_map, m_data->x, m_data->y); 01405 01406 if ((MAP_OUTDOORS(mirror_map) && !(GET_MAP_FLAGS(mirror_map, m_data->x, m_data->y) & P_OUTDOOR)) || (!MAP_OUTDOORS(mirror_map) && GET_MAP_FLAGS(mirror_map, m_data->x, m_data->y) & P_OUTDOOR)) 01407 { 01408 d = mirror_msp->light_value + wdark + dm_light; 01409 } 01410 else 01411 { 01412 d = mirror_map->light_value + mirror_msp->light_value + dm_light; 01413 } 01414 } 01415 } 01416 01417 /* Tile is not normally visible */ 01418 if (d <= 0) 01419 { 01420 /* Xray or infravision? */ 01421 if (special_vision & 1 || (special_vision & 2 && msp->flags & (P_IS_PLAYER | P_IS_ALIVE))) 01422 { 01423 d = 100; 01424 } 01425 else 01426 { 01427 if (CONTR(pl)->socket.lastmap.cells[ax][ay].count != -1) 01428 { 01429 SockList_AddShort(&sl, mask | MAP2_MASK_CLEAR); 01430 map_clearcell(&CONTR(pl)->socket.lastmap.cells[ax][ay]); 01431 } 01432 01433 continue; 01434 } 01435 } 01436 01437 if (d > 640) 01438 { 01439 d = 210; 01440 } 01441 else if (d > 320) 01442 { 01443 d = 180; 01444 } 01445 else if (d > 160) 01446 { 01447 d = 150; 01448 } 01449 else if (d > 80) 01450 { 01451 d = 120; 01452 } 01453 else if (d > 40) 01454 { 01455 d = 90; 01456 } 01457 else if (d > 20) 01458 { 01459 d = 60; 01460 } 01461 else 01462 { 01463 d = 30; 01464 } 01465 01466 mp = &(CONTR(pl)->socket.lastmap.cells[ax][ay]); 01467 01468 /* Initialize default values for some variables. */ 01469 dark = NO_FACE_SEND; 01470 ext_flags = 0; 01471 oldlen = sl.len; 01472 anim_type = 0; 01473 anim_value = 0; 01474 01475 /* Do we need to send the darkness? */ 01476 if (mp->count != d) 01477 { 01478 mask |= MAP2_MASK_DARKNESS; 01479 dark = d; 01480 mp->count = d; 01481 } 01482 01483 /* Add the mask. Any mask changes should go above this line. */ 01484 SockList_AddShort(&sl, mask); 01485 01486 /* If we have darkness to send, send it. */ 01487 if (dark != NO_FACE_SEND) 01488 { 01489 SockList_AddChar(&sl, (char) dark); 01490 } 01491 01492 /* We will use a temporary SockList instance to add any layers we find. 01493 * If we don't find any, there is no reason to send the data about them 01494 * to the client. */ 01495 sl_layer.buf = sock_buf_layer; 01496 sl_layer.len = 0; 01497 num_layers = 0; 01498 01499 /* Go through the visible layers. */ 01500 for (layer = 0; layer < NUM_LAYERS; layer++) 01501 { 01502 object *tmp = GET_MAP_SPACE_LAYER(msp, layer); 01503 01504 /* Double check that we can actually see this object. */ 01505 if (tmp && ((QUERY_FLAG(tmp, FLAG_IS_INVISIBLE) && !inv_flag) || QUERY_FLAG(tmp, FLAG_HIDDEN))) 01506 { 01507 tmp = NULL; 01508 } 01509 01510 /* If we didn't find a layer but we can see invisible, 01511 * try in the invisible layers. */ 01512 if (!tmp && inv_flag) 01513 { 01514 tmp = GET_MAP_SPACE_LAYER(msp, layer + 7); 01515 } 01516 01517 /* This is done so that the player image is always shown 01518 * to the player, even if they are standing on top of another 01519 * player or monster. */ 01520 if (tmp && tmp->layer == LAYER_LIVING && pl->x == nx && pl->y == ny) 01521 { 01522 tmp = pl; 01523 } 01524 01525 /* Still nothing, but there's a magic mirror on this tile? */ 01526 if (!tmp && mirror) 01527 { 01528 magic_mirror_struct *m_data = MMIRROR(mirror); 01529 mapstruct *mirror_map; 01530 01531 if (m_data && (mirror_map = magic_mirror_get_map(mirror)) && !OUT_OF_REAL_MAP(mirror_map, m_data->x, m_data->y)) 01532 { 01533 tmp = GET_MAP_SPACE_LAYER(GET_MAP_SPACE_PTR(mirror_map, m_data->x, m_data->y), layer); 01534 } 01535 } 01536 01537 /* Handle objects that are shown based on their direction 01538 * and the player's position. */ 01539 if (tmp && QUERY_FLAG(tmp, FLAG_DRAW_DIRECTION)) 01540 { 01541 /* If the object is dir [0124568] and not in the top 01542 * or right quadrant or on the central square, do not 01543 * show it. */ 01544 if ((!tmp->direction || tmp->direction == NORTH || tmp->direction == NORTHEAST || tmp->direction == SOUTHEAST || tmp->direction == SOUTH || tmp->direction == SOUTHWEST || tmp->direction == NORTHWEST) && !((ax <= CONTR(pl)->socket.mapx_2) && (ay <= CONTR(pl)->socket.mapy_2)) && !((ax > CONTR(pl)->socket.mapx_2) && (ay < CONTR(pl)->socket.mapy_2))) 01545 { 01546 tmp = NULL; 01547 } 01548 /* If the object is dir [0234768] and not in the top 01549 * or left quadrant or on the central square, do not 01550 * show it. */ 01551 else if ((!tmp->direction || tmp->direction == NORTHEAST || tmp->direction == EAST || tmp->direction == SOUTHEAST || tmp->direction == SOUTHWEST || tmp->direction == WEST || tmp->direction == NORTHWEST) && !((ax <= CONTR(pl)->socket.mapx_2) && (ay <= CONTR(pl)->socket.mapy_2)) && !((ax < CONTR(pl)->socket.mapx_2) && (ay > CONTR(pl)->socket.mapy_2))) 01552 { 01553 tmp = NULL; 01554 } 01555 } 01556 01557 /* Found something. */ 01558 if (tmp) 01559 { 01560 sint16 face; 01561 uint8 quick_pos = tmp->quick_pos; 01562 uint8 flags = 0, probe_val = 0; 01563 uint32 flags2 = 0; 01564 object *head = tmp->head ? tmp->head : tmp; 01565 01566 /* If we have a multi-arch object. */ 01567 if (quick_pos) 01568 { 01569 flags |= MAP2_FLAG_MULTI; 01570 01571 /* Tail? */ 01572 if (tmp->head) 01573 { 01574 /* If true, we have sent a part of this in this map 01575 * update before, so skip it. */ 01576 if (head->update_tag == map2_count) 01577 { 01578 face = 0; 01579 } 01580 else 01581 { 01582 /* Mark this object as sent. */ 01583 head->update_tag = map2_count; 01584 face = head->face->number; 01585 } 01586 } 01587 /* Head. */ 01588 else 01589 { 01590 if (tmp->update_tag == map2_count) 01591 { 01592 face = 0; 01593 } 01594 else 01595 { 01596 tmp->update_tag = map2_count; 01597 face = tmp->face->number; 01598 } 01599 } 01600 } 01601 else 01602 { 01603 face = tmp->face->number; 01604 } 01605 01606 /* Player? So we want to send their name. */ 01607 if (tmp->type == PLAYER) 01608 { 01609 flags |= MAP2_FLAG_NAME; 01610 } 01611 01612 /* If our player has this object as their target, we want to 01613 * know its HP percent. */ 01614 if (head->count == CONTR(pl)->target_object_count) 01615 { 01616 flags |= MAP2_FLAG_PROBE; 01617 probe_val = MAX(1, ((double) head->stats.hp / ((double) head->stats.maxhp / 100.0))); 01618 } 01619 01620 /* Z position set? */ 01621 if (head->z) 01622 { 01623 flags |= MAP2_FLAG_HEIGHT; 01624 } 01625 01626 /* Check if the object has zoom, or check if the magic mirror 01627 * should affect the zoom value of this layer. */ 01628 if ((head->zoom && head->zoom != 100) || (mirror && mirror->last_heal && mirror->last_heal != 100 && mirror->path_attuned & (1U << layer))) 01629 { 01630 flags |= MAP2_FLAG_ZOOM; 01631 } 01632 01633 if (head->align || (mirror && mirror->align)) 01634 { 01635 flags |= MAP2_FLAG_ALIGN; 01636 } 01637 01638 /* Draw the object twice if set, but only if it's not 01639 * in the bottom quadrant of the map. */ 01640 if ((QUERY_FLAG(tmp, FLAG_DRAW_DOUBLE) && (ax < CONTR(pl)->socket.mapx_2 || ay < CONTR(pl)->socket.mapy_2)) || QUERY_FLAG(tmp, FLAG_DRAW_DOUBLE_ALWAYS)) 01641 { 01642 flags |= MAP2_FLAG_DOUBLE; 01643 } 01644 01645 if (head->alpha) 01646 { 01647 flags2 |= MAP2_FLAG2_ALPHA; 01648 } 01649 01650 if (head->rotate && CONTR(pl)->socket.socket_version >= 1049) 01651 { 01652 flags2 |= MAP2_FLAG2_ROTATE; 01653 } 01654 01655 if (CONTR(pl)->socket.socket_version >= 1053 && QUERY_FLAG(pl, FLAG_SEE_IN_DARK) && ((head->layer == LAYER_LIVING && d < 150) || (head->type == CONTAINER && (head->sub_type & 1) == ST1_CONTAINER_CORPSE && QUERY_FLAG(head, FLAG_IS_USED_UP) && (float) head->stats.food / head->last_eat >= CORPSE_INFRAVISION_PERCENT / 100.0))) 01656 { 01657 flags2 |= MAP2_FLAG2_INFRAVISION; 01658 } 01659 01660 if (flags2) 01661 { 01662 flags |= MAP2_FLAG_MORE; 01663 } 01664 01665 /* Damage animation? Store it for later. */ 01666 if (tmp->last_damage && tmp->damage_round_tag == ROUND_TAG) 01667 { 01668 ext_flags |= MAP2_FLAG_EXT_ANIM; 01669 anim_type = ANIM_DAMAGE; 01670 anim_value = tmp->last_damage; 01671 } 01672 01673 /* Now, check if we have cached this. */ 01674 if (mp->faces[layer] == face && mp->quick_pos[layer] == quick_pos && mp->flags[layer] == flags && mp->probe == probe_val) 01675 { 01676 continue; 01677 } 01678 01679 /* Different from cache, add it to the cache now. */ 01680 mp->faces[layer] = face; 01681 mp->quick_pos[layer] = quick_pos; 01682 mp->flags[layer] = flags; 01683 01684 if (layer == LAYER_LIVING - 1) 01685 { 01686 mp->probe = probe_val; 01687 } 01688 01689 if (OBJECT_IS_HIDDEN(pl, head)) 01690 { 01691 /* Update target if applicable. */ 01692 if (flags & MAP2_FLAG_PROBE) 01693 { 01694 CONTR(pl)->target_object = NULL; 01695 CONTR(pl)->target_object_count = 0; 01696 send_target_command(CONTR(pl)); 01697 } 01698 01699 if (mp->faces[layer]) 01700 { 01701 SockList_AddChar(&sl_layer, MAP2_LAYER_CLEAR); 01702 SockList_AddChar(&sl_layer, (char) layer + 1); 01703 num_layers++; 01704 } 01705 01706 continue; 01707 } 01708 01709 num_layers++; 01710 01711 /* Add its layer. */ 01712 SockList_AddChar(&sl_layer, (char) layer + 1); 01713 /* The face. */ 01714 SockList_AddShort(&sl_layer, face); 01715 /* Get the first several flags of this object (like paralyzed, 01716 * sleeping, etc). */ 01717 SockList_AddChar(&sl_layer, (char) GET_CLIENT_FLAGS(head)); 01718 /* Flags we figured out above. */ 01719 SockList_AddChar(&sl_layer, flags); 01720 01721 /* Multi-arch? Add it's quick pos. */ 01722 if (flags & MAP2_FLAG_MULTI) 01723 { 01724 SockList_AddChar(&sl_layer, (char) quick_pos); 01725 } 01726 01727 /* Player name? Add the player's name, and their player name color. */ 01728 if (flags & MAP2_FLAG_NAME) 01729 { 01730 SockList_AddString(&sl_layer, CONTR(tmp)->quick_name); 01731 if (CONTR(pl)->socket.socket_version >= 1055) 01732 { 01733 SockList_AddString(&sl_layer, get_playername_color(pl, tmp)); 01734 } 01735 else 01736 { 01737 SockList_AddChar(&sl_layer, (char) color_notation_to_flag(get_playername_color(pl, tmp))); 01738 } 01739 } 01740 01741 /* Target's HP bar. */ 01742 if (flags & MAP2_FLAG_PROBE) 01743 { 01744 SockList_AddChar(&sl_layer, (char) probe_val); 01745 } 01746 01747 /* Z position. */ 01748 if (flags & MAP2_FLAG_HEIGHT) 01749 { 01750 if (mirror && mirror->last_eat) 01751 { 01752 SockList_AddShort(&sl_layer, head->z + mirror->last_eat); 01753 } 01754 else 01755 { 01756 SockList_AddShort(&sl_layer, head->z); 01757 } 01758 } 01759 01760 if (flags & MAP2_FLAG_ZOOM) 01761 { 01762 /* First check mirror, even if the object *does* have custom zoom. */ 01763 if (mirror && mirror->last_heal) 01764 { 01765 SockList_AddShort(&sl_layer, mirror->last_heal); 01766 } 01767 else 01768 { 01769 SockList_AddShort(&sl_layer, head->zoom); 01770 } 01771 } 01772 01773 if (flags & MAP2_FLAG_ALIGN) 01774 { 01775 if (mirror && mirror->align) 01776 { 01777 SockList_AddShort(&sl_layer, head->align + mirror->align); 01778 } 01779 else 01780 { 01781 SockList_AddShort(&sl_layer, head->align); 01782 } 01783 } 01784 01785 if (flags & MAP2_FLAG_MORE) 01786 { 01787 SockList_AddInt(&sl_layer, flags2); 01788 01789 if (flags2 & MAP2_FLAG2_ALPHA) 01790 { 01791 SockList_AddChar(&sl_layer, head->alpha); 01792 } 01793 01794 if (flags2 & MAP2_FLAG2_ROTATE) 01795 { 01796 SockList_AddShort(&sl_layer, head->rotate); 01797 } 01798 } 01799 } 01800 /* Didn't find anything. Now, if we have previously seen a face 01801 * on this layer, we will want the client to clear it. */ 01802 else if (mp->faces[layer]) 01803 { 01804 mp->faces[layer] = 0; 01805 mp->quick_pos[layer] = 0; 01806 SockList_AddChar(&sl_layer, MAP2_LAYER_CLEAR); 01807 SockList_AddChar(&sl_layer, (char) layer + 1); 01808 num_layers++; 01809 } 01810 } 01811 01812 SockList_AddChar(&sl, (char) num_layers); 01813 01814 /* Do we have any data about layers? If so, copy it to the main SockList instance. */ 01815 if (sl_layer.len) 01816 { 01817 memcpy(sl.buf + sl.len, sl_layer.buf, sl_layer.len); 01818 sl.len += sl_layer.len; 01819 } 01820 01821 /* Kill animation? */ 01822 if (GET_MAP_RTAG(m, nx, ny) == ROUND_TAG) 01823 { 01824 ext_flags |= MAP2_FLAG_EXT_ANIM; 01825 anim_type = ANIM_KILL; 01826 anim_value = GET_MAP_DAMAGE(m, nx, ny); 01827 } 01828 01829 /* Add flags for this tile. */ 01830 SockList_AddChar(&sl, (char) ext_flags); 01831 01832 /* Animation? Add its type and value. */ 01833 if (ext_flags & MAP2_FLAG_EXT_ANIM) 01834 { 01835 SockList_AddChar(&sl, (char) anim_type); 01836 SockList_AddShort(&sl, (sint16) anim_value); 01837 } 01838 01839 /* If nothing has really changed, go back to old SockList length. */ 01840 if (!(mask & 0x3f) && !num_layers && !ext_flags) 01841 { 01842 sl.len = oldlen; 01843 } 01844 01845 /* Set 'mirror' back to NULL, so we'll try to re-find it on another tile. */ 01846 mirror = NULL; 01847 } 01848 } 01849 01850 /* Verify that we in fact do need to send this. */ 01851 if (sl.len > 4) 01852 { 01853 Send_With_Handling(&CONTR(pl)->socket, &sl); 01854 } 01855 } 01856 01862 void ShopCmd(char *buf, int len, player *pl) 01863 { 01864 (void) buf; 01865 (void) len; 01866 (void) pl; 01867 } 01868 01874 void QuestListCmd(char *data, int len, player *pl) 01875 { 01876 object *quest_container = pl->quest_container, *tmp; 01877 StringBuffer *sb = stringbuffer_new(); 01878 char *cp; 01879 size_t cp_len; 01880 01881 (void) data; 01882 (void) len; 01883 01884 if (!quest_container || !quest_container->inv) 01885 { 01886 stringbuffer_append_string(sb, "qlist <title>No quests to speak of.</title>"); 01887 01888 cp_len = sb->pos; 01889 cp = stringbuffer_finish(sb); 01890 Write_String_To_Socket(&pl->socket, BINARY_CMD_QLIST, cp, cp_len); 01891 free(cp); 01892 return; 01893 } 01894 01895 stringbuffer_append_string(sb, "qlist <book=Quest List><title>Incomplete quests:</title>\n"); 01896 01897 /* First show incomplete quests */ 01898 for (tmp = quest_container->inv; tmp; tmp = tmp->below) 01899 { 01900 if (tmp->type != QUEST_CONTAINER || tmp->magic == QUEST_STATUS_COMPLETED) 01901 { 01902 continue; 01903 } 01904 01905 stringbuffer_append_printf(sb, "\n<title>%s</title>\n%s%s", tmp->name, tmp->msg ? tmp->msg : "", tmp->msg ? "\n" : ""); 01906 01907 if (tmp->sub_type == QUEST_TYPE_MULTI) 01908 { 01909 object *tmp2, *last; 01910 01911 /* Find the last entry. */ 01912 for (last = tmp->inv; last && last->below; last = last->below) 01913 { 01914 } 01915 01916 /* Show the quest parts. */ 01917 for (tmp2 = last; tmp2; tmp2 = tmp2->above) 01918 { 01919 if (tmp2->msg) 01920 { 01921 stringbuffer_append_printf(sb, "\n- %s", tmp2->msg); 01922 01923 if (tmp2->magic == QUEST_STATUS_COMPLETED) 01924 { 01925 stringbuffer_append_string(sb, " [done]"); 01926 } 01927 } 01928 01929 switch (tmp2->sub_type) 01930 { 01931 case QUEST_TYPE_KILL: 01932 stringbuffer_append_printf(sb, "\n<x=10>Status: %d/%d", MIN(tmp2->last_sp, tmp2->last_grace), tmp2->last_grace); 01933 break; 01934 } 01935 } 01936 01937 stringbuffer_append_string(sb, "\n"); 01938 } 01939 else 01940 { 01941 switch (tmp->sub_type) 01942 { 01943 case QUEST_TYPE_KILL: 01944 stringbuffer_append_printf(sb, "Status: %d/%d\n", MIN(tmp->last_sp, tmp->last_grace), tmp->last_grace); 01945 break; 01946 } 01947 } 01948 } 01949 01950 stringbuffer_append_string(sb, "<p>\n<title>Completed quests:</title>\n"); 01951 01952 /* Now show completed quests */ 01953 for (tmp = quest_container->inv; tmp; tmp = tmp->below) 01954 { 01955 if (tmp->type != QUEST_CONTAINER || tmp->magic != QUEST_STATUS_COMPLETED) 01956 { 01957 continue; 01958 } 01959 01960 stringbuffer_append_printf(sb, "\n<title>%s</title>\n%s%s", tmp->name, tmp->msg ? tmp->msg : "", tmp->msg ? "\n" : ""); 01961 } 01962 01963 cp_len = sb->pos; 01964 cp = stringbuffer_finish(sb); 01965 Write_String_To_Socket(&pl->socket, BINARY_CMD_QLIST, cp, cp_len); 01966 free(cp); 01967 } 01968 01974 void command_clear_cmds(char *buf, int len, socket_struct *ns) 01975 { 01976 (void) buf; 01977 (void) len; 01978 01979 ns->cmdbuf.len = 0; 01980 ns->cmdbuf.buf[0] = '\0'; 01981 } 01982 01985 void SetSound(char *buf, int len, socket_struct *ns) 01986 { 01987 if (!buf || !len) 01988 { 01989 return; 01990 } 01991 01992 ns->sound = atoi(buf); 01993 } 01994 02000 void command_move_path(uint8 *buf, int len, player *pl) 02001 { 02002 sint8 x, y; 02003 mapstruct *m; 02004 int xt, yt; 02005 path_node *node, *tmp; 02006 02007 if (!buf || len < 2) 02008 { 02009 return; 02010 } 02011 02012 x = buf[0]; 02013 y = buf[1]; 02014 02015 /* Validate the passed x/y. */ 02016 if (x < 0 || x >= pl->socket.mapx || y < 0 || y >= pl->socket.mapy) 02017 { 02018 return; 02019 } 02020 02021 /* If this is the middle of the screen where the player is already, 02022 * there isn't much to do. */ 02023 if (x == pl->socket.mapx_2 && y == pl->socket.mapy_2) 02024 { 02025 return; 02026 } 02027 02028 /* The x/y we got above is from the client's map, so 0,0 is 02029 * actually topmost (northwest) corner of the map in the client, 02030 * and not 0,0 of the actual map, so we need to transform it to 02031 * actual map coordinates. */ 02032 xt = pl->ob->x + (x - pl->socket.mapx / 2); 02033 yt = pl->ob->y + (y - pl->socket.mapy / 2); 02034 m = get_map_from_coord(pl->ob->map, &xt, &yt); 02035 02036 /* Invalid x/y. */ 02037 if (!m) 02038 { 02039 return; 02040 } 02041 02042 /* Find and compress the path to the destination. */ 02043 node = compress_path(find_path(pl->ob, pl->ob->map, pl->ob->x, pl->ob->y, m, xt, yt)); 02044 02045 /* No path available. */ 02046 if (!node) 02047 { 02048 return; 02049 } 02050 02051 /* Clear any previously queued paths. */ 02052 player_path_clear(pl); 02053 02054 /* 'node' now actually points to where the player is standing, so 02055 * skip that. */ 02056 if (node->next) 02057 { 02058 for (tmp = node->next; tmp; tmp = tmp->next) 02059 { 02060 player_path_add(pl, tmp->map, tmp->x, tmp->y); 02061 } 02062 } 02063 02064 /* The last x,y where we wanted to move is not included in the 02065 * above paths finding, so we have to add it manually. */ 02066 player_path_add(pl, m, xt, yt); 02067 } 02068 02075 void cmd_ready(uint8 *buf, int len, player *pl) 02076 { 02077 tag_t tag; 02078 object *tmp; 02079 int type; 02080 02081 if (!buf || len < 4) 02082 { 02083 return; 02084 } 02085 02086 /* Get the ID of the object this is being done for. */ 02087 tag = GetInt_String(buf); 02088 02089 /* Search for the object in the player's inventory. */ 02090 for (tmp = pl->ob->inv; tmp; tmp = tmp->below) 02091 { 02092 if (tmp->count == tag) 02093 { 02094 /* Determine whether this object can be readied. */ 02095 type = cmd_ready_determine(tmp); 02096 02097 if (type == -1) 02098 { 02099 return; 02100 } 02101 02102 /* If it's already readied, just unready it. */ 02103 if (QUERY_FLAG(tmp, FLAG_IS_READY)) 02104 { 02105 CLEAR_FLAG(tmp, FLAG_IS_READY); 02106 pl->ready_object[type] = NULL; 02107 /* Inform the client about the change. */ 02108 cmd_ready_send(pl, -1, type); 02109 02110 new_draw_info_format(0, COLOR_WHITE, pl->ob, "Unready %s.", query_base_name(tmp, pl->ob)); 02111 } 02112 /* Otherwise ready it. */ 02113 else 02114 { 02115 /* Unready previously readied object of this type, if any. */ 02116 cmd_ready_clear(pl->ob, type); 02117 SET_FLAG(tmp, FLAG_IS_READY); 02118 pl->ready_object[type] = tmp; 02119 pl->ready_object_tag[type] = tmp->count; 02120 /* Inform the client about the change. */ 02121 cmd_ready_send(pl, tag, type); 02122 02123 if (type == READY_OBJ_ARROW) 02124 { 02125 new_draw_info_format(0, COLOR_WHITE, pl->ob, "Ready %s as ammunition.", query_base_name(tmp, pl->ob)); 02126 } 02127 else if (type == READY_OBJ_THROW) 02128 { 02129 new_draw_info_format(0, COLOR_WHITE, pl->ob, "Ready %s for throwing.", query_base_name(tmp, pl->ob)); 02130 } 02131 } 02132 02133 break; 02134 } 02135 } 02136 } 02137 02144 void command_fire(uint8 *buf, int len, player *pl) 02145 { 02146 int dir, pos = 0; 02147 uint8 type; 02148 02149 if (!buf || len < 2) 02150 { 02151 return; 02152 } 02153 02154 /* Get the direction for firing and the fire mode (type). */ 02155 dir = buf[pos++]; 02156 dir = MAX(0, MIN(dir, 8)); 02157 type = buf[pos++]; 02158 02159 /* For spell and skill firing, get the name of what to use. */ 02160 if (type == FIRE_MODE_SPELL || type == FIRE_MODE_SKILL) 02161 { 02162 if (len < 4) 02163 { 02164 return; 02165 } 02166 02167 /* Store the name. */ 02168 strncpy(pl->firemode_name, (char *) buf + pos, sizeof(pl->firemode_name) - 1); 02169 pl->firemode_name[sizeof(pl->firemode_name) - 1] = '\0'; 02170 } 02171 02172 /* Check that we can actually cast this spell... */ 02173 if (type == FIRE_MODE_SPELL && !fire_cast_spell(pl->ob, pl->firemode_name)) 02174 { 02175 return; 02176 } 02177 02178 pl->fire_on = 1; 02179 pl->firemode_type = type; 02180 move_player(pl->ob, dir); 02181 pl->fire_on = 0; 02182 pl->firemode_type = -1; 02183 } 02184 02190 void cmd_keepalive(char *buf, int len, socket_struct *ns) 02191 { 02192 (void) buf; 02193 (void) len; 02194 ns->keepalive = 0; 02195 } 02196 02202 void cmd_password_change(uint8 *buf, int len, player *pl) 02203 { 02204 char pswd_current[MAX_BUF], pswd_new[MAX_BUF]; 02205 int pos = 0; 02206 size_t pswd_len; 02207 02208 /* This makes the assumption that there is at least 1 character 02209 * in both current and new password. */ 02210 if (len < 4) 02211 { 02212 return; 02213 } 02214 02215 /* Get the current and new password. */ 02216 GetString_String(buf, len, &pos, pswd_current, sizeof(pswd_current)); 02217 GetString_String(buf, len, &pos, pswd_new, sizeof(pswd_new)); 02218 02219 /* Make sure there are no untypeable characters... */ 02220 cleanup_chat_string(pswd_current); 02221 cleanup_chat_string(pswd_new); 02222 02223 /* Make sure there is current and new password. */ 02224 if (!*pswd_current || !*pswd_new) 02225 { 02226 return; 02227 } 02228 02229 pswd_len = strlen(pswd_new); 02230 02231 /* Make sure the new password has a valid length. */ 02232 if (pswd_len < PLAYER_PASSWORD_MIN || pswd_len > PLAYER_PASSWORD_MAX) 02233 { 02234 new_draw_info_format(0, COLOR_RED, pl->ob, "That password has an invalid length (must be %d-%d).", PLAYER_PASSWORD_MIN, PLAYER_PASSWORD_MAX); 02235 return; 02236 } 02237 02238 /* Ensure the current password matches, but silently ignore if it 02239 * doesn't as the client should handle this. */ 02240 if (!strcmp(crypt_string(pswd_current, NULL), pl->password)) 02241 { 02242 return; 02243 } 02244 02245 /* Update the player's password. */ 02246 strcpy(pl->password, crypt_string(pswd_new, NULL)); 02247 new_draw_info(0, COLOR_GREEN, pl->ob, "Your password has been changed successfully."); 02248 }
1.7.4