|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00030 #include <global.h> 00031 00032 #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 }
1.7.4