… | |
… | |
33 | LOS_YI = 0x02, |
33 | LOS_YI = 0x02, |
34 | }; |
34 | }; |
35 | |
35 | |
36 | struct los_info |
36 | struct los_info |
37 | { |
37 | { |
38 | sint8 xo, yo; // obscure angle |
38 | sint8 xo, yo; // obscure angle |
39 | sint8 xe, ye; // angle deviation |
39 | sint8 xe, ye; // angle deviation |
40 | uint8 culled; |
40 | uint8 culled; // culled from "tree" |
41 | uint8 queued; |
41 | uint8 queued; // already queued |
42 | uint8 visible; |
42 | uint8 visible; |
43 | uint8 flags; |
43 | uint8 flags; // LOS_XI/YI |
44 | }; |
44 | }; |
45 | |
45 | |
46 | // temporary storage for the los algorithm, |
46 | // temporary storage for the los algorithm, |
47 | // one los_info for each lightable map space |
47 | // one los_info for each lightable map space |
48 | static los_info los[MAP_CLIENT_X][MAP_CLIENT_Y]; |
48 | static los_info los[MAP_CLIENT_X][MAP_CLIENT_Y]; |
… | |
… | |
62 | /* |
62 | /* |
63 | * Clears/initialises the los-array associated to the player |
63 | * Clears/initialises the los-array associated to the player |
64 | * controlling the object. |
64 | * controlling the object. |
65 | */ |
65 | */ |
66 | void |
66 | void |
67 | clear_los (player *pl) |
67 | player::clear_los (sint8 value) |
68 | { |
68 | { |
69 | memset (pl->los, LOS_BLOCKED, sizeof (pl->los)); |
69 | memset (los, value, sizeof (los)); |
70 | } |
70 | } |
71 | |
71 | |
72 | // enqueue a single mapspace, but only if it hasn't |
72 | // enqueue a single mapspace, but only if it hasn't |
73 | // been enqueued yet. |
73 | // been enqueued yet. |
74 | static void |
74 | static void |
… | |
… | |
267 | |
267 | |
268 | return 0; |
268 | return 0; |
269 | } |
269 | } |
270 | |
270 | |
271 | /* radius, distance => lightness adjust */ |
271 | /* radius, distance => lightness adjust */ |
272 | static sint8 darkness[MAX_LIGHT_RADIUS * 2 + 1][MAX_LIGHT_RADIUS * 3 / 2 + 1]; |
272 | static sint8 light_atten[MAX_LIGHT_RADIUS * 2 + 1][MAX_LIGHT_RADIUS * 3 / 2 + 1]; |
273 | |
273 | |
274 | static struct darkness_init |
274 | static struct los_init |
275 | { |
275 | { |
276 | darkness_init () |
276 | los_init () |
277 | { |
277 | { |
278 | for (int radius = -MAX_LIGHT_RADIUS; radius <= MAX_LIGHT_RADIUS; ++radius) |
278 | for (int radius = -MAX_LIGHT_RADIUS; radius <= MAX_LIGHT_RADIUS; ++radius) |
279 | for (int distance = 0; distance <= MAX_LIGHT_RADIUS * 3 / 2; ++distance) |
279 | for (int distance = 0; distance <= MAX_LIGHT_RADIUS * 3 / 2; ++distance) |
280 | { |
280 | { |
281 | // max intensity |
281 | // max intensity |
282 | int intensity = min (LOS_MAX, abs (radius) + 1); |
282 | int intensity = min (LOS_MAX, abs (radius) + 1); |
283 | |
283 | |
284 | // actual intensity |
284 | // actual intensity |
285 | intensity = max (0, lerp_rd (distance, 0, abs (radius) + 1, intensity, 0)); |
285 | intensity = max (0, lerp_rd (distance, 0, abs (radius) + 1, intensity, 0)); |
286 | |
286 | |
287 | darkness [radius + MAX_LIGHT_RADIUS][distance] = radius < 0 |
287 | light_atten [radius + MAX_LIGHT_RADIUS][distance] = radius < 0 |
288 | ? min (3, intensity) |
288 | ? min (3, intensity) |
289 | : LOS_MAX - intensity; |
289 | : LOS_MAX - intensity; |
290 | } |
290 | } |
291 | } |
291 | } |
292 | } darkness_init; |
292 | } los_init; |
293 | |
293 | |
294 | sint8 |
294 | sint8 |
295 | los_brighten (sint8 b, sint8 l) |
295 | los_brighten (sint8 b, sint8 l) |
296 | { |
296 | { |
297 | return b == LOS_BLOCKED ? b : min (b, l); |
297 | return b == LOS_BLOCKED ? b : min (b, l); |
… | |
… | |
303 | return max (b, l); |
303 | return max (b, l); |
304 | } |
304 | } |
305 | |
305 | |
306 | template<sint8 change_it (sint8, sint8)> |
306 | template<sint8 change_it (sint8, sint8)> |
307 | static void |
307 | static void |
308 | apply_light (object *op, int dx, int dy, int light, const sint8 *darkness_table) |
308 | apply_light (object *op, int dx, int dy, int light, const sint8 *atten_table) |
309 | { |
309 | { |
310 | // min or max the circular area around basex, basey |
310 | // min or max the circular area around basex, basey |
311 | player *pl = op->contr; |
311 | player *pl = op->contr; |
312 | |
312 | |
313 | dx += LOS_X0; |
313 | dx += LOS_X0; |
… | |
… | |
322 | int ay1 = min (dy + light, LOS_Y0 + hy); |
322 | int ay1 = min (dy + light, LOS_Y0 + hy); |
323 | |
323 | |
324 | for (int ax = ax0; ax <= ax1; ax++) |
324 | for (int ax = ax0; ax <= ax1; ax++) |
325 | for (int ay = ay0; ay <= ay1; ay++) |
325 | for (int ay = ay0; ay <= ay1; ay++) |
326 | pl->los[ax][ay] = |
326 | pl->los[ax][ay] = |
327 | change_it (pl->los[ax][ay], darkness_table [idistance (ax - dx, ay - dy)]); |
327 | change_it (pl->los[ax][ay], atten_table [idistance (ax - dx, ay - dy)]); |
328 | } |
328 | } |
329 | |
329 | |
330 | /* add light, by finding all (non-null) nearby light sources, then |
330 | /* add light, by finding all (non-null) nearby light sources, then |
331 | * mark those squares specially. |
331 | * mark those squares specially. |
332 | */ |
332 | */ |
… | |
… | |
394 | |
394 | |
395 | if (expect_false (light)) |
395 | if (expect_false (light)) |
396 | if (light < 0) |
396 | if (light < 0) |
397 | pass2 = 1; |
397 | pass2 = 1; |
398 | else |
398 | else |
399 | apply_light<los_brighten> (op, x - op->x, y - op->y, light, darkness [light + MAX_LIGHT_RADIUS]); |
399 | apply_light<los_brighten> (op, x - op->x, y - op->y, light, light_atten [light + MAX_LIGHT_RADIUS]); |
400 | } |
400 | } |
401 | |
401 | |
402 | /* grant some vision to the player, based on the darklevel */ |
402 | /* grant some vision to the player, based on the darklevel */ |
403 | /* for outdoor maps, ensure some mininum visibility radius */ |
|
|
404 | { |
403 | { |
405 | int light = clamp (MAX_DARKNESS - darklevel, op->map->outdoor ? 2 : 0, MAX_LIGHT_RADIUS); |
404 | int light = clamp (MAX_DARKNESS - darklevel, 0, MAX_LIGHT_RADIUS); |
406 | |
405 | |
407 | apply_light<los_brighten> (op, 0, 0, light, darkness [light + MAX_LIGHT_RADIUS]); |
406 | apply_light<los_brighten> (op, 0, 0, light, light_atten [light + MAX_LIGHT_RADIUS]); |
408 | } |
407 | } |
409 | } |
408 | } |
410 | |
409 | |
411 | // possibly do 2nd pass for rare negative glow radii |
410 | // possibly do 2nd pass for rare negative glow radii |
412 | // for effect, those are always considered to be stronger than anything else |
411 | // for effect, those are always considered to be stronger than anything else |
… | |
… | |
425 | mapspace &ms = m->at (nx, ny); |
424 | mapspace &ms = m->at (nx, ny); |
426 | ms.update (); |
425 | ms.update (); |
427 | sint8 light = ms.light; |
426 | sint8 light = ms.light; |
428 | |
427 | |
429 | if (expect_false (light < 0)) |
428 | if (expect_false (light < 0)) |
430 | apply_light<los_darken> (op, x - op->x, y - op->y, -light, darkness [light + MAX_LIGHT_RADIUS]); |
429 | apply_light<los_darken> (op, x - op->x, y - op->y, -light, light_atten [light + MAX_LIGHT_RADIUS]); |
431 | } |
430 | } |
432 | } |
431 | } |
433 | |
432 | |
434 | /* blinded_sight() - sets all viewable squares to blocked except |
433 | /* blinded_sight() - sets all viewable squares to blocked except |
435 | * for the one the central one that the player occupies. A little |
434 | * for the one the central one that the player occupies. A little |
… | |
… | |
450 | update_los (object *op) |
449 | update_los (object *op) |
451 | { |
450 | { |
452 | if (QUERY_FLAG (op, FLAG_REMOVED)) |
451 | if (QUERY_FLAG (op, FLAG_REMOVED)) |
453 | return; |
452 | return; |
454 | |
453 | |
455 | clear_los (op->contr); |
454 | op->contr->clear_los (); |
456 | |
455 | |
457 | if (QUERY_FLAG (op, FLAG_WIZ) /* ||XRAYS(op) */ ) |
456 | if (QUERY_FLAG (op, FLAG_WIZ) /* ||XRAYS(op) */ ) |
458 | memset (op->contr->los, 0, sizeof (op->contr->los)); |
457 | memset (op->contr->los, 0, sizeof (op->contr->los)); |
459 | else if (QUERY_FLAG (op, FLAG_BLIND)) /* player is blind */ |
458 | else if (QUERY_FLAG (op, FLAG_BLIND)) /* player is blind */ |
460 | blinded_sight (op); |
459 | blinded_sight (op); |