gamerend.cc

Go to the documentation of this file.
00001 /*
00002  *  gamerend.cc - Rendering methods.
00003  *
00004  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00005  *  Copyright (C) 2000-2003  The Exult Team
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020  */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #  include <config.h>
00024 #endif
00025 
00026 #include <cstdio>
00027 
00028 #ifndef HAVE_SNPRINTF
00029 extern int snprintf(char *, size_t, const char *, /*args*/ ...);
00030 namespace std {
00031 using ::snprintf;
00032 }
00033 #else
00034 #endif
00035 
00036 #include "gamewin.h"
00037 #include "gamerend.h"
00038 #include "gameclk.h"
00039 #include "gamemap.h"
00040 #include "actors.h"
00041 #include "chunks.h"
00042 #include "objiter.h"
00043 #include "Gump_manager.h"
00044 #include "Gump.h"
00045 #include "effects.h"
00046 #include "cheat.h"
00047 #include "drag.h"
00048 
00049 /*
00050  *  Paint just the map with given top-left-corner tile.
00051  */
00052 
00053 void Game_window::paint_map_at_tile
00054   (
00055   int x, int y, int w, int h, // Clip to this area.
00056   int toptx, int topty,
00057   int skip_above      // Don't display above this lift.
00058   )
00059   {
00060   int savescrolltx = scrolltx, savescrollty = scrollty;
00061   int saveskip = skip_lift;
00062   scrolltx = toptx;
00063   scrollty = topty;
00064   skip_lift = skip_above;
00065   map->read_map_data();   // Gather in all objs., etc.
00066   win->set_clip(x, y, w, h);
00067   render->paint_map(0, 0, get_width(), get_height());
00068   win->clear_clip();
00069   scrolltx = savescrolltx;
00070   scrollty = savescrollty;
00071   skip_lift = saveskip;
00072   }
00073 
00074 /*
00075  *  Figure offsets on screen.
00076  */
00077 
00078 inline int Figure_screen_offset
00079   (
00080   int ch,       // Chunk #
00081   int scroll      // Top/left tile of screen.
00082   )
00083   {
00084           // Watch for wrapping.
00085   int t = ch*c_tiles_per_chunk - scroll;
00086   if (t < -c_num_tiles/2)
00087     t += c_num_tiles;
00088   t %= c_num_tiles;
00089   return t*c_tilesize;
00090   }
00091 
00092 /*
00093  *  Show the outline around a chunk.
00094  */
00095 
00096 inline void Paint_chunk_outline
00097   (
00098   Game_window *gwin,
00099   int pixel,      // Pixel value to use.
00100   int cx, int cy,     // Chunk coords.
00101   int tnum,     // Terrain #.
00102   int xoff, int yoff    // Where chunk was painted.
00103   )
00104   {
00105   gwin->get_win()->fill8(pixel, c_chunksize, 1, xoff, yoff);
00106   gwin->get_win()->fill8(pixel, 1, c_chunksize, xoff, yoff);
00107   char text[40];      // Show chunk #.
00108   snprintf(text, sizeof(text), "(%d,%d)T%d", cx, cy, tnum);
00109   Shape_manager::get_instance()->paint_text(2, text, xoff + 2, yoff + 2);
00110   }
00111 
00112 /*
00113  *  Paint tile grid.
00114  */
00115 
00116 static void Paint_grid
00117   (
00118   Game_window *gwin,
00119   Xform_palette& xform    // For transparency.
00120   )
00121   {
00122   Image_window8 *win = gwin->get_win();
00123           // Paint grid at edit height.
00124   int xtiles = gwin->get_width()/c_tilesize,
00125       ytiles = gwin->get_height()/c_tilesize;
00126   int lift = cheat.get_edit_lift();
00127   int liftpixels = lift*(c_tilesize/2) + 1;
00128   for (int y = 0; y < ytiles; y++)
00129     win->fill_translucent8(0, xtiles*c_tilesize, 1, 
00130         -liftpixels, y*c_tilesize - liftpixels, xform);
00131   for (int x = 0; x < xtiles; x++)
00132     win->fill_translucent8(0, 1, ytiles*c_tilesize,
00133         x*c_tilesize - liftpixels, -liftpixels, xform);
00134   }
00135 
00136 
00137 /*
00138  *  Just paint terrain.  This is for terrain_editing mode.
00139  */
00140 
00141 void Game_render::paint_terrain_only
00142   (
00143   int start_chunkx, int start_chunky,
00144   int stop_chunkx, int stop_chunky
00145   )
00146   {
00147   Game_window *gwin = Game_window::get_instance();
00148   Game_map *map = gwin->map;
00149   Shape_manager *sman = Shape_manager::get_instance();
00150   int cx, cy;     // Chunk #'s.
00151           // Paint all the flat scenery.
00152   for (cy = start_chunky; cy != stop_chunky; cy = INCR_CHUNK(cy))
00153     {
00154     int yoff = Figure_screen_offset(cy, gwin->scrollty);
00155     for (cx = start_chunkx; cx != stop_chunkx; cx = INCR_CHUNK(cx))
00156       {
00157       int xoff = Figure_screen_offset(cx, gwin->scrolltx);
00158       Map_chunk *chunk = map->get_chunk(cx, cy);
00159       chunk->get_terrain()->render_all(cx, cy);
00160       if (cheat.in_map_editor())
00161         Paint_chunk_outline(gwin, 
00162             sman->get_special_pixel(HIT_PIXEL), cx, cy,
00163             map->get_terrain_num(cx, cy), xoff, yoff);
00164       }
00165     }
00166           // Paint tile grid if desired.
00167   if (cheat.show_tile_grid())
00168     Paint_grid(gwin, sman->get_xform(10));
00169   }
00170 
00171 /*
00172  *  Paint just the map and its objects (no gumps, effects).
00173  *  (The caller should set/clear clip area.)
00174  *
00175  *  Output: # light-sources found.
00176  */
00177 
00178 int Game_render::paint_map
00179   (
00180   int x, int y, int w, int h  // Rectangle to cover.
00181   )
00182   {
00183   Game_window *gwin = Game_window::get_instance();
00184   Game_map *map = gwin->map;
00185   Shape_manager *sman = gwin->shape_man;
00186   render_seq++;     // Increment sequence #.
00187   gwin->painted = 1;
00188   int scrolltx = gwin->scrolltx, scrollty = gwin->scrollty;
00189   int light_sources = 0;    // Count light sources found.
00190           // Get chunks to start with, starting
00191           //   1 tile left/above.
00192   int start_chunkx = (scrolltx + x/c_tilesize - 1)/c_tiles_per_chunk;
00193           // Wrap around.
00194   start_chunkx = (start_chunkx + c_num_chunks)%c_num_chunks;
00195   int start_chunky = (scrollty + y/c_tilesize - 1)/c_tiles_per_chunk;
00196   start_chunky = (start_chunky + c_num_chunks)%c_num_chunks;
00197           // End 8 tiles to right.
00198   int stop_chunkx = 1 + (scrolltx + (x + w + c_tilesize - 2)/c_tilesize + 
00199           c_tiles_per_chunk/2)/c_tiles_per_chunk;
00200   int stop_chunky = 1 + (scrollty + (y + h + c_tilesize - 2)/c_tilesize + 
00201           c_tiles_per_chunk/2)/c_tiles_per_chunk;
00202           // Wrap around the world:
00203   stop_chunkx = (stop_chunkx + c_num_chunks)%c_num_chunks;
00204   stop_chunky = (stop_chunky + c_num_chunks)%c_num_chunks;
00205   if (!gwin->skip_lift)   // Special mode for editing?
00206     {
00207     paint_terrain_only(start_chunkx, start_chunky,
00208             stop_chunkx, stop_chunky);
00209     return 10;    // Pretend there's lots of light!
00210     }
00211   int cx, cy;     // Chunk #'s.
00212           // Paint all the flat scenery.
00213   for (cy = start_chunky; cy != stop_chunky; cy = INCR_CHUNK(cy))
00214     {
00215     int yoff = Figure_screen_offset(cy, scrollty);
00216     for (cx = start_chunkx; cx != stop_chunkx; cx = INCR_CHUNK(cx))
00217       {
00218       int xoff = Figure_screen_offset(cx, scrolltx);
00219       paint_chunk_flats(cx, cy, xoff, yoff);
00220 
00221       if (cheat.in_map_editor())
00222         Paint_chunk_outline(gwin, 
00223             sman->get_special_pixel(HIT_PIXEL), cx, cy,
00224             map->get_terrain_num(cx, cy), xoff, yoff);
00225       }
00226     }
00227           // Now the flat RLE terrain.
00228   for (cy = start_chunky; cy != stop_chunky; cy = INCR_CHUNK(cy))
00229     {
00230     int yoff = Figure_screen_offset(cy, scrollty);
00231     for (cx = start_chunkx; cx != stop_chunkx; cx = INCR_CHUNK(cx))
00232       {
00233       int xoff = Figure_screen_offset(cx, scrolltx);
00234       paint_chunk_flat_rles(cx, cy, xoff, yoff);
00235 
00236       if (cheat.in_map_editor())
00237         Paint_chunk_outline(gwin, 
00238             sman->get_special_pixel(HIT_PIXEL), cx, cy,
00239             map->get_terrain_num(cx, cy), xoff, yoff);
00240       }
00241     }
00242           // Draw the chunks' objects
00243           //   diagonally NE.
00244   int tmp_stopy = DECR_CHUNK(start_chunky);
00245   for (cy = start_chunky; cy != stop_chunky; cy = INCR_CHUNK(cy))
00246     {
00247     for (int dx = start_chunkx, dy = cy;
00248       dx != stop_chunkx && dy != tmp_stopy; 
00249         dx = INCR_CHUNK(dx), dy = DECR_CHUNK(dy))
00250       light_sources += paint_chunk_objects(dx, dy);
00251     }
00252   for (cx = (start_chunkx + 1)%c_num_chunks; cx != stop_chunkx; 
00253               cx = INCR_CHUNK(cx))
00254     {
00255     for (int dx = cx, 
00256       dy = (stop_chunky - 1 + c_num_chunks)%c_num_chunks; 
00257       dx != stop_chunkx && dy != tmp_stopy; 
00258         dx = INCR_CHUNK(dx), dy = DECR_CHUNK(dy))
00259       light_sources += paint_chunk_objects(dx, dy);
00260     }
00261 
00263   if (gwin->in_dungeon >= gwin->skip_above_actor && 
00264               !cheat.in_map_editor())
00265     paint_blackness (start_chunkx, start_chunky, stop_chunkx, 
00266           stop_chunky, gwin->ice_dungeon?73:0);
00267 
00268           // Outline selected objects.
00269   const Game_object_vector& sel = cheat.get_selected();
00270   int render_skip = gwin->get_render_skip_lift();
00271   for (Game_object_vector::const_iterator it = sel.begin();
00272             it != sel.end(); ++it)
00273     {
00274     Game_object *obj = *it;
00275     if (!obj->get_owner() && obj->get_lift() < render_skip)
00276       obj->paint_outline(HIT_PIXEL);
00277     }
00278           // Paint tile grid if desired.
00279   if (cheat.in_map_editor() && cheat.show_tile_grid())
00280     Paint_grid(gwin, sman->get_xform(10));
00281 
00282   return light_sources;
00283   }
00284 
00285 /*
00286  *  Paint a rectangle in the window by pulling in vga chunks.
00287  */
00288 
00289 void Game_window::paint
00290   (
00291   int x, int y, int w, int h  // Rectangle to cover.
00292   )
00293   {
00294   if (!win->ready()) return;
00295 
00296   win->set_clip(x, y, w, h);  // Clip to this area.
00297   int light_sources = render->paint_map(x, y, w, h);
00298 
00299   effects->paint();   // Draw sprites.
00300   if (gump_man->modal_gump_mode())// Modal gumps?
00301     {     // Draw text, then gumps.
00302     effects->paint_text();
00303     gump_man->paint();
00304     }
00305   else
00306     {     // Draw gumps unless in dont_move mode.
00307     if (!main_actor_dont_move())
00308       gump_man->paint();
00309     effects->paint_text();  // Draw text over gumps.
00310     }
00311   if (dragging)
00312     dragging->paint();  // Paint what user is dragging.
00313   win->clear_clip();
00314           // Complete repaint?
00315   if (!x && !y && w == get_width() && h == get_height() && main_actor)
00316     {     // Look for lights.
00317     Actor *party[9];  // Get party, including Avatar.
00318     int cnt = get_party(party, 1);
00319     int carried_light = 0;
00320     for (int i = 0; !carried_light && i < cnt; i++)
00321       carried_light = party[i]->has_light_source();
00322           // Also check light spell.
00323     if (special_light && clock->get_total_minutes() >special_light)
00324       {   // Just expired.
00325       special_light = 0;
00326       clock->set_palette();
00327       }
00328           // Set palette for lights.
00329     clock->set_light_source(carried_light + (light_sources > 0),
00330                 in_dungeon);
00331     }
00332   }
00333 
00334 /*
00335  *  Paint whole window.
00336  */
00337 void Game_window::paint()
00338   {
00339   map->read_map_data();   // Gather in all objs., etc.
00340   set_all_dirty();
00341   paint_dirty();
00342   }
00343 
00344 /*
00345  *  Paint the flat (non-rle) shapes in a chunk.
00346  */
00347 
00348 void Game_render::paint_chunk_flats
00349   (
00350   int cx, int cy,     // Chunk coords (0 - 12*16).
00351   int xoff, int yoff    // Pixel offset of top-of-screen.
00352   )
00353   {
00354   Game_window *gwin = Game_window::get_instance();
00355   Map_chunk *olist = gwin->map->get_chunk(cx, cy);
00356           // Paint flat tiles.
00357 #ifdef HAVE_OPENGL
00358   if (GL_manager::get_instance()) // OpenGL rendering?
00359     {
00360     Chunk_terrain *terrain = olist->get_terrain();
00361     if (terrain)
00362       terrain->get_glflats()->paint(xoff, yoff);
00363     }
00364   else
00365 #endif
00366     {
00367     Image_buffer8 *cflats = olist->get_rendered_flats();
00368     if (cflats)
00369       gwin->win->copy8(cflats->get_bits(), 
00370         c_chunksize, c_chunksize, xoff, yoff);
00371     }
00372   }
00373 
00374 /*
00375  *  Paint the flat RLE (terrain) shapes in a chunk.
00376  */
00377 
00378 void Game_render::paint_chunk_flat_rles
00379   (
00380   int cx, int cy,     // Chunk coords (0 - 12*16).
00381   int xoff, int yoff    // Pixel offset of top-of-screen.
00382   )
00383   {
00384   Game_window *gwin = Game_window::get_instance();
00385   Map_chunk *olist = gwin->map->get_chunk(cx, cy);
00386   Flat_object_iterator next(olist);// Do flat RLE objects.
00387   Game_object *obj;
00388   while ((obj = next.get_next()) != 0)
00389     obj->paint();
00390   }
00391 
00392 /*
00393  *  Paint a chunk's objects, left-to-right, top-to-bottom.
00394  *
00395  *  Output: # light sources found.
00396  */
00397 
00398 int Game_render::paint_chunk_objects
00399   (
00400   int cx, int cy      // Chunk coords (0 - 12*16).
00401   )
00402   {
00403   Game_object *obj;
00404   Game_window *gwin = Game_window::get_instance();
00405   Map_chunk *olist = gwin->map->get_chunk(cx, cy);
00406   int light_sources =   // Also check for light sources.
00407       gwin->is_in_dungeon() ? olist->get_dungeon_lights()
00408           : olist->get_non_dungeon_lights();
00409   skip = gwin->get_render_skip_lift();
00410   Nonflat_object_iterator next(olist);
00411 
00412   while ((obj = next.get_next()) != 0)
00413     if (obj->render_seq != render_seq)
00414       paint_object(obj);
00415 
00416   skip = 31;      // Back to a safe #.
00417   return light_sources;
00418   }
00419 
00420 /*
00421  *  Render an object after first rendering any that it depends on.
00422  */
00423 
00424 void Game_render::paint_object
00425   (
00426   Game_object *obj
00427   )
00428   {
00429   int lift = obj->get_lift();
00430   if (lift >= skip)
00431     return;
00432   obj->render_seq = render_seq;
00433   int cnt = obj->get_dependency_count();
00434   for (int i = 0; i < cnt; i++)
00435     {
00436     Game_object *dep = obj->get_dependency(i);
00437     if (dep && dep->render_seq != render_seq)
00438       paint_object(dep);
00439     }
00440   obj->paint();     // Finally, paint this one.
00441   }
00442 
00443 /*
00444  *  Paint 'dirty' rectangle.
00445  */
00446 
00447 void Game_window::paint_dirty()
00448 {
00449   // Update the gumps before painting, unless in dont_move mode (may change dirty area)
00450     if (!main_actor_dont_move())
00451         gump_man->update_gumps();
00452 
00453   Rectangle box = clip_to_win(dirty);
00454   if (box.w > 0 && box.h > 0)
00455     paint(box); // (Could create new dirty rects.)
00456 
00457   clear_dirty();
00458 }
00459 
00460 /*
00461  *  Dungeon Blacking
00462  *
00463  *  This is really simple. If there is a dungeon roof over our head we
00464  *  black out every tile on screen that doens't have a roof at the height
00465  *  of the roof that is directly over our head. The tiles are blacked out
00466  *  at the height of the the roof. 
00467  *
00468  *  I've done some simple optimizations. Generally all the blackness will
00469  *  cover entire chunks. So, instead of drawing each tile individually, I
00470  *  work out home many tiles in a row that need to be blacked out, and then
00471  *  black them all out at the same time.
00472  */
00473 
00474 void Game_render::paint_blackness(int start_chunkx, int start_chunky, int stop_chunkx, int stop_chunky, int index)
00475 {
00476   Game_window *gwin = Game_window::get_instance();
00477   // Calculate the offset due to the lift (4x the lift).
00478   const int off = gwin->in_dungeon << 2;
00479 
00480   // For each chunk that might be renderable
00481   for (int cy = start_chunky; cy != stop_chunky; cy = INCR_CHUNK(cy))
00482   {
00483     for (int cx = start_chunkx; cx != stop_chunkx; cx = INCR_CHUNK(cx))
00484     {
00485       // Coord of the left edge
00486       const int xoff = 
00487         Figure_screen_offset(cx, gwin->scrolltx) - off;
00488       // Coord of the top edge 
00489       int y = Figure_screen_offset(cy, gwin->scrollty) - off;
00490 
00491       // Need the chunk cache (needs to be setup!)
00492       Map_chunk *mc = gwin->map->get_chunk(cx, cy);
00493       if (!mc->has_dungeon())
00494       {
00495         gwin->win->fill8(index, 
00496           c_tilesize*c_tiles_per_chunk,
00497           c_tilesize*c_tiles_per_chunk, xoff, y);
00498         continue;
00499       }
00500       // For each line in the chunk
00501       for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
00502       {
00503         // Start and width of the area to black out
00504         int x = xoff;
00505         int w = 0;
00506 
00507         // For each tile in the line
00508         for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++)
00509         {
00510           // If the tile is blocked by 'roof'
00511           if (!mc->is_dungeon(tilex, tiley))
00512           {
00513             // Add to the width of the area
00514             w += c_tilesize;
00515           }
00516           // If not blocked and have area,
00517           else if (w)
00518           {
00519             // Draw blackness
00520             gwin->win->fill8(index, w, c_tilesize, x, y); 
00521 
00522             // Set the start of the area to the next tile
00523             x += w + c_tilesize;
00524 
00525             // Clear the width
00526             w = 0;  
00527           }
00528           // Not blocked, and no area
00529           else
00530           {
00531             // Increment the start of the area to the next tile
00532             x += c_tilesize;
00533           }
00534 
00535         }
00536 
00537         // If we have an area, paint it.
00538         if (w) 
00539           gwin->win->fill8(index, w, c_tilesize,
00540                    x, y);
00541 
00542         // Increment the y coord for the next line
00543         y += c_tilesize;
00544       }
00545     }
00546   }
00547 }

Generated on Mon Jul 9 14:42:46 2007 for ExultEngine by  doxygen 1.5.1