Atrinik Server 2.5
commands/new.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 
00030 #include <global.h>
00031 
00032 #define MAP_POS_X 0
00033 #define MAP_POS_Y 1
00034 
00035 static int map_pos_array[][2] =
00036 {
00037     {0,0},
00038 
00039     {0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},
00040 
00041     {0,-2},{1,-2},{2,-2},{2,-1},{2,0},{2,1},{2,2},{1,2},{0,2},{-1,2},{-2,2},{-2,1},
00042     {-2,0},{-2,-1},{-2,-2},{-1,-2},
00043 
00044     {0,-3},{1,-3},{2,-3},
00045     {3,-3},{3,-2},{3,-1},{3,0},{3,1},{3,2},
00046     {3,3},{2,3},{1,3},{0,3},{-1,3},{-2,3},
00047     {-3,3},{-3,2},{-3,1},{-3,0},{-3,-1},{-3,-2},
00048     {-3,-3},{-2,-3},{-1,-3},
00049 
00050     {0,-4},{1,-4},{2,-4},{3,-4},
00051     {4,-4},{4,-3},{4,-2},{4,-1},{4,0},{4,1},{4,2},{4,3},
00052     {4,4},{3,4},{2,4},{1,4},{0,4},{-1,4},{-2,4},{-3,4},
00053     {-4,4},{-4,3},{-4,2},{-4,1},{-4,0},{-4,-1},{-4,-2},{-4,-3},
00054     {-4,-4},{-3,-4},{-2,-4},{-1,-4},
00055 
00056     {0,-5},{1,-5},{2,-5},{3,-5},{4,-5},
00057     {5,-5},{5,-4},{5,-3},{5,-2},{5,-1},{5,0},{5,1},{5,2},{5,3},{5,4},
00058     {5,5},{4,5},{3,5},{2,5},{1,5},{0,5},{-1,5},{-2,5},{-3,5},{-4,5},
00059     {-5,5},{-5,4},{-5,3},{-5,2},{-5,1},{-5,0},{-5,-1},{-5,-2},{-5,-3},{-5,-4},
00060     {-5,-5},{-4,-5},{-3,-5},{-2,-5}, {-1,-5},
00061 
00062     {0,-6},{1,-6},{2,-6},{3,-6},{4,-6},{5,-6},
00063     {6,-6},{6,-5},{6,-4},{6,-3},{6,-2},{6,-1},{6,0},{6,1},{6,2},{6,3},{6,4},{6,5},
00064     {6,6},{5,6},{4,6},{3,6},{2,6},{1,6},{0,6},{-1,6},{-2,6},{-3,6},{-4,6},{-5,6},
00065     {-6,6},{-6,5},{-6,4},{-6,3},{-6,2},{-6,1},{-6,0},{-6,-1},{-6,-2},{-6,-3},{-6,-4},{-6,-5},
00066     {-6,-6},{-5,-6},{-4,-6},{-3,-6},{-2,-6},{-1,-6},
00067 
00068     {0,-7},{1,-7},{2,-7},{3,-7},{4,-7},{5,-7},{6,-7},
00069     {7,-7},{7,-6},{7,-5},{7,-4},{7,-3},{7,-2},{7,-1},{7,0},{7,1},{7,2},{7,3},{7,4},{7,5},{7,6},
00070     {7,7},{6,7},{5,7},{4,7},{3,7},{2,7},{1,7},{0,7},{-1,7},{-2,7},{-3,7},{-4,7},{-5,7},{-6,7},
00071     {-7,7},{-7,6},{-7,5},{-7,4},{-7,3},{-7,2},{-7,1},{-7,0},{-7,-1},{-7,-2},{-7,-3},{-7,-4},{-7,-5},{-7,-6},
00072     {-7,-7},{-6,-7},{-5,-7},{-4,-7},{-3,-7},{-2,-7},{-1,-7},
00073 
00074     {0,-8},{1,-8},{2,-8},{3,-8},{4,-8},{5,-8},{6,-8},{7,-8},
00075     {8,-8},{8,-7},{8,-6},{8,-5},{8,-4},{8,-3},{8,-2},{8,-1},{8,0},{8,1},{8,2},{8,3},{8,4},{8,5},{8,6},{8,7},
00076     {8,8},{7,8},{6,8},{5,8},{4,8},{3,8},{2,8},{1,8},{0,8},{-1,8},{-2,8},{-3,8},{-4,8},{-5,8},{-6,8},{-7,8},
00077     {-8,8},{-8,7},{-8,6},{-8,5},{-8,4},{-8,3},{-8,2},{-8,1},{-8,0},{-8,-1},{-8,-2},{-8,-3},{-8,-4},{-8,-5},{-8,-6},{-8,-7},
00078     {-8,-8},{-7,-8},{-6,-8},{-5,-8},{-4,-8},{-3,-8},{-2,-8},{-1,-8}
00079 };
00080 
00081 #define NROF_MAP_NODE (sizeof(map_pos_array) / (sizeof(int) * 2))
00082 
00087 int command_run(object *op, char *params)
00088 {
00089     int dir;
00090 
00091     if (!params)
00092     {
00093         CONTR(op)->run_on = 1;
00094         return 0;
00095     }
00096 
00097     dir = atoi(params);
00098 
00099     if (dir <= 0 || dir > 9)
00100     {
00101         new_draw_info(0, COLOR_WHITE, op, "Can't run into a non-adjacent square.");
00102         return 0;
00103     }
00104 
00105     CONTR(op)->run_on = 1;
00106 
00107     if (dir == 9)
00108     {
00109         dir = absdir(op->facing - 4);
00110     }
00111 
00112     return move_player(op, dir);
00113 }
00114 
00120 int command_run_stop(object *op, char *params)
00121 {
00122     (void) params;
00123 
00124     CONTR(op)->run_on = 0;
00125     return 1;
00126 }
00127 
00131 void send_target_command(player *pl)
00132 {
00133     if (pl->socket.socket_version >= 1055)
00134     {
00135     SockList sl;
00136     unsigned char sockbuf[HUGE_BUF];
00137 
00138     if (!pl->ob->map)
00139     {
00140         return;
00141     }
00142 
00143     sl.buf = sockbuf;
00144     SOCKET_SET_BINARY_CMD(&sl, BINARY_CMD_TARGET);
00145     SockList_AddChar(&sl, pl->combat_mode);
00146 
00147     pl->ob->enemy = NULL;
00148     pl->ob->enemy_count = 0;
00149 
00150     if (!pl->target_object || pl->target_object == pl->ob || !OBJECT_VALID(pl->target_object, pl->target_object_count) || IS_INVISIBLE(pl->target_object, pl->ob))
00151     {
00152         SockList_AddChar(&sl, CMD_TARGET_SELF);
00153         SockList_AddString(&sl, COLOR_YELLOW);
00154         SockList_AddString(&sl, pl->ob->name);
00155 
00156         pl->target_object = pl->ob;
00157         pl->target_object_count = 0;
00158         pl->target_map_pos = 0;
00159     }
00160     else
00161     {
00162         const char *color;
00163 
00164         if (is_friend_of(pl->ob, pl->target_object))
00165         {
00166             SockList_AddChar(&sl, CMD_TARGET_FRIEND);
00167         }
00168         else
00169         {
00170             SockList_AddChar(&sl, CMD_TARGET_ENEMY);
00171 
00172             pl->ob->enemy = pl->target_object;
00173             pl->ob->enemy_count = pl->target_object_count;
00174         }
00175 
00176         if (pl->target_object->level < level_color[pl->ob->level].yellow)
00177         {
00178             if (pl->target_object->level < level_color[pl->ob->level].green)
00179             {
00180                 color = COLOR_GRAY;
00181             }
00182             else
00183             {
00184                 if (pl->target_object->level < level_color[pl->ob->level].blue)
00185                 {
00186                     color = COLOR_GREEN;
00187                 }
00188                 else
00189                 {
00190                     color = COLOR_BLUE;
00191                 }
00192             }
00193         }
00194         else
00195         {
00196             if (pl->target_object->level >= level_color[pl->ob->level].purple)
00197             {
00198                 color = COLOR_PURPLE;
00199             }
00200             else if (pl->target_object->level >= level_color[pl->ob->level].red)
00201             {
00202                 color = COLOR_RED;
00203             }
00204             else if (pl->target_object->level >= level_color[pl->ob->level].orange)
00205             {
00206                 color = COLOR_ORANGE;
00207             }
00208             else
00209             {
00210                 color = COLOR_YELLOW;
00211             }
00212         }
00213 
00214         SockList_AddString(&sl, color);
00215 
00216         if (QUERY_FLAG(pl->ob, FLAG_WIZ))
00217         {
00218             char buf[MAX_BUF];
00219 
00220             snprintf(buf, sizeof(buf), "%s (lvl %d)", pl->target_object->name, pl->target_object->level);
00221             SockList_AddString(&sl, buf);
00222         }
00223         else
00224         {
00225             SockList_AddString(&sl, pl->target_object->name);
00226         }
00227     }
00228 
00229     Send_With_Handling(&pl->socket, &sl);
00230     }
00231     else
00232     {
00233     int aim_self_flag = 0;
00234     char tmp[256];
00235 
00236     if (!pl->ob->map)
00237     {
00238         return;
00239     }
00240 
00241     tmp[0] = BINARY_CMD_TARGET;
00242     tmp[1] = pl->combat_mode;
00243     /* Color mode */
00244     tmp[2] = 0;
00245 
00246     pl->ob->enemy = NULL;
00247     pl->ob->enemy_count = 0;
00248 
00249     /* Target still legal? */
00250     /* that's we self */
00251     if (!pl->target_object || !OBJECT_ACTIVE(pl->target_object) || pl->target_object == pl->ob)
00252     {
00253         aim_self_flag = 1;
00254     }
00255     else if (pl->target_object_count == pl->target_object->count)
00256     {
00257         /* OK, a last check... i put it here to have clear code:
00258          * perhaps we have legal issues why we can't aim or attack
00259          * our target anymore... invisible & stuff are handled here.
00260          * stuff like a out of PvP area moved player are handled different.
00261          * we HOLD the target - perhaps the guy moved back.
00262          * this special stuff is handled deeper in attack() functions. */
00263         if (QUERY_FLAG(pl->target_object, FLAG_SYS_OBJECT) || (QUERY_FLAG(pl->target_object, FLAG_IS_INVISIBLE) && !QUERY_FLAG(pl->ob, FLAG_SEE_INVISIBLE)))
00264         {
00265             aim_self_flag = 1;
00266         }
00267         else
00268         {
00269             /* friend */
00270             if (is_friend_of(pl->ob, pl->target_object))
00271             {
00272                 tmp[3] = 2;
00273             }
00274             /* enemy */
00275             else
00276             {
00277                 tmp[3] = 1;
00278                 pl->ob->enemy = pl->target_object;
00279                 pl->ob->enemy_count = pl->target_object_count;
00280             }
00281 
00282             if (pl->target_object->name)
00283             {
00284                 strcpy(tmp + 4, pl->target_object->name);
00285             }
00286             else
00287             {
00288                 strcpy(tmp + 4, "(null)");
00289             }
00290         }
00291     }
00292     else
00293     {
00294         aim_self_flag = 1;
00295     }
00296 
00297     /* OK... at last, target self */
00298     if (aim_self_flag)
00299     {
00300         /* self */
00301         tmp[3] = 0;
00302         strcpy(tmp + 4, pl->ob->name);
00303         pl->target_object = pl->ob;
00304         pl->target_object_count = 0;
00305         pl->target_map_pos = 0;
00306     }
00307 
00308     /* now we have a target - lets calculate the color code.
00309      * we can do it easy and send the real level to client and
00310      * let Calculate it there but this will allow to spoil that
00311      * data on client side. */
00312     /* target is lower */
00313     if (pl->target_object->level < level_color[pl->ob->level].yellow)
00314     {
00315         /* if < the green border value, the mob is gray */
00316         if (pl->target_object->level < level_color[pl->ob->level].green)
00317         {
00318             tmp[2] = 6;
00319         }
00320         /* calc green or blue */
00321         else
00322         {
00323             if (pl->target_object->level < level_color[pl->ob->level].blue)
00324             {
00325                 tmp[2] = 4;
00326             }
00327             else
00328             {
00329                 tmp[2] = 5;
00330             }
00331         }
00332     }
00333     /* target is higher or as yellow min. range */
00334     else
00335     {
00336         if (pl->target_object->level >= level_color[pl->ob->level].purple)
00337         {
00338             tmp[2] = 8;
00339         }
00340         else if (pl->target_object->level >= level_color[pl->ob->level].red)
00341         {
00342             tmp[2] = 3;
00343         }
00344         else if (pl->target_object->level >= level_color[pl->ob->level].orange)
00345         {
00346             tmp[2] = 1;
00347         }
00348         else
00349         {
00350             tmp[2] = 10;
00351         }
00352     }
00353 
00354     /* Some nice extra info for DMs */
00355     if (QUERY_FLAG(pl->ob, FLAG_WIZ))
00356     {
00357         char buf[64];
00358 
00359         snprintf(buf, sizeof(buf), " (lvl %d)", pl->target_object->level);
00360         strcat(tmp + 4, buf);
00361     }
00362 
00363     Write_String_To_Socket(&pl->socket, BINARY_CMD_TARGET, tmp, strlen(tmp + 4) + 4);
00364     }
00365 }
00366 
00372 int command_combat(object *op, char *params)
00373 {
00374     (void) params;
00375 
00376     if (!op || !op->map || op->type != PLAYER || !CONTR(op))
00377     {
00378         return 1;
00379     }
00380 
00381     if (CONTR(op)->combat_mode)
00382     {
00383         CONTR(op)->combat_mode = 0;
00384     }
00385     else
00386     {
00387         CONTR(op)->combat_mode = 1;
00388         CONTR(op)->praying = 0;
00389     }
00390 
00391     send_target_command(CONTR(op));
00392     return 1;
00393 }
00394 
00400 int command_target(object *op, char *params)
00401 {
00402     mapstruct *m;
00403     object *tmp = NULL, *head;
00404     int jump_in, jump_in_n = 0, get_ob_flag;
00405     int n, nt, xt, yt, block;
00406 
00407     if (!op || !op->map || op->type != PLAYER || !CONTR(op) || !params || params[0] == '\0')
00408     {
00409         return 1;
00410     }
00411 
00412     /* !x y = mouse map target */
00413     if (params[0] == '!')
00414     {
00415         int x, y, i;
00416 
00417         /* Try to get the x/y for the target. */
00418         if (sscanf(params + 1, "%d %d", &x, &y) != 2)
00419         {
00420             return 0;
00421         }
00422 
00423         /* Validate the passed x/y. */
00424         if (x < 0 || x >= CONTR(op)->socket.mapx || y < 0 || y >= CONTR(op)->socket.mapy)
00425         {
00426             return 0;
00427         }
00428 
00429         for (i = 0; i <= SIZEOFFREE1; i++)
00430         {
00431             /* Check whether we are still in range of the player's
00432              * viewport, and whether the player can see the square. */
00433             if (x + freearr_x[i] < 0 || x + freearr_x[i] >= CONTR(op)->socket.mapx || y + freearr_y[i] < 0 || y + freearr_y[i] >= CONTR(op)->socket.mapy || CONTR(op)->blocked_los[x + freearr_x[i]][y + freearr_y[i]] > BLOCKED_LOS_BLOCKSVIEW)
00434             {
00435                 continue;
00436             }
00437 
00438             /* The x/y we got above is from the client's map, so 0,0 is
00439              * actually topmost (northwest) corner of the map in the client,
00440              * and not 0,0 of the actual map, so we need to transform it to
00441              * actual map coordinates. */
00442             xt = op->x + (x - CONTR(op)->socket.mapx_2) + freearr_x[i];
00443             yt = op->y + (y - CONTR(op)->socket.mapy_2) + freearr_y[i];
00444             m = get_map_from_coord(op->map, &xt, &yt);
00445 
00446             /* Invalid x/y. */
00447             if (!m)
00448             {
00449                 continue;
00450             }
00451 
00452             /* Nothing alive on this spot. */
00453             if (!(GET_MAP_FLAGS(m, xt, yt) & (P_IS_ALIVE | P_IS_PLAYER)))
00454             {
00455                 continue;
00456             }
00457 
00458             /* Try to find an alive object here. */
00459             for (tmp = GET_MAP_OB_LAYER(m, xt, yt, LAYER_LIVING - 1); tmp && tmp->layer == LAYER_LIVING; tmp = tmp->above)
00460             {
00461                 head = HEAD(tmp);
00462 
00463                 if (!IS_LIVE(head) || head == CONTR(op)->target_object || head == op || IS_INVISIBLE(head, op) || OBJECT_IS_HIDDEN(op, head))
00464                 {
00465                     continue;
00466                 }
00467 
00468                 CONTR(op)->target_object = head;
00469                 CONTR(op)->target_object_count = head->count;
00470                 CONTR(op)->target_map_pos = i;
00471                 send_target_command(CONTR(op));
00472                 return 1;
00473             }
00474         }
00475 
00476         return 0;
00477     }
00478     else if (params[0] == '0')
00479     {
00480         /* if our target before was a non enemy, start new search
00481          * if it was an enemy, use old value. */
00482         n = 0;
00483         nt = -1;
00484 
00485         /* lets search for enemy object! */
00486         if (CONTR(op)->target_object && OBJECT_ACTIVE(CONTR(op)->target_object) && CONTR(op)->target_object_count == CONTR(op)->target_object->count && !is_friend_of(op, CONTR(op)->target_object))
00487         {
00488             n = CONTR(op)->target_map_pos;
00489         }
00490         else
00491         {
00492             CONTR(op)->target_object = NULL;
00493         }
00494 
00495         for (; n < (int) NROF_MAP_NODE && n != nt; n++)
00496         {
00497             int xx, yy;
00498 
00499             if (nt == -1)
00500             {
00501                 nt = n;
00502             }
00503 
00504             xt = op->x + (xx = map_pos_array[n][MAP_POS_X]);
00505             yt = op->y + (yy = map_pos_array[n][MAP_POS_Y]);
00506             block = CONTR(op)->blocked_los[xx + CONTR(op)->socket.mapx_2][yy + CONTR(op)->socket.mapy_2];
00507 
00508             if (block > BLOCKED_LOS_BLOCKSVIEW || !(m = get_map_from_coord(op->map, &xt, &yt)))
00509             {
00510                 if ((n + 1) == NROF_MAP_NODE)
00511                 {
00512                     n = -1;
00513                 }
00514 
00515                 continue;
00516             }
00517 
00518             /* we can have more as one possible target
00519              * on a square - but i try this first without
00520              * handle it. */
00521             for (tmp = get_map_ob(m, xt, yt); tmp != NULL; tmp = tmp->above)
00522             {
00523                 /* this is a possible target */
00524                 /* ensure we have head */
00525                 tmp->head != NULL ? (head = tmp->head) : (head = tmp);
00526 
00527                 if (IS_LIVE(head) && !is_friend_of(op, head))
00528                 {
00529                     /* this can happen when our old target has moved to next position */
00530                     if (head == CONTR(op)->target_object || head == op || QUERY_FLAG(head, FLAG_SYS_OBJECT) || (QUERY_FLAG(head, FLAG_IS_INVISIBLE) && !QUERY_FLAG(op, FLAG_SEE_INVISIBLE)) || OBJECT_IS_HIDDEN(op, head))
00531                     {
00532                         continue;
00533                     }
00534 
00535                     CONTR(op)->target_object = head;
00536                     CONTR(op)->target_object_count = head->count;
00537                     CONTR(op)->target_map_pos = n;
00538                     goto found_target;
00539                 }
00540             }
00541 
00542             /* force a full loop */
00543             if ((n + 1) == NROF_MAP_NODE)
00544             {
00545                 n = -1;
00546             }
00547         }
00548     }
00549     /* friend */
00550     else if (params[0] == '1')
00551     {
00552         /* if /target friend but old target was enemy - target self first */
00553         if (CONTR(op)->target_object && OBJECT_ACTIVE(CONTR(op)->target_object) && CONTR(op)->target_object_count == CONTR(op)->target_object->count && !is_friend_of(op, CONTR(op)->target_object))
00554         {
00555             CONTR(op)->target_object = op;
00556             CONTR(op)->target_object_count = op->count;
00557             CONTR(op)->target_map_pos = 0;
00558         }
00559         /* OK - search for a friendly object now! */
00560         else
00561         {
00562             /* if our target before was a non enemy, start new search
00563              * if it was an enemy, use old value. */
00564             n = 0;
00565             nt = -1;
00566 
00567             /* lets search for last friendly object position! */
00568             if (CONTR(op)->target_object == op)
00569             {
00570                 get_ob_flag = 0;
00571                 jump_in = 1;
00572                 jump_in_n = n;
00573                 tmp = op->above;
00574             }
00575             else if (OBJECT_VALID(CONTR(op)->target_object, CONTR(op)->target_object_count) && is_friend_of(op, CONTR(op)->target_object))
00576             {
00577                 get_ob_flag = 0;
00578                 jump_in = 1;
00579                 n = CONTR(op)->target_map_pos;
00580                 jump_in_n = n;
00581                 tmp = CONTR(op)->target_object->above;
00582             }
00583             else
00584             {
00585                 n = 1;
00586                 CONTR(op)->target_object = NULL;
00587                 jump_in = 0;
00588                 get_ob_flag = 1;
00589             }
00590 
00591             for (; n < (int) NROF_MAP_NODE && n != nt; n++)
00592             {
00593                 int xx, yy;
00594 
00595                 if (nt == -1)
00596                 {
00597                     nt = n;
00598                 }
00599 
00600 dirty_jump_in1:
00601                 xt = op->x + (xx = map_pos_array[n][MAP_POS_X]);
00602                 yt = op->y + (yy = map_pos_array[n][MAP_POS_Y]);
00603                 block = CONTR(op)->blocked_los[xx + CONTR(op)->socket.mapx_2][yy + CONTR(op)->socket.mapy_2];
00604 
00605                 if (block > BLOCKED_LOS_BLOCKSVIEW || !(m = get_map_from_coord(op->map, &xt, &yt)))
00606                 {
00607                     if ((n + 1) == NROF_MAP_NODE)
00608                     {
00609                         n = -1;
00610                     }
00611 
00612                     continue;
00613                 }
00614 
00615                 /* we can have more as one possible target
00616                  * on a square - but i try this first without
00617                  * handle it. */
00618                 if (get_ob_flag)
00619                 {
00620                     tmp = get_map_ob(m, xt, yt);
00621                 }
00622 
00623                 for (get_ob_flag = 1; tmp != NULL; tmp = tmp->above)
00624                 {
00625                     /* this is a possible target */
00626                     /* ensure we have head */
00627                     tmp->head != NULL ? (head = tmp->head) : (head = tmp);
00628 
00629                     if (IS_LIVE(head) && is_friend_of(op, head))
00630                     {
00631                         /* this can happen when our old target has moved to next position
00632                          * i have no tmp == op here to allow self targeting in the friendly chain */
00633                         if (head == CONTR(op)->target_object || QUERY_FLAG(head, FLAG_SYS_OBJECT) || (QUERY_FLAG(head, FLAG_IS_INVISIBLE) && !QUERY_FLAG(op, FLAG_SEE_INVISIBLE)) || OBJECT_IS_HIDDEN(op, head))
00634                         {
00635                             continue;
00636                         }
00637 
00638                         CONTR(op)->target_object = head;
00639                         CONTR(op)->target_object_count = head->count;
00640                         CONTR(op)->target_map_pos = n;
00641                         goto found_target;
00642                     }
00643                 }
00644 
00645                 /* force a full loop */
00646                 if ((n + 1) == NROF_MAP_NODE)
00647                 {
00648                     n = -1;
00649                 }
00650             }
00651 
00652             /* force another dirty jump ;) */
00653             if (jump_in)
00654             {
00655                 n = jump_in_n;
00656                 jump_in = 0;
00657 
00658                 if ((n + 1) == NROF_MAP_NODE)
00659                 {
00660                     nt = -1;
00661                 }
00662                 else
00663                 {
00664                     nt = n;
00665                 }
00666 
00667                 goto dirty_jump_in1;
00668             }
00669         }
00670     }
00671     /* self */
00672     else if (params[0] == '2')
00673     {
00674         CONTR(op)->target_object = op;
00675         CONTR(op)->target_object_count = op->count;
00676         CONTR(op)->target_map_pos = 0;
00677     }
00678     /* TODO: OK... try to use params as a name */
00679     else
00680     {
00681         /* still not sure we need this.. perhaps for groups? */
00682         /* dummy */
00683         CONTR(op)->target_object = NULL;
00684     }
00685 
00686 found_target:
00687 
00688     send_target_command(CONTR(op));
00689     return 1;
00690 }
00691 
00695 static void set_first_map(object *op)
00696 {
00697     object *current;
00698 
00699     strcpy(CONTR(op)->maplevel, first_map_path);
00700     op->x = -1;
00701     op->y = -1;
00702 
00703     if (!strcmp(first_map_path, "/tutorial"))
00704     {
00705         current = get_object();
00706         FREE_AND_COPY_HASH(EXIT_PATH(current), first_map_path);
00707         EXIT_X(current) = 1;
00708         EXIT_Y(current) = 1;
00709         current->last_eat = MAP_PLAYER_MAP;
00710         enter_exit(op, current);
00711         /* Update save bed position, so if we die, we don't end up in
00712          * the public version of the map. */
00713         strncpy(CONTR(op)->savebed_map, CONTR(op)->maplevel, sizeof(CONTR(op)->savebed_map) - 1);
00714     }
00715     else
00716     {
00717         enter_exit(op, NULL);
00718     }
00719 
00720     /* Update save bed X/Y in any case. */
00721     CONTR(op)->bed_x = op->x;
00722     CONTR(op)->bed_y = op->y;
00723 }
00724 
00727 typedef struct new_char_struct
00728 {
00730     char arch[MAX_BUF];
00731 
00735     int points_max;
00736 
00738     int stats_base[NUM_STATS];
00739 
00741     int stats_min[NUM_STATS];
00742 
00744     int stats_max[NUM_STATS];
00745 } new_char_struct;
00746 
00748 new_char_struct *new_chars = NULL;
00750 static size_t num_new_chars = 0;
00751 
00754 void new_chars_init()
00755 {
00756     char filename[HUGE_BUF], buf[HUGE_BUF];
00757     FILE *fp;
00758     size_t added = 0, i;
00759 
00760     /* Open the server_settings file. */
00761     snprintf(filename, sizeof(filename), "%s/server_settings", settings.localdir);
00762     fp = fopen(filename, "r");
00763 
00764     while (fgets(buf, sizeof(buf) - 1, fp))
00765     {
00766         /* New race; added keeps track of how many archetypes have been
00767          * added since the last new. */
00768         if (!strncmp(buf, "char ", 5))
00769         {
00770             added = 0;
00771         }
00772         /* Add new archetype for this race. */
00773         else if (!strncmp(buf, "gender ", 7))
00774         {
00775             char gender[MAX_BUF], arch[MAX_BUF], face[MAX_BUF];
00776 
00777             /* Parse the line. */
00778             if (sscanf(buf + 7, "%s %s %s", gender, arch, face) != 3)
00779             {
00780                 LOG(llevError, "Bogus line in %s: %s\n", filename, buf);
00781             }
00782 
00783             new_chars = realloc(new_chars, sizeof(*new_chars) * (num_new_chars + 1));
00784             strncpy(new_chars[num_new_chars].arch, arch, sizeof(new_chars[num_new_chars].arch) - 1);
00785             new_chars[num_new_chars].arch[sizeof(new_chars[num_new_chars].arch) - 1] = '\0';
00786             num_new_chars++;
00787             added++;
00788         }
00789         /* Data that applies to any gender archetype of this race. */
00790         else if (!strncmp(buf, "points_max ", 11) || !strncmp(buf, "stats_", 6))
00791         {
00792             /* Start from the end of the array. */
00793             for (i = num_new_chars - 1; ; i--)
00794             {
00795                 if (!strncmp(buf, "points_max ", 11))
00796                 {
00797                     new_chars[i].points_max = atoi(buf + 11);
00798                 }
00799                 else if (!strncmp(buf, "stats_base ", 11) && sscanf(buf + 11, "%d %d %d %d %d %d %d", &new_chars[i].stats_base[STR], &new_chars[i].stats_base[DEX], &new_chars[i].stats_base[CON], &new_chars[i].stats_base[INT], &new_chars[i].stats_base[WIS], &new_chars[i].stats_base[POW], &new_chars[i].stats_base[CHA]) != NUM_STATS)
00800                 {
00801                     LOG(llevError, "Bogus line in %s: %s\n", filename, buf);
00802                 }
00803                 else if (!strncmp(buf, "stats_min ", 10) && sscanf(buf + 10, "%d %d %d %d %d %d %d", &new_chars[i].stats_min[STR], &new_chars[i].stats_min[DEX], &new_chars[i].stats_min[CON], &new_chars[i].stats_min[INT], &new_chars[i].stats_min[WIS], &new_chars[i].stats_min[POW], &new_chars[i].stats_min[CHA]) != NUM_STATS)
00804                 {
00805                     LOG(llevError, "Bogus line in %s: %s\n", filename, buf);
00806                 }
00807                 else if (!strncmp(buf, "stats_max ", 10) && sscanf(buf + 10, "%d %d %d %d %d %d %d", &new_chars[i].stats_max[STR], &new_chars[i].stats_max[DEX], &new_chars[i].stats_max[CON], &new_chars[i].stats_max[INT], &new_chars[i].stats_max[WIS], &new_chars[i].stats_max[POW], &new_chars[i].stats_max[CHA]) != NUM_STATS)
00808                 {
00809                     LOG(llevError, "Bogus line in %s: %s\n", filename, buf);
00810                 }
00811 
00812                 /* Check if we have reached the total number of gender
00813                  * archetypes added for this race. */
00814                 if (i == num_new_chars - added)
00815                 {
00816                     break;
00817                 }
00818             }
00819         }
00820     }
00821 
00822     fclose(fp);
00823 }
00824 
00838 void command_new_char(char *params, int len, player *pl)
00839 {
00840     archetype *player_arch;
00841     const char *name_tmp = NULL;
00842     object *op = pl->ob;
00843     int x = pl->ob->x, y = pl->ob->y;
00844     int stats[NUM_STATS];
00845     size_t i, j;
00846     char arch[HUGE_BUF] = "";
00847 
00848     /* Ignore the command if the player is already playing. */
00849     if (pl->state == ST_PLAYING)
00850     {
00851         return;
00852     }
00853 
00854     /* Incorrect state... */
00855     if (pl->state != ST_ROLL_STAT)
00856     {
00857         LOG(llevSystem, "command_new_char(): %s does not have state ST_ROLL_STAT.\n", pl->ob->name);
00858         pl->socket.status = Ns_Dead;
00859         return;
00860     }
00861 
00862     /* Make sure there is some data to process for this command, and
00863      * actually process the data. */
00864     if (!params || !len || len > MAX_BUF || sscanf(params, "%s %d %d %d %d %d %d %d\n", arch, &stats[STR], &stats[DEX], &stats[CON], &stats[INT], &stats[WIS], &stats[POW], &stats[CHA]) != 8)
00865     {
00866         pl->socket.status = Ns_Dead;
00867         return;
00868     }
00869 
00870     player_arch = find_archetype(arch);
00871 
00872     /* Invalid player arch? */
00873     if (!player_arch || player_arch->clone.type != PLAYER)
00874     {
00875         LOG(llevSystem, "command_new_char(): %s tried to make a character with invalid player arch.\n", pl->ob->name);
00876         pl->socket.status = Ns_Dead;
00877         return;
00878     }
00879 
00880     LOG(llevInfo, "NewChar: %s: ARCH: %s (%d %d %d %d %d %d %d)\n", pl->ob->name, arch, stats[STR], stats[DEX], stats[CON], stats[INT], stats[WIS], stats[POW], stats[CHA]);
00881 
00882     for (i = 0; i < num_new_chars; i++)
00883     {
00884         if (!strcmp(arch, new_chars[i].arch))
00885         {
00886             break;
00887         }
00888     }
00889 
00890     if (i == num_new_chars)
00891     {
00892         LOG(llevSystem, "command_new_char(): %s tried to make a character with valid player arch (%s), but the arch is not defined in server_settings file.\n", pl->ob->name, arch);
00893         pl->socket.status = Ns_Dead;
00894         return;
00895     }
00896 
00897     /* Ensure all stat points have been allocated. */
00898     if (stats[STR] + stats[DEX] + stats[CON] + stats[INT] + stats[WIS] + stats[POW] + stats[CHA] != new_chars[i].stats_min[STR] + new_chars[i].stats_min[DEX] + new_chars[i].stats_min[CON] + new_chars[i].stats_min[INT] + new_chars[i].stats_min[WIS] + new_chars[i].stats_min[POW] + new_chars[i].stats_min[CHA] + new_chars[i].points_max)
00899     {
00900         LOG(llevSystem, "command_new_char(): %s didn't allocate all stat points (player arch: %s) (stats: %d, %d, %d, %d, %d, %d, %d).\n", pl->ob->name, arch, stats[STR], stats[DEX], stats[CON], stats[INT], stats[WIS], stats[POW], stats[CHA]);
00901         pl->socket.status = Ns_Dead;
00902         return;
00903     }
00904 
00905     /* Make sure all the stats are in a valid range. */
00906     for (j = 0; j < NUM_STATS; j++)
00907     {
00908         if (stats[j] < new_chars[i].stats_min[j])
00909         {
00910             LOG(llevSystem, "command_new_char(): %s tried to allocate too few points to %s (min: %d).", pl->ob->name, statname[j], new_chars[i].stats_min[j]);
00911             pl->socket.status = Ns_Dead;
00912             return;
00913         }
00914         else if (stats[j] > new_chars[i].stats_max[j])
00915         {
00916             LOG(llevSystem, "command_new_char(): %s tried to allocate too many points to %s (max: %d).", pl->ob->name, statname[j], new_chars[i].stats_max[j]);
00917             pl->socket.status = Ns_Dead;
00918             return;
00919         }
00920     }
00921 
00922     FREE_AND_ADD_REF_HASH(name_tmp, op->name);
00923     copy_object(&player_arch->clone, op, 0);
00924     op->custom_attrset = pl;
00925     pl->ob = op;
00926     FREE_AND_CLEAR_HASH2(op->name);
00927     op->name = name_tmp;
00928     op->x = x;
00929     op->y = y;
00930     /* So the player faces east. */
00931     op->direction = op->anim_last_facing = op->anim_last_facing_last = op->facing = 3;
00932     /* We assume that players always have a valid animation. */
00933     SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction);
00934 
00935     pl->orig_stats.Str = stats[STR];
00936     pl->orig_stats.Dex = stats[DEX];
00937     pl->orig_stats.Con = stats[CON];
00938     pl->orig_stats.Int = stats[INT];
00939     pl->orig_stats.Wis = stats[WIS];
00940     pl->orig_stats.Pow = stats[POW];
00941     pl->orig_stats.Cha = stats[CHA];
00942 
00943     SET_FLAG(op, FLAG_NO_FIX_PLAYER);
00944     /* This must before then initial items are given. */
00945     esrv_new_player(CONTR(op), op->weight + op->carrying);
00946 
00947     /* Trigger the global BORN event */
00948     trigger_global_event(GEVENT_BORN, op, NULL);
00949 
00950     /* Trigger the global LOGIN event */
00951     trigger_global_event(GEVENT_LOGIN, CONTR(op), CONTR(op)->socket.host);
00952 
00953     CONTR(op)->state = ST_PLAYING;
00954     FREE_AND_CLEAR_HASH2(op->msg);
00955 
00956 #ifdef AUTOSAVE
00957     CONTR(op)->last_save_tick = pticks;
00958 #endif
00959 
00960     display_motd(op);
00961 
00962     if (!CONTR(op)->dm_stealth)
00963     {
00964         new_draw_info_format(NDI_ALL, COLOR_DK_ORANGE, op, "%s entered the game.", op->name);
00965     }
00966 
00967     CLEAR_FLAG(op, FLAG_WIZ);
00968     init_player_exp(op);
00969     give_initial_items(op, op->randomitems);
00970     link_player_skills(op);
00971     CLEAR_FLAG(op, FLAG_NO_FIX_PLAYER);
00972     /* Force sending of skill exp data to client */
00973     CONTR(op)->last_stats.exp = 1;
00974     fix_player(op);
00975     esrv_update_item(UPD_FACE, op, op);
00976     esrv_send_inventory(op, op);
00977 
00978     set_first_map(op);
00979     SET_FLAG(op, FLAG_FRIENDLY);
00980 
00981     CONTR(op)->socket.update_tile = 0;
00982     CONTR(op)->socket.look_position = 0;
00983     CONTR(op)->socket.ext_title_flag = 1;
00984     esrv_new_player(CONTR(op), op->weight + op->carrying);
00985     send_skilllist_cmd(op, NULL, SPLIST_MODE_ADD);
00986     send_spelllist_cmd(op, NULL, SPLIST_MODE_ADD);
00987 }
00988 
00997 void command_fire_old(char *params, int len, player *pl)
00998 {
00999     int dir = 0, type, tag1, tag2;
01000     object *op = pl->ob;
01001 
01002     if (!params || !len)
01003     {
01004         return;
01005     }
01006 
01007     CONTR(op)->fire_on = 1;
01008 
01009     sscanf(params, "%d %d %d %d", &dir, &type, &tag1, &tag2);
01010 
01011     if (type == FIRE_MODE_SPELL)
01012     {
01013         char *tmp;
01014 
01015         tag2 = -1;
01016         tmp = strchr(params, ' ');
01017         tmp = strchr(tmp + 1, ' ');
01018         tmp = strchr(tmp + 1, ' ');
01019 
01020         strncpy(CONTR(op)->firemode_name, tmp + 1, sizeof(CONTR(op)->firemode_name) - 1);
01021 
01022         if (!fire_cast_spell(op, CONTR(op)->firemode_name))
01023         {
01024             CONTR(op)->fire_on = 0;
01025             /* marks no client fire action */
01026             CONTR(op)->firemode_type = -1;
01027             return;
01028         }
01029     }
01030     else if (type == FIRE_MODE_SKILL)
01031     {
01032         char *tmp;
01033 
01034         tag2 = -1;
01035         tmp = strchr(params, ' ');
01036         tmp = strchr(tmp + 1, ' ');
01037         tmp = strchr(tmp + 1, ' ');
01038 
01039         strncpy(CONTR(op)->firemode_name, tmp + 1, sizeof(CONTR(op)->firemode_name) - 1);
01040     }
01041 
01042     /* only here will this value be set */
01043     CONTR(op)->firemode_type = type;
01044     CONTR(op)->firemode_tag1 = tag1;
01045     CONTR(op)->firemode_tag2 = tag2;
01046 
01047     move_player(op, dir);
01048     CONTR(op)->fire_on = 0;
01049     /* marks no client fire action */
01050     CONTR(op)->firemode_type = -1;
01051 }
01052 
01061 static char spelllist_determine_path(object *op, int spell_number)
01062 {
01063     uint32 path = spells[spell_number].path;
01064 
01065     if ((op->path_denied & path))
01066     {
01067         return 'd';
01068     }
01069 
01070     if ((op->path_attuned & path) && !(op->path_repelled & path))
01071     {
01072         return 'a';
01073     }
01074 
01075     if ((op->path_repelled & path) && !(op->path_attuned & path))
01076     {
01077         return 'r';
01078     }
01079 
01080     return ' ';
01081 }
01082 
01089 static void add_spell_to_spelllist(object *op, int spell_number, StringBuffer *sb)
01090 {
01091     int cost = 0;
01092 
01093     /* Determine cost of the spell */
01094     if (spells[spell_number].type == SPELL_TYPE_PRIEST && CONTR(op)->skill_ptr[SK_PRAYING])
01095     {
01096         cost = SP_level_spellpoint_cost(op, spell_number, CONTR(op)->skill_ptr[SK_PRAYING]->level);
01097     }
01098     else if (spells[spell_number].type == SPELL_TYPE_WIZARD && CONTR(op)->skill_ptr[SK_SPELL_CASTING])
01099     {
01100         cost = SP_level_spellpoint_cost(op, spell_number, CONTR(op)->skill_ptr[SK_SPELL_CASTING]->level);
01101     }
01102 
01103     stringbuffer_append_printf(sb, "/%s:%d:%c", spells[spell_number].name, cost, spelllist_determine_path(op, spell_number));
01104 }
01105 
01112 void send_spelllist_cmd(object *op, const char *spellname, int mode)
01113 {
01114     StringBuffer *sb = stringbuffer_new();
01115     char *cp;
01116     size_t cp_len;
01117 
01118     stringbuffer_append_printf(sb, "X%d ", mode);
01119 
01120     /* Send single name */
01121     if (spellname)
01122     {
01123         add_spell_to_spelllist(op, look_up_spell_name(spellname), sb);
01124     }
01125     /* Send all. If the player is a wizard, send all spells in the game. */
01126     else
01127     {
01128         int i, spnum, num_spells = QUERY_FLAG(op, FLAG_WIZ) ? NROFREALSPELLS : CONTR(op)->nrofknownspells;
01129 
01130         for (i = 0; i < num_spells; i++)
01131         {
01132             if (QUERY_FLAG(op, FLAG_WIZ))
01133             {
01134                 spnum = i;
01135             }
01136             else
01137             {
01138                 spnum = CONTR(op)->known_spells[i];
01139             }
01140 
01141             add_spell_to_spelllist(op, spnum, sb);
01142         }
01143     }
01144 
01145     cp_len = sb->pos;
01146     cp = stringbuffer_finish(sb);
01147     Write_String_To_Socket(&CONTR(op)->socket, BINARY_CMD_SPELL_LIST, cp, cp_len);
01148     free(cp);
01149 }
01150 
01156 void send_skilllist_cmd(object *op, object *skillp, int mode)
01157 {
01158     StringBuffer *sb = stringbuffer_new();
01159     char *cp;
01160     size_t cp_len;
01161 
01162     stringbuffer_append_printf(sb, "X%d ", mode);
01163 
01164     if (skillp)
01165     {
01166         add_skill_to_skilllist(skillp, sb);
01167     }
01168     else
01169     {
01170         int i;
01171 
01172         for (i = 0; i < NROFSKILLS; i++)
01173         {
01174             if (CONTR(op)->skill_ptr[i])
01175             {
01176                 add_skill_to_skilllist(CONTR(op)->skill_ptr[i], sb);
01177             }
01178         }
01179     }
01180 
01181     cp_len = sb->pos;
01182     cp = stringbuffer_finish(sb);
01183     Write_String_To_Socket(&CONTR(op)->socket, BINARY_CMD_SKILL_LIST, cp, cp_len);
01184     free(cp);
01185 }
01186 
01191 void send_ready_skill(object *op, const char *skillname)
01192 {
01193     char tmp[MAX_BUF];
01194 
01195     snprintf(tmp, sizeof(tmp), "X%s", skillname);
01196     Write_String_To_Socket(&CONTR(op)->socket, BINARY_CMD_SKILLRDY, tmp, strlen(tmp));
01197 }
01198 
01202 void generate_ext_title(player *pl)
01203 {
01204     object *walk;
01205     char prof[32] = "";
01206     char title[32] = "";
01207     char rank[32] = "";
01208     char align[32] = "";
01209     char race[MAX_BUF];
01210     char name[MAX_BUF];
01211     shstr *godname;
01212 
01213     for (walk = pl->ob->inv; walk; walk = walk->below)
01214     {
01215         if (!walk->name || !walk->arch->name)
01216         {
01217             LOG(llevDebug, "Object in %s doesn't have name/archname! (%s:%s)\n", pl->ob->name, STRING_SAFE(walk->name), STRING_SAFE(walk->arch->name));
01218             continue;
01219         }
01220 
01221         if (walk->name == shstr_cons.GUILD_FORCE && walk->arch->name == shstr_cons.guild_force)
01222         {
01223             if (walk->slaying)
01224             {
01225                 strcpy(prof, walk->slaying);
01226             }
01227 
01228             if (walk->title)
01229             {
01230                 strcpy(title, " the ");
01231                 strcat(title, walk->title);
01232             }
01233         }
01234         else if (walk->name == shstr_cons.RANK_FORCE && walk->arch->name == shstr_cons.rank_force)
01235         {
01236             if (walk->title)
01237             {
01238                 strcpy(rank, walk->title);
01239                 strcat(rank, " ");
01240             }
01241         }
01242         else if (walk->name == shstr_cons.ALIGNMENT_FORCE && walk->arch->name == shstr_cons.alignment_force)
01243         {
01244             if (walk->title)
01245             {
01246                 strcpy(align, walk->title);
01247             }
01248         }
01249     }
01250 
01251     strcpy(pl->quick_name, rank);
01252     strcat(pl->quick_name, pl->ob->name);
01253 
01254     if (QUERY_FLAG(pl->ob, FLAG_WIZ))
01255     {
01256         strcat(pl->quick_name, " [WIZ]");
01257     }
01258 
01259     snprintf(name, sizeof(name), "%s%s%s", rank, pl->ob->name, title);
01260 
01261     if (QUERY_FLAG(pl->ob, FLAG_WIZ))
01262     {
01263         strncat(name, " [WIZ]", sizeof(name) - strlen(name) - 1);
01264     }
01265 
01266     if (pl->afk)
01267     {
01268         strncat(name, " [AFK]", sizeof(name) - strlen(name) - 1);
01269     }
01270 
01271     snprintf(pl->ext_title, sizeof(pl->ext_title), "%s\n%s %s %s\n%s", name, gender_noun[object_get_gender(pl->ob)], player_get_race_class(pl->ob, race, sizeof(race)), prof, align);
01272 
01273     godname = determine_god(pl->ob);
01274 
01275     if (godname)
01276     {
01277         strncat(pl->ext_title, " follower of ", sizeof(pl->ext_title) - strlen(pl->ext_title) - 1);
01278         strncat(pl->ext_title, godname, sizeof(pl->ext_title) - strlen(pl->ext_title) - 1);
01279     }
01280 }