Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_los_c = 00003 * "$Id: los.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00034 /* Nov 95 - inserted USE_LIGHTING code stuff in here - b.t. */ 00035 00036 #include <global.h> 00037 #include <math.h> 00038 00046 #define SPACE_BLOCK 0.5 00047 00048 typedef struct blstr { 00049 int x[4], y[4]; 00050 int index; 00051 } blocks; 00052 00053 static blocks block[MAP_CLIENT_X][MAP_CLIENT_Y]; 00054 00055 static void expand_lighted_sight(object *op); 00056 00075 static void set_block(int x, int y, int bx, int by) { 00076 int index = block[x][y].index, i; 00077 00078 /* Due to flipping, we may get duplicates - better safe than sorry. 00079 */ 00080 for (i = 0; i < index; i++) { 00081 if (block[x][y].x[i] == bx 00082 && block[x][y].y[i] == by) 00083 return; 00084 } 00085 00086 block[x][y].x[index] = bx; 00087 block[x][y].y[index] = by; 00088 block[x][y].index++; 00089 #ifdef LOS_DEBUG 00090 LOG(llevDebug, "setblock: added %d %d -> %d %d (%d)\n", x, y, bx, by, block[x][y].index); 00091 #endif 00092 } 00093 00102 void init_block(void) { 00103 int x, y, dx, dy, i; 00104 static const int block_x[3] = { 00105 -1, -1, 0 00106 }, block_y[3] = { 00107 -1, 0, -1 00108 }; 00109 00110 for (x = 0; x < MAP_CLIENT_X; x++) 00111 for (y = 0; y < MAP_CLIENT_Y; y++) { 00112 block[x][y].index = 0; 00113 } 00114 00115 /* The table should be symmetric, so only do the upper left 00116 * quadrant - makes the processing easier. 00117 */ 00118 for (x = 1; x <= MAP_CLIENT_X/2; x++) { 00119 for (y = 1; y <= MAP_CLIENT_Y/2; y++) { 00120 for (i = 0; i < 3; i++) { 00121 dx = x+block_x[i]; 00122 dy = y+block_y[i]; 00123 00124 /* center space never blocks */ 00125 if (x == MAP_CLIENT_X/2 && y == MAP_CLIENT_Y/2) 00126 continue; 00127 00128 /* If its a straight line, its blocked */ 00129 if ((dx == x && x == MAP_CLIENT_X/2) 00130 || (dy == y && y == MAP_CLIENT_Y/2)) { 00131 /* For simplicity, we mirror the coordinates to block the other 00132 * quadrants. 00133 */ 00134 set_block(x, y, dx, dy); 00135 if (x == MAP_CLIENT_X/2) { 00136 set_block(x, MAP_CLIENT_Y-y-1, dx, MAP_CLIENT_Y-dy-1); 00137 } else if (y == MAP_CLIENT_Y/2) { 00138 set_block(MAP_CLIENT_X-x-1, y, MAP_CLIENT_X-dx-1, dy); 00139 } 00140 } else { 00141 float d1, r, s, l; 00142 00143 /* We use the algorihm that found out how close the point 00144 * (x,y) is to the line from dx,dy to the center of the viewable 00145 * area. l is the distance from x,y to the line. 00146 * r is more a curiosity - it lets us know what direction (left/right) 00147 * the line is off 00148 */ 00149 00150 d1 = (float)(pow(MAP_CLIENT_X/2-dx, 2)+pow(MAP_CLIENT_Y/2-dy, 2)); 00151 r = (float)((dy-y)*(dy-MAP_CLIENT_Y/2)-(dx-x)*(MAP_CLIENT_X/2-dx))/d1; 00152 s = (float)((dy-y)*(MAP_CLIENT_X/2-dx)-(dx-x)*(MAP_CLIENT_Y/2-dy))/d1; 00153 l = FABS(sqrt(d1)*s); 00154 00155 if (l <= SPACE_BLOCK) { 00156 /* For simplicity, we mirror the coordinates to block the other 00157 * quadrants. 00158 */ 00159 set_block(x, y, dx, dy); 00160 set_block(MAP_CLIENT_X-x-1, y, MAP_CLIENT_X-dx-1, dy); 00161 set_block(x, MAP_CLIENT_Y-y-1, dx, MAP_CLIENT_Y-dy-1); 00162 set_block(MAP_CLIENT_X-x-1, MAP_CLIENT_Y-y-1, MAP_CLIENT_X-dx-1, MAP_CLIENT_Y-dy-1); 00163 } 00164 } 00165 } 00166 } 00167 } 00168 } 00169 00185 static void set_wall(object *op, int x, int y) { 00186 int i; 00187 00188 for (i = 0; i < block[x][y].index; i++) { 00189 int dx = block[x][y].x[i], dy = block[x][y].y[i], ax, ay; 00190 00191 /* ax, ay are the values as adjusted to be in the 00192 * socket look structure. 00193 */ 00194 ax = dx-(MAP_CLIENT_X-op->contr->socket.mapx)/2; 00195 ay = dy-(MAP_CLIENT_Y-op->contr->socket.mapy)/2; 00196 00197 if (ax < 0 || ax >= op->contr->socket.mapx 00198 || ay < 0 || ay >= op->contr->socket.mapy) 00199 continue; 00200 /* we need to adjust to the fact that the socket 00201 * code wants the los to start from the 0,0 00202 * and not be relative to middle of los array. 00203 */ 00204 op->contr->blocked_los[ax][ay] = 100; 00205 set_wall(op, dx, dy); 00206 } 00207 } 00208 00220 static void check_wall(object *op, int x, int y) { 00221 int ax, ay; 00222 00223 if (!block[x][y].index) 00224 return; 00225 00226 /* ax, ay are coordinates as indexed into the look window */ 00227 ax = x-(MAP_CLIENT_X-op->contr->socket.mapx)/2; 00228 ay = y-(MAP_CLIENT_Y-op->contr->socket.mapy)/2; 00229 00230 /* If the converted coordinates are outside the viewable 00231 * area for the client, return now. 00232 */ 00233 if (ax < 0 || ay < 0 || ax >= op->contr->socket.mapx || ay >= op->contr->socket.mapy) 00234 return; 00235 00236 /* If this space is already blocked, prune the processing - presumably 00237 * whatever has set this space to be blocked has done the work and already 00238 * done the dependency chain. 00239 */ 00240 if (op->contr->blocked_los[ax][ay] == 100) 00241 return; 00242 00243 00244 if (get_map_flags(op->map, NULL, op->x+x-MAP_CLIENT_X/2, op->y+y-MAP_CLIENT_Y/2, NULL, NULL)&(P_BLOCKSVIEW|P_OUT_OF_MAP)) 00245 set_wall(op, x, y); 00246 } 00247 00258 void clear_los(object *op) { 00259 /* This is safer than using the socket->mapx, mapy because 00260 * we index the blocked_los as a 2 way array, so clearing 00261 * the first z spaces may not not cover the spaces we are 00262 * actually going to use 00263 */ 00264 (void)memset((void *)op->contr->blocked_los, 0, MAP_CLIENT_X*MAP_CLIENT_Y); 00265 } 00266 00278 static void expand_sight(object *op) { 00279 int i, x, y, dx, dy; 00280 00281 for (x = 1; x < op->contr->socket.mapx-1; x++) /* loop over inner squares */ 00282 for (y = 1; y < op->contr->socket.mapy-1; y++) { 00283 if (!op->contr->blocked_los[x][y] 00284 && !(get_map_flags(op->map, NULL, 00285 op->x-op->contr->socket.mapx/2+x, 00286 op->y-op->contr->socket.mapy/2+y, 00287 NULL, NULL)&(P_BLOCKSVIEW|P_OUT_OF_MAP))) { 00288 00289 for (i = 1; i <= 8; i += 1) { /* mark all directions */ 00290 dx = x+freearr_x[i]; 00291 dy = y+freearr_y[i]; 00292 if (op->contr->blocked_los[dx][dy] > 0) /* for any square blocked */ 00293 op->contr->blocked_los[dx][dy] = -1; 00294 } 00295 } 00296 } 00297 00298 if (MAP_DARKNESS(op->map) > 0) /* player is on a dark map */ 00299 expand_lighted_sight(op); 00300 00301 00302 /* clear mark squares */ 00303 for (x = 0; x < op->contr->socket.mapx; x++) 00304 for (y = 0; y < op->contr->socket.mapy; y++) 00305 if (op->contr->blocked_los[x][y] < 0) 00306 op->contr->blocked_los[x][y] = 0; 00307 } 00308 00322 int has_carried_lights(const object *op) { 00323 /* op may glow! */ 00324 if (op->glow_radius > 0) 00325 return 1; 00326 00327 return 0; 00328 } 00329 00336 static void expand_lighted_sight(object *op) { 00337 int x, y, darklevel, ax, ay, basex, basey, mflags, light, x1, y1; 00338 mapstruct *m = op->map; 00339 sint16 nx, ny; 00340 00341 darklevel = MAP_DARKNESS(m); 00342 00343 /* If the player can see in the dark, lower the darklevel for him */ 00344 if (QUERY_FLAG(op, FLAG_SEE_IN_DARK)) 00345 darklevel -= 2; 00346 00347 /* add light, by finding all (non-null) nearby light sources, then 00348 * mark those squares specially. If the darklevel<1, there is no 00349 * reason to do this, so we skip this function 00350 */ 00351 if (darklevel < 1) 00352 return; 00353 00354 /* Do a sanity check. If not valid, some code below may do odd 00355 * things. 00356 */ 00357 if (darklevel > MAX_DARKNESS) { 00358 LOG(llevError, "Map darkness for %s on %s is too high (%d)\n", op->name, op->map->path, darklevel); 00359 darklevel = MAX_DARKNESS; 00360 } 00361 00362 /* First, limit player furthest (unlighted) vision */ 00363 for (x = 0; x < op->contr->socket.mapx; x++) 00364 for (y = 0; y < op->contr->socket.mapy; y++) 00365 if (op->contr->blocked_los[x][y] != 100) 00366 op->contr->blocked_los[x][y] = MAX_LIGHT_RADII; 00367 00368 /* the spaces[] darkness value contains the information we need. 00369 * Only process the area of interest. 00370 * the basex, basey values represent the position in the op->contr->blocked_los 00371 * array. Its easier to just increment them here (and start with the right 00372 * value) than to recalculate them down below. 00373 */ 00374 for (x = (op->x-op->contr->socket.mapx/2-MAX_LIGHT_RADII), basex = -MAX_LIGHT_RADII; 00375 x <= (op->x+op->contr->socket.mapx/2+MAX_LIGHT_RADII); x++, basex++) { 00376 00377 for (y = (op->y-op->contr->socket.mapy/2-MAX_LIGHT_RADII), basey = -MAX_LIGHT_RADII; 00378 y <= (op->y+op->contr->socket.mapy/2+MAX_LIGHT_RADII); y++, basey++) { 00379 m = op->map; 00380 nx = x; 00381 ny = y; 00382 00383 mflags = get_map_flags(m, &m, nx, ny, &nx, &ny); 00384 00385 if (mflags&P_OUT_OF_MAP) 00386 continue; 00387 00388 /* This space is providing light, so we need to brighten up the 00389 * spaces around here. 00390 */ 00391 light = GET_MAP_LIGHT(m, nx, ny); 00392 if (light != 0) { 00393 for (ax = basex-light; ax <= basex+light; ax++) { 00394 if (ax < 0 || ax >= op->contr->socket.mapx) 00395 continue; 00396 for (ay = basey-light; ay <= basey+light; ay++) { 00397 if (ay < 0 || ay >= op->contr->socket.mapy) 00398 continue; 00399 00400 /* If the space is fully blocked, do nothing. Otherwise, we 00401 * brighten the space. The further the light is away from the 00402 * source (basex-x), the less effect it has. Though light used 00403 * to dim in a square manner, it now dims in a circular manner 00404 * using the the pythagorean theorem. glow_radius still 00405 * represents the radius 00406 */ 00407 if (op->contr->blocked_los[ax][ay] != 100) { 00408 x1 = abs(basex-ax)*abs(basex-ax); 00409 y1 = abs(basey-ay)*abs(basey-ay); 00410 if (light > 0) 00411 op->contr->blocked_los[ax][ay] -= MAX((light-isqrt(x1+y1)), 0); 00412 if (light < 0) 00413 op->contr->blocked_los[ax][ay] -= MIN((light+isqrt(x1+y1)), 0); 00414 } 00415 } /* for ay */ 00416 } /* for ax */ 00417 } /* if this space is providing light */ 00418 } /* for y */ 00419 } /* for x */ 00420 00421 /* Outdoor should never really be completely pitch black dark like 00422 * a dungeon, so let the player at least see a little around themselves 00423 */ 00424 if (op->map->outdoor && darklevel > (MAX_DARKNESS-3)) { 00425 if (op->contr->blocked_los[op->contr->socket.mapx/2][op->contr->socket.mapy/2] > (MAX_DARKNESS-3)) 00426 op->contr->blocked_los[op->contr->socket.mapx/2][op->contr->socket.mapy/2] = MAX_DARKNESS-3; 00427 00428 for (x = -1; x <= 1; x++) 00429 for (y = -1; y <= 1; y++) { 00430 if (op->contr->blocked_los[x+op->contr->socket.mapx/2][y+op->contr->socket.mapy/2] > (MAX_DARKNESS-2)) 00431 op->contr->blocked_los[x+op->contr->socket.mapx/2][y+op->contr->socket.mapy/2] = MAX_DARKNESS-2; 00432 } 00433 } 00434 /* grant some vision to the player, based on the darklevel */ 00435 for (x = darklevel-MAX_DARKNESS; x < MAX_DARKNESS+1-darklevel; x++) 00436 for (y = darklevel-MAX_DARKNESS; y < MAX_DARKNESS+1-darklevel; y++) 00437 if (!(op->contr->blocked_los[x+op->contr->socket.mapx/2][y+op->contr->socket.mapy/2] == 100)) 00438 op->contr->blocked_los[x+op->contr->socket.mapx/2][y+op->contr->socket.mapy/2] -= MAX(0, 6-darklevel-MAX(abs(x), abs(y))); 00439 } 00440 00450 static void blinded_sight(object *op) { 00451 int x, y; 00452 00453 for (x = 0; x < op->contr->socket.mapx; x++) 00454 for (y = 0; y < op->contr->socket.mapy; y++) 00455 op->contr->blocked_los[x][y] = 100; 00456 00457 op->contr->blocked_los[op->contr->socket.mapx/2][op->contr->socket.mapy/2] = 0; 00458 } 00459 00467 void update_los(object *op) { 00468 int dx = op->contr->socket.mapx/2, dy = op->contr->socket.mapy/2, x, y; 00469 00470 if (QUERY_FLAG(op, FLAG_REMOVED)) 00471 return; 00472 00473 clear_los(op); 00474 if (QUERY_FLAG(op, FLAG_WIZ) /* || XRAYS(op) */) 00475 return; 00476 00477 /* For larger maps, this is more efficient than the old way which 00478 * used the chaining of the block array. Since many space views could 00479 * be blocked by different spaces in front, this mean that a lot of spaces 00480 * could be examined multile times, as each path would be looked at. 00481 */ 00482 for (x = (MAP_CLIENT_X-op->contr->socket.mapx)/2+1; x < (MAP_CLIENT_X+op->contr->socket.mapx)/2-1; x++) 00483 for (y = (MAP_CLIENT_Y-op->contr->socket.mapy)/2+1; y < (MAP_CLIENT_Y+op->contr->socket.mapy)/2-1; y++) 00484 check_wall(op, x, y); 00485 00486 00487 /* do the los of the player. 3 (potential) cases */ 00488 if (QUERY_FLAG(op, FLAG_BLIND)) /* player is blind */ 00489 blinded_sight(op); 00490 else 00491 expand_sight(op); 00492 00493 if (QUERY_FLAG(op, FLAG_XRAYS)) { 00494 int x, y; 00495 for (x = -2; x <= 2; x++) 00496 for (y = -2; y <= 2; y++) 00497 op->contr->blocked_los[dx+x][dy+y] = 0; 00498 } 00499 } 00500 00516 void update_all_map_los(mapstruct *map) { 00517 player *pl; 00518 00519 for (pl = first_player; pl != NULL; pl = pl->next) { 00520 if (pl->ob->map == map) 00521 pl->do_los = 1; 00522 } 00523 } 00524 00544 void update_all_los(const mapstruct *map, int x, int y) { 00545 player *pl; 00546 00547 for (pl = first_player; pl != NULL; pl = pl->next) { 00548 /* Player should not have a null map, but do this 00549 * check as a safety 00550 */ 00551 if (!pl->ob->map) 00552 continue; 00553 00554 /* Same map is simple case - see if pl is close enough. 00555 * Note in all cases, we did the check for same map first, 00556 * and then see if the player is close enough and update 00557 * los if that is the case. If the player is on the 00558 * corresponding map, but not close enough, then the 00559 * player can't be on another map that may be closer, 00560 * so by setting it up this way, we trim processing 00561 * some. 00562 */ 00563 if (pl->ob->map == map) { 00564 if ((abs(pl->ob->x-x) <= pl->socket.mapx/2) 00565 && (abs(pl->ob->y-y) <= pl->socket.mapy/2)) 00566 pl->do_los = 1; 00567 } 00568 /* Now we check to see if player is on adjacent 00569 * maps to the one that changed and also within 00570 * view. The tile_maps[] could be null, but in that 00571 * case it should never match the pl->ob->map, so 00572 * we want ever try to dereference any of the data in it. 00573 */ 00574 00575 /* The logic for 0 and 3 is to see how far the player is 00576 * from the edge of the map (height/width) - pl->ob->(x,y) 00577 * and to add current position on this map - that gives a 00578 * distance. 00579 * For 1 and 2, we check to see how far the given 00580 * coordinate (x,y) is from the corresponding edge, 00581 * and then add the players location, which gives 00582 * a distance. 00583 */ 00584 else if (pl->ob->map == map->tile_map[0]) { 00585 if ((abs(pl->ob->x-x) <= pl->socket.mapx/2) 00586 && (abs(y+MAP_HEIGHT(map->tile_map[0])-pl->ob->y) <= pl->socket.mapy/2)) 00587 pl->do_los = 1; 00588 } else if (pl->ob->map == map->tile_map[2]) { 00589 if ((abs(pl->ob->x-x) <= pl->socket.mapx/2) 00590 && (abs(pl->ob->y+MAP_HEIGHT(map)-y) <= pl->socket.mapy/2)) 00591 pl->do_los = 1; 00592 } else if (pl->ob->map == map->tile_map[1]) { 00593 if ((abs(pl->ob->x+MAP_WIDTH(map)-x) <= pl->socket.mapx/2) 00594 && (abs(pl->ob->y-y) <= pl->socket.mapy/2)) 00595 pl->do_los = 1; 00596 } else if (pl->ob->map == map->tile_map[3]) { 00597 if ((abs(x+MAP_WIDTH(map->tile_map[3])-pl->ob->x) <= pl->socket.mapx/2) 00598 && (abs(pl->ob->y-y) <= pl->socket.mapy/2)) 00599 pl->do_los = 1; 00600 } 00601 } 00602 } 00603 00614 void print_los(object *op) { 00615 int x, y; 00616 char buf[MAP_CLIENT_X*2+20], buf2[10]; 00617 00618 snprintf(buf, sizeof(buf), "[fixed] "); 00619 for (x = 0; x < op->contr->socket.mapx; x++) { 00620 snprintf(buf2, sizeof(buf2), "%2d", x); 00621 strncat(buf, buf2, sizeof(buf)-strlen(buf)-1); 00622 } 00623 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG, buf, NULL); 00624 for (y = 0; y < op->contr->socket.mapy; y++) { 00625 snprintf(buf, sizeof(buf), "[fixed]%2d:", y); 00626 for (x = 0; x < op->contr->socket.mapx; x++) { 00627 snprintf(buf2, sizeof(buf2), " %1d", op->contr->blocked_los[x][y] == 100 ? 1 : 0); 00628 strncat(buf, buf2, sizeof(buf)-strlen(buf)-1); 00629 } 00630 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG, buf, NULL); 00631 } 00632 } 00633 00644 void make_sure_seen(const object *op) { 00645 player *pl; 00646 00647 for (pl = first_player; pl; pl = pl->next) 00648 if (pl->ob->map == op->map 00649 && pl->ob->y-pl->socket.mapy/2 <= op->y 00650 && pl->ob->y+pl->socket.mapy/2 >= op->y 00651 && pl->ob->x-pl->socket.mapx/2 <= op->x 00652 && pl->ob->x+pl->socket.mapx/2 >= op->x) 00653 pl->blocked_los[pl->socket.mapx/2+op->x-pl->ob->x][pl->socket.mapy/2+op->y-pl->ob->y] = 0; 00654 } 00655 00667 void make_sure_not_seen(const object *op) { 00668 player *pl; 00669 00670 for (pl = first_player; pl; pl = pl->next) 00671 if (pl->ob->map == op->map 00672 && pl->ob->y-pl->socket.mapy/2 <= op->y 00673 && pl->ob->y+pl->socket.mapy/2 >= op->y 00674 && pl->ob->x-pl->socket.mapx/2 <= op->x 00675 && pl->ob->x+pl->socket.mapx/2 >= op->x) 00676 pl->do_los = 1; 00677 }