Atrinik Server 2.5
socket/request.c
Go to the documentation of this file.
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 }