--- deliantra/server/common/los.C 2008/12/20 02:32:31 1.42 +++ deliantra/server/common/los.C 2008/12/23 06:58:23 1.48 @@ -35,12 +35,12 @@ struct los_info { - sint8 xo, yo; // obscure angle - sint8 xe, ye; // angle deviation - uint8 culled; - uint8 queued; + sint8 xo, yo; // obscure angle + sint8 xe, ye; // angle deviation + uint8 culled; // culled from "tree" + uint8 queued; // already queued uint8 visible; - uint8 flags; + uint8 flags; // LOS_XI/YI }; // temporary storage for the los algorithm, @@ -101,10 +101,8 @@ // which has been simplified and changed considerably, but // still is basically the same algorithm. static void -do_los (object *op) +calculate_los (player *pl) { - player *pl = op->contr; - int max_radius = max (pl->ns->mapx, pl->ns->mapy) / 2; memset (los, 0, sizeof (los)); @@ -210,9 +208,9 @@ } // check whether this space blocks the view - maptile *m = op->map; - sint16 nx = op->x + dx; - sint16 ny = op->y + dy; + maptile *m = pl->observe->map; + sint16 nx = pl->observe->x + dx; + sint16 ny = pl->observe->y + dy; if (expect_true (!xy_normalise (m, nx, ny)) || expect_false (m->at (nx, ny).flags () & P_BLOCKSVIEW)) @@ -269,12 +267,14 @@ } /* radius, distance => lightness adjust */ -static sint8 darkness[MAX_LIGHT_RADIUS * 2 + 1][MAX_LIGHT_RADIUS * 3 / 2 + 1]; +static sint8 light_atten[MAX_LIGHT_RADIUS * 2 + 1][MAX_LIGHT_RADIUS * 3 / 2 + 1]; +static sint8 vision_atten[MAX_DARKNESS + 1][MAX_DARKNESS * 3 / 2 + 1]; -static struct darkness_init +static struct los_init { - darkness_init () + los_init () { + /* for lights */ for (int radius = -MAX_LIGHT_RADIUS; radius <= MAX_LIGHT_RADIUS; ++radius) for (int distance = 0; distance <= MAX_LIGHT_RADIUS * 3 / 2; ++distance) { @@ -284,12 +284,19 @@ // actual intensity intensity = max (0, lerp_rd (distance, 0, abs (radius) + 1, intensity, 0)); - darkness [radius + MAX_LIGHT_RADIUS][distance] = radius < 0 + light_atten [radius + MAX_LIGHT_RADIUS][distance] = radius < 0 ? min (3, intensity) : LOS_MAX - intensity; } + + /* for general vision */ + for (int radius = 0; radius <= MAX_DARKNESS; ++radius) + for (int distance = 0; distance <= MAX_DARKNESS * 3 / 2; ++distance) + { + vision_atten [radius][distance] = distance <= radius ? 3 : 4; + } } -} darkness_init; +} los_init; sint8 los_brighten (sint8 b, sint8 l) @@ -305,16 +312,14 @@ template static void -apply_light (object *op, int dx, int dy, int light, const sint8 *darkness_table) +apply_light (player *pl, int dx, int dy, int light, const sint8 *atten_table) { // min or max the circular area around basex, basey - player *pl = op->contr; - dx += LOS_X0; dy += LOS_Y0; - int hx = op->contr->ns->mapx / 2; - int hy = op->contr->ns->mapy / 2; + int hx = pl->ns->mapx / 2; + int hy = pl->ns->mapy / 2; int ax0 = max (LOS_X0 - hx, dx - light); int ay0 = max (LOS_Y0 - hy, dy - light); @@ -324,36 +329,24 @@ for (int ax = ax0; ax <= ax1; ax++) for (int ay = ay0; ay <= ay1; ay++) pl->los[ax][ay] = - change_it (pl->los[ax][ay], darkness_table [idistance (ax - dx, ay - dy)]); + change_it (pl->los[ax][ay], atten_table [idistance (ax - dx, ay - dy)]); } /* add light, by finding all (non-null) nearby light sources, then * mark those squares specially. */ static void -apply_lights (object *op) +apply_lights (player *pl) { - int darklevel, mflags, light, x1, y1; - maptile *m = op->map; - sint16 nx, ny; - - darklevel = m->darkness; + object *op = pl->observe; + int darklevel = op->map->darklevel (); /* If the player can see in the dark, lower the darklevel for him */ - if (QUERY_FLAG (op, FLAG_SEE_IN_DARK)) - darklevel -= LOS_MAX / 2; + if (op->flag [FLAG_SEE_IN_DARK]) + darklevel = max (0, darklevel - 2); - /* Do a sanity check. If not valid, some code below may do odd - * things. - */ - if (darklevel > MAX_DARKNESS) - { - LOG (llevError, "Map darkness for %s on %s is too high (%d)\n", &op->name, &op->map->path, darklevel); - darklevel = MAX_DARKNESS; - } - - int half_x = op->contr->ns->mapx / 2; - int half_y = op->contr->ns->mapy / 2; + int half_x = pl->ns->mapx / 2; + int half_y = pl->ns->mapy / 2; int min_x = op->x - half_x - MAX_LIGHT_RADIUS; int min_y = op->y - half_y - MAX_LIGHT_RADIUS; @@ -362,15 +355,15 @@ int pass2 = 0; // negative lights have an extra pass - if (darklevel < 1) + if (!darklevel) pass2 = 1; else { /* first, make everything totally dark */ for (int dx = -half_x; dx <= half_x; dx++) for (int dy = -half_x; dy <= half_y; dy++) - if (op->contr->los[dx + LOS_X0][dy + LOS_Y0] != LOS_BLOCKED) - op->contr->los[dx + LOS_X0][dy + LOS_Y0] = LOS_MAX; + if (pl->los[dx + LOS_X0][dy + LOS_Y0] != LOS_BLOCKED) + pl->los[dx + LOS_X0][dy + LOS_Y0] = LOS_MAX; /* * Only process the area of interest. @@ -381,7 +374,7 @@ for (int x = min_x; x <= max_x; x++) for (int y = min_y; y <= max_y; y++) { - maptile *m = op->map; + maptile *m = pl->observe->map; sint16 nx = x; sint16 ny = y; @@ -396,15 +389,14 @@ if (light < 0) pass2 = 1; else - apply_light (op, x - op->x, y - op->y, light, darkness [light + MAX_LIGHT_RADIUS]); + apply_light (pl, x - op->x, y - op->y, light, light_atten [light + MAX_LIGHT_RADIUS]); } /* grant some vision to the player, based on the darklevel */ - /* for outdoor maps, ensure some mininum visibility radius */ { - int light = clamp (MAX_DARKNESS - darklevel, op->map->outdoor ? 2 : 0, MAX_LIGHT_RADIUS); + int light = clamp (MAX_DARKNESS - darklevel, 0, MAX_DARKNESS); - apply_light (op, 0, 0, light, darkness [light + MAX_LIGHT_RADIUS]); + apply_light (pl, 0, 0, light, vision_atten [light]); } } @@ -415,7 +407,7 @@ for (int x = min_x; x <= max_x; x++) for (int y = min_y; y <= max_y; y++) { - maptile *m = op->map; + maptile *m = pl->observe->map; sint16 nx = x; sint16 ny = y; @@ -427,7 +419,7 @@ sint8 light = ms.light; if (expect_false (light < 0)) - apply_light (op, x - op->x, y - op->y, -light, darkness [light + MAX_LIGHT_RADIUS]); + apply_light (pl, x - op->x, y - op->y, -light, light_atten [light + MAX_LIGHT_RADIUS]); } } @@ -437,9 +429,9 @@ * really need for any reasonable game play. */ static void -blinded_sight (object *op) +blinded_sight (player *pl) { - op->contr->los[LOS_X0][LOS_Y0] = 3; + pl->los[LOS_X0][LOS_Y0] = 1; } /* @@ -447,27 +439,27 @@ * visible for the given player-object. */ void -update_los (object *op) +player::update_los () { - if (QUERY_FLAG (op, FLAG_REMOVED)) + if (ob->flag [FLAG_REMOVED])//D really needed? return; - op->contr->clear_los (); + clear_los (); - if (QUERY_FLAG (op, FLAG_WIZ) /* ||XRAYS(op) */ ) - memset (op->contr->los, 0, sizeof (op->contr->los)); - else if (QUERY_FLAG (op, FLAG_BLIND)) /* player is blind */ - blinded_sight (op); + if (ob->flag [FLAG_WIZLOOK]) + memset (los, 0, sizeof (los)); + else if (observe->flag [FLAG_BLIND]) /* player is blind */ + blinded_sight (this); else { - do_los (op); - apply_lights (op); + calculate_los (this); + apply_lights (this); } - if (QUERY_FLAG (op, FLAG_XRAYS)) + if (observe->flag [FLAG_XRAYS]) for (int dx = -2; dx <= 2; dx++) for (int dy = -2; dy <= 2; dy++) - op->contr->los[dx + LOS_X0][dy + LOS_X0] = 0; + min_it (los[dx + LOS_X0][dy + LOS_X0], 1); } /* update all_map_los is like update_all_los below, @@ -484,9 +476,8 @@ void update_all_map_los (maptile *map) { - for_all_players (pl) - if (pl->ob && pl->ob->map == map) - pl->do_los = 1; + for_all_players_on_map (pl, map) + pl->do_los = 1; } /* @@ -504,6 +495,8 @@ void update_all_los (const maptile *map, int x, int y) { + map->at (x, y).invalidate (); + for_all_players (pl) { /* Player should not have a null map, but do this @@ -565,6 +558,57 @@ } } +static const int season_darkness[5][HOURS_PER_DAY] = { + /*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 */ + { 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 }, + { 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 }, + { 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 }, + { 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 }, + { 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 } +}; + +/* + * Tell players the time and compute the darkness level for all maps in the game. + * MUST be called exactly once per hour. + */ +void +maptile::adjust_daylight () +{ + timeofday_t tod; + + get_tod (&tod); + + // log the time to log-1 every hour, and to chat every day + { + char todbuf[512]; + + format_tod (todbuf, sizeof (todbuf), &tod); + + for_all_players (pl) + pl->ns->send_msg (NDI_GREY, tod.hour == 15 ? CHAT_CHANNEL : LOG_CHANNEL, todbuf); + } + + /* If the light level isn't changing, no reason to do all + * the work below. + */ + sint8 new_darkness = season_darkness[tod.season][tod.hour]; + + if (new_darkness == maptile::outdoor_darkness) + return; + + new_draw_info (NDI_GREY | NDI_UNIQUE | NDI_ALL, 1, 0, + new_darkness > maptile::outdoor_darkness + ? "It becomes darker." + : "It becomes brighter."); + + maptile::outdoor_darkness = new_darkness; + + // we simply update the los for all players, which is unnecessarily + // costly, but should do for the moment. + for_all_players (pl) + pl->do_los = 1; +} + /* * make_sure_seen: The object is supposed to be visible through walls, thus * check if any players are nearby, and edit their LOS array.