… | |
… | |
99 | // this is a variant of a spiral los algorithm taken from |
99 | // this is a variant of a spiral los algorithm taken from |
100 | // http://www.geocities.com/temerra/los_rays.html |
100 | // http://www.geocities.com/temerra/los_rays.html |
101 | // which has been simplified and changed considerably, but |
101 | // which has been simplified and changed considerably, but |
102 | // still is basically the same algorithm. |
102 | // still is basically the same algorithm. |
103 | static void |
103 | static void |
104 | do_los (object *op) |
104 | calculate_los (player *pl) |
105 | { |
105 | { |
106 | player *pl = op->contr; |
|
|
107 | |
|
|
108 | int max_radius = max (pl->ns->mapx, pl->ns->mapy) / 2; |
106 | int max_radius = max (pl->ns->mapx, pl->ns->mapy) / 2; |
109 | |
107 | |
110 | memset (los, 0, sizeof (los)); |
108 | memset (los, 0, sizeof (los)); |
111 | |
109 | |
112 | q1 = 0; q2 = 0; // initialise queue, not strictly required |
110 | q1 = 0; q2 = 0; // initialise queue, not strictly required |
… | |
… | |
208 | } |
206 | } |
209 | } |
207 | } |
210 | } |
208 | } |
211 | |
209 | |
212 | // check whether this space blocks the view |
210 | // check whether this space blocks the view |
213 | maptile *m = op->map; |
211 | maptile *m = pl->observe->map; |
214 | sint16 nx = op->x + dx; |
212 | sint16 nx = pl->observe->x + dx; |
215 | sint16 ny = op->y + dy; |
213 | sint16 ny = pl->observe->y + dy; |
216 | |
214 | |
217 | if (expect_true (!xy_normalise (m, nx, ny)) |
215 | if (expect_true (!xy_normalise (m, nx, ny)) |
218 | || expect_false (m->at (nx, ny).flags () & P_BLOCKSVIEW)) |
216 | || expect_false (m->at (nx, ny).flags () & P_BLOCKSVIEW)) |
219 | { |
217 | { |
220 | l.xo = l.xe = abs (dx); |
218 | l.xo = l.xe = abs (dx); |
… | |
… | |
312 | return max (b, l); |
310 | return max (b, l); |
313 | } |
311 | } |
314 | |
312 | |
315 | template<sint8 change_it (sint8, sint8)> |
313 | template<sint8 change_it (sint8, sint8)> |
316 | static void |
314 | static void |
317 | apply_light (object *op, int dx, int dy, int light, const sint8 *atten_table) |
315 | apply_light (player *pl, int dx, int dy, int light, const sint8 *atten_table) |
318 | { |
316 | { |
319 | // min or max the circular area around basex, basey |
317 | // min or max the circular area around basex, basey |
320 | player *pl = op->contr; |
|
|
321 | |
|
|
322 | dx += LOS_X0; |
318 | dx += LOS_X0; |
323 | dy += LOS_Y0; |
319 | dy += LOS_Y0; |
324 | |
320 | |
325 | int hx = op->contr->ns->mapx / 2; |
321 | int hx = pl->ns->mapx / 2; |
326 | int hy = op->contr->ns->mapy / 2; |
322 | int hy = pl->ns->mapy / 2; |
327 | |
323 | |
328 | int ax0 = max (LOS_X0 - hx, dx - light); |
324 | int ax0 = max (LOS_X0 - hx, dx - light); |
329 | int ay0 = max (LOS_Y0 - hy, dy - light); |
325 | int ay0 = max (LOS_Y0 - hy, dy - light); |
330 | int ax1 = min (dx + light, LOS_X0 + hx); |
326 | int ax1 = min (dx + light, LOS_X0 + hx); |
331 | int ay1 = min (dy + light, LOS_Y0 + hy); |
327 | int ay1 = min (dy + light, LOS_Y0 + hy); |
… | |
… | |
338 | |
334 | |
339 | /* add light, by finding all (non-null) nearby light sources, then |
335 | /* add light, by finding all (non-null) nearby light sources, then |
340 | * mark those squares specially. |
336 | * mark those squares specially. |
341 | */ |
337 | */ |
342 | static void |
338 | static void |
343 | apply_lights (object *op) |
339 | apply_lights (player *pl) |
344 | { |
340 | { |
345 | int darklevel, mflags, light, x1, y1; |
341 | object *op = pl->observe; |
346 | maptile *m = op->map; |
342 | int darklevel = op->map->darklevel (); |
347 | sint16 nx, ny; |
|
|
348 | |
|
|
349 | darklevel = m->darkness; |
|
|
350 | |
343 | |
351 | /* If the player can see in the dark, lower the darklevel for him */ |
344 | /* If the player can see in the dark, lower the darklevel for him */ |
352 | if (QUERY_FLAG (op, FLAG_SEE_IN_DARK)) |
345 | if (op->flag [FLAG_SEE_IN_DARK]) |
353 | darklevel -= LOS_MAX / 2; |
346 | darklevel = max (0, darklevel - 2); |
354 | |
347 | |
355 | /* Do a sanity check. If not valid, some code below may do odd |
|
|
356 | * things. |
|
|
357 | */ |
|
|
358 | if (darklevel > MAX_DARKNESS) |
|
|
359 | { |
|
|
360 | LOG (llevError, "Map darkness for %s on %s is too high (%d)\n", &op->name, &op->map->path, darklevel); |
|
|
361 | darklevel = MAX_DARKNESS; |
|
|
362 | } |
|
|
363 | |
|
|
364 | int half_x = op->contr->ns->mapx / 2; |
348 | int half_x = pl->ns->mapx / 2; |
365 | int half_y = op->contr->ns->mapy / 2; |
349 | int half_y = pl->ns->mapy / 2; |
366 | |
350 | |
367 | int min_x = op->x - half_x - MAX_LIGHT_RADIUS; |
351 | int min_x = op->x - half_x - MAX_LIGHT_RADIUS; |
368 | int min_y = op->y - half_y - MAX_LIGHT_RADIUS; |
352 | int min_y = op->y - half_y - MAX_LIGHT_RADIUS; |
369 | int max_x = op->x + half_x + MAX_LIGHT_RADIUS; |
353 | int max_x = op->x + half_x + MAX_LIGHT_RADIUS; |
370 | int max_y = op->y + half_y + MAX_LIGHT_RADIUS; |
354 | int max_y = op->y + half_y + MAX_LIGHT_RADIUS; |
371 | |
355 | |
372 | int pass2 = 0; // negative lights have an extra pass |
356 | int pass2 = 0; // negative lights have an extra pass |
373 | |
357 | |
374 | if (darklevel < 1) |
358 | if (!darklevel) |
375 | pass2 = 1; |
359 | pass2 = 1; |
376 | else |
360 | else |
377 | { |
361 | { |
378 | /* first, make everything totally dark */ |
362 | /* first, make everything totally dark */ |
379 | for (int dx = -half_x; dx <= half_x; dx++) |
363 | for (int dx = -half_x; dx <= half_x; dx++) |
380 | for (int dy = -half_x; dy <= half_y; dy++) |
364 | for (int dy = -half_x; dy <= half_y; dy++) |
381 | if (op->contr->los[dx + LOS_X0][dy + LOS_Y0] != LOS_BLOCKED) |
365 | if (pl->los[dx + LOS_X0][dy + LOS_Y0] != LOS_BLOCKED) |
382 | op->contr->los[dx + LOS_X0][dy + LOS_Y0] = LOS_MAX; |
366 | pl->los[dx + LOS_X0][dy + LOS_Y0] = LOS_MAX; |
383 | |
367 | |
384 | /* |
368 | /* |
385 | * Only process the area of interest. |
369 | * Only process the area of interest. |
386 | * the basex, basey values represent the position in the op->contr->los |
370 | * the basex, basey values represent the position in the op->contr->los |
387 | * array. Its easier to just increment them here (and start with the right |
371 | * array. Its easier to just increment them here (and start with the right |
388 | * value) than to recalculate them down below. |
372 | * value) than to recalculate them down below. |
389 | */ |
373 | */ |
390 | for (int x = min_x; x <= max_x; x++) |
374 | for (int x = min_x; x <= max_x; x++) |
391 | for (int y = min_y; y <= max_y; y++) |
375 | for (int y = min_y; y <= max_y; y++) |
392 | { |
376 | { |
393 | maptile *m = op->map; |
377 | maptile *m = pl->observe->map; |
394 | sint16 nx = x; |
378 | sint16 nx = x; |
395 | sint16 ny = y; |
379 | sint16 ny = y; |
396 | |
380 | |
397 | if (!xy_normalise (m, nx, ny)) |
381 | if (!xy_normalise (m, nx, ny)) |
398 | continue; |
382 | continue; |
… | |
… | |
403 | |
387 | |
404 | if (expect_false (light)) |
388 | if (expect_false (light)) |
405 | if (light < 0) |
389 | if (light < 0) |
406 | pass2 = 1; |
390 | pass2 = 1; |
407 | else |
391 | else |
408 | apply_light<los_brighten> (op, x - op->x, y - op->y, light, light_atten [light + MAX_LIGHT_RADIUS]); |
392 | apply_light<los_brighten> (pl, x - op->x, y - op->y, light, light_atten [light + MAX_LIGHT_RADIUS]); |
409 | } |
393 | } |
410 | |
394 | |
411 | /* grant some vision to the player, based on the darklevel */ |
395 | /* grant some vision to the player, based on the darklevel */ |
412 | { |
396 | { |
413 | int light = clamp (MAX_DARKNESS - darklevel, 0, MAX_DARKNESS); |
397 | int light = clamp (MAX_DARKNESS - darklevel, 0, MAX_DARKNESS); |
414 | |
398 | |
415 | apply_light<los_brighten> (op, 0, 0, light, vision_atten [light]); |
399 | apply_light<los_brighten> (pl, 0, 0, light, vision_atten [light]); |
416 | } |
400 | } |
417 | } |
401 | } |
418 | |
402 | |
419 | // possibly do 2nd pass for rare negative glow radii |
403 | // possibly do 2nd pass for rare negative glow radii |
420 | // for effect, those are always considered to be stronger than anything else |
404 | // for effect, those are always considered to be stronger than anything else |
421 | // but they can't darken a place completely |
405 | // but they can't darken a place completely |
422 | if (pass2) |
406 | if (pass2) |
423 | for (int x = min_x; x <= max_x; x++) |
407 | for (int x = min_x; x <= max_x; x++) |
424 | for (int y = min_y; y <= max_y; y++) |
408 | for (int y = min_y; y <= max_y; y++) |
425 | { |
409 | { |
426 | maptile *m = op->map; |
410 | maptile *m = pl->observe->map; |
427 | sint16 nx = x; |
411 | sint16 nx = x; |
428 | sint16 ny = y; |
412 | sint16 ny = y; |
429 | |
413 | |
430 | if (!xy_normalise (m, nx, ny)) |
414 | if (!xy_normalise (m, nx, ny)) |
431 | continue; |
415 | continue; |
… | |
… | |
433 | mapspace &ms = m->at (nx, ny); |
417 | mapspace &ms = m->at (nx, ny); |
434 | ms.update (); |
418 | ms.update (); |
435 | sint8 light = ms.light; |
419 | sint8 light = ms.light; |
436 | |
420 | |
437 | if (expect_false (light < 0)) |
421 | if (expect_false (light < 0)) |
438 | apply_light<los_darken> (op, x - op->x, y - op->y, -light, light_atten [light + MAX_LIGHT_RADIUS]); |
422 | apply_light<los_darken> (pl, x - op->x, y - op->y, -light, light_atten [light + MAX_LIGHT_RADIUS]); |
439 | } |
423 | } |
440 | } |
424 | } |
441 | |
425 | |
442 | /* blinded_sight() - sets all viewable squares to blocked except |
426 | /* blinded_sight() - sets all viewable squares to blocked except |
443 | * for the one the central one that the player occupies. A little |
427 | * for the one the central one that the player occupies. A little |
444 | * odd that you can see yourself (and what your standing on), but |
428 | * odd that you can see yourself (and what your standing on), but |
445 | * really need for any reasonable game play. |
429 | * really need for any reasonable game play. |
446 | */ |
430 | */ |
447 | static void |
431 | static void |
448 | blinded_sight (object *op) |
432 | blinded_sight (player *pl) |
449 | { |
433 | { |
450 | op->contr->los[LOS_X0][LOS_Y0] = 1; |
434 | pl->los[LOS_X0][LOS_Y0] = 1; |
451 | } |
435 | } |
452 | |
436 | |
453 | /* |
437 | /* |
454 | * update_los() recalculates the array which specifies what is |
438 | * update_los() recalculates the array which specifies what is |
455 | * visible for the given player-object. |
439 | * visible for the given player-object. |
456 | */ |
440 | */ |
457 | void |
441 | void |
458 | update_los (object *op) |
442 | player::update_los () |
459 | { |
443 | { |
460 | if (QUERY_FLAG (op, FLAG_REMOVED)) |
444 | if (ob->flag [FLAG_REMOVED])//D really needed? |
461 | return; |
445 | return; |
462 | |
446 | |
463 | op->contr->clear_los (); |
447 | clear_los (); |
464 | |
448 | |
465 | if (QUERY_FLAG (op, FLAG_WIZ) /* ||XRAYS(op) */ ) |
449 | if (ob->flag [FLAG_WIZLOOK]) |
466 | memset (op->contr->los, 0, sizeof (op->contr->los)); |
450 | memset (los, 0, sizeof (los)); |
467 | else if (QUERY_FLAG (op, FLAG_BLIND)) /* player is blind */ |
451 | else if (observe->flag [FLAG_BLIND]) /* player is blind */ |
468 | blinded_sight (op); |
452 | blinded_sight (this); |
469 | else |
453 | else |
470 | { |
454 | { |
471 | do_los (op); |
455 | calculate_los (this); |
472 | apply_lights (op); |
456 | apply_lights (this); |
473 | } |
457 | } |
474 | |
458 | |
475 | if (QUERY_FLAG (op, FLAG_XRAYS)) |
459 | if (observe->flag [FLAG_XRAYS]) |
476 | for (int dx = -2; dx <= 2; dx++) |
460 | for (int dx = -2; dx <= 2; dx++) |
477 | for (int dy = -2; dy <= 2; dy++) |
461 | for (int dy = -2; dy <= 2; dy++) |
478 | op->contr->los[dx + LOS_X0][dy + LOS_X0] = 0; |
462 | min_it (los[dx + LOS_X0][dy + LOS_X0], 1); |
479 | } |
463 | } |
480 | |
464 | |
481 | /* update all_map_los is like update_all_los below, |
465 | /* update all_map_los is like update_all_los below, |
482 | * but updates everyone on the map, no matter where they |
466 | * but updates everyone on the map, no matter where they |
483 | * are. This generally should not be used, as a per |
467 | * are. This generally should not be used, as a per |
… | |
… | |
572 | pl->do_los = 1; |
556 | pl->do_los = 1; |
573 | } |
557 | } |
574 | } |
558 | } |
575 | } |
559 | } |
576 | |
560 | |
|
|
561 | static const int season_darkness[5][HOURS_PER_DAY] = { |
|
|
562 | /*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 */ |
|
|
563 | { 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 1, 2, 2, 2, 3, 3, 4, 4, 5 }, |
|
|
564 | { 5, 5, 4, 4, 4, 4, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 }, |
|
|
565 | { 5, 4, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 4 }, |
|
|
566 | { 4, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4 }, |
|
|
567 | { 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4 } |
|
|
568 | }; |
|
|
569 | |
|
|
570 | /* |
|
|
571 | * Tell players the time and compute the darkness level for all maps in the game. |
|
|
572 | * MUST be called exactly once per hour. |
|
|
573 | */ |
|
|
574 | void |
|
|
575 | maptile::adjust_daylight () |
|
|
576 | { |
|
|
577 | timeofday_t tod; |
|
|
578 | |
|
|
579 | get_tod (&tod); |
|
|
580 | |
|
|
581 | // log the time to log-1 every hour, and to chat every day |
|
|
582 | { |
|
|
583 | char todbuf[512]; |
|
|
584 | |
|
|
585 | format_tod (todbuf, sizeof (todbuf), &tod); |
|
|
586 | |
|
|
587 | for_all_players (pl) |
|
|
588 | pl->ns->send_msg (NDI_GREY, tod.hour == 15 ? CHAT_CHANNEL : LOG_CHANNEL, todbuf); |
|
|
589 | } |
|
|
590 | |
|
|
591 | /* If the light level isn't changing, no reason to do all |
|
|
592 | * the work below. |
|
|
593 | */ |
|
|
594 | sint8 new_darkness = season_darkness[tod.season][tod.hour]; |
|
|
595 | |
|
|
596 | if (new_darkness == maptile::outdoor_darkness) |
|
|
597 | return; |
|
|
598 | |
|
|
599 | new_draw_info (NDI_GREY | NDI_UNIQUE | NDI_ALL, 1, 0, |
|
|
600 | new_darkness > maptile::outdoor_darkness |
|
|
601 | ? "It becomes darker." |
|
|
602 | : "It becomes brighter."); |
|
|
603 | |
|
|
604 | maptile::outdoor_darkness = new_darkness; |
|
|
605 | |
|
|
606 | // we simply update the los for all players, which is unnecessarily |
|
|
607 | // costly, but should do for the moment. |
|
|
608 | for_all_players (pl) |
|
|
609 | pl->do_los = 1; |
|
|
610 | } |
|
|
611 | |
577 | /* |
612 | /* |
578 | * make_sure_seen: The object is supposed to be visible through walls, thus |
613 | * make_sure_seen: The object is supposed to be visible through walls, thus |
579 | * check if any players are nearby, and edit their LOS array. |
614 | * check if any players are nearby, and edit their LOS array. |
580 | */ |
615 | */ |
581 | void |
616 | void |