1 /**
2 ** Chunkter.cc - Chunk terrain (16x16 flat tiles) on the map.
3 **
4 ** Written: 7/6/01 - JSF
5 **/
6
7 /*
8 Copyright (C) 2001-2013 The Exult Team
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28 #include "chunkter.h"
29 #include "gamewin.h"
30
31 #include <cstring>
32
33 Chunk_terrain *Chunk_terrain::render_queue = nullptr;
34 int Chunk_terrain::queue_size = 0;
35
36 /*
37 * Insert at start of render queue. It may already be there, but it's
38 * assumed that it's already tested as not being at the start.
39 */
40
insert_in_queue()41 void Chunk_terrain::insert_in_queue(
42 ) {
43 if (render_queue_next) { // In queue already?
44 // !!Assuming it's not at head!!
45 render_queue_next->render_queue_prev = render_queue_prev;
46 render_queue_prev->render_queue_next = render_queue_next;
47 } else
48 queue_size++; // Adding, so increment count.
49 if (!render_queue) // First?
50 render_queue_next = render_queue_prev = this;
51 else {
52 render_queue_next = render_queue;
53 render_queue_prev = render_queue->render_queue_prev;
54 render_queue_prev->render_queue_next = this;
55 render_queue->render_queue_prev = this;
56 }
57 render_queue = this;
58 }
59
60 /*
61 * Remove from render queue.
62 */
63
remove_from_queue()64 void Chunk_terrain::remove_from_queue(
65 ) {
66 if (!render_queue_next)
67 return; // Not in queue.
68 queue_size--;
69 if (render_queue_next == this) // Only element?
70 render_queue = nullptr;
71 else {
72 if (render_queue == this)
73 render_queue = render_queue_next;
74 render_queue_next->render_queue_prev = render_queue_prev;
75 render_queue_prev->render_queue_next = render_queue_next;
76 }
77 render_queue_next = render_queue_prev = nullptr;
78 }
79
80 /*
81 * Paint a flat tile into our cached buffer.
82 */
83
paint_tile(int tilex,int tiley)84 inline void Chunk_terrain::paint_tile(
85 int tilex, int tiley // Tile within chunk.
86 ) {
87 Shape_frame *shape = get_shape(tilex, tiley);
88 if (shape && !shape->is_rle()) // Only do flat tiles.
89 rendered_flats->copy8(shape->get_data(),
90 c_tilesize, c_tilesize, tilex * c_tilesize,
91 tiley * c_tilesize);
92 }
93
94 /*
95 * Create list for a given chunk.
96 */
97
Chunk_terrain(const unsigned char * data,bool v2_chunks)98 Chunk_terrain::Chunk_terrain(
99 const unsigned char *data, // Chunk data.
100 bool v2_chunks // 3 bytes/shape.
101 ) : undo_shapes(nullptr),
102 num_clients(0), modified(false), rendered_flats(nullptr),
103 render_queue_next(nullptr), render_queue_prev(nullptr) {
104 for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
105 for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++) {
106 int shnum;
107 int frnum;
108 if (v2_chunks) {
109 shnum = data[0] + 256 * data[1];
110 frnum = data[2];
111 data += 3;
112 } else {
113 shnum = data[0] + 256 * (data[1] & 3);
114 frnum = (data[1] >> 2) & 0x1f;
115 data += 2;
116 }
117 ShapeID id(shnum, frnum);
118 shapes[16 * tiley + tilex] = id;
119 }
120 }
121
122 /*
123 * Copy another. The 'modified' flag is set to true.
124 */
125
Chunk_terrain(const Chunk_terrain & c2)126 Chunk_terrain::Chunk_terrain(
127 const Chunk_terrain &c2
128 ) : undo_shapes(nullptr),
129 num_clients(0), modified(true), rendered_flats(nullptr),
130 render_queue_next(nullptr), render_queue_prev(nullptr) {
131 for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
132 for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++)
133 shapes[16 * tiley + tilex] = c2.shapes[16 * tiley + tilex];
134 }
135
136 /*
137 * Clean up.
138 */
139
~Chunk_terrain()140 Chunk_terrain::~Chunk_terrain(
141 ) {
142 delete [] undo_shapes;
143 delete rendered_flats;
144 remove_from_queue();
145 }
146
147
148 /*
149 * Set tile's shape.
150 * NOTE: Set's 'modified' flag.
151 */
152
set_flat(int tilex,int tiley,const ShapeID & id)153 void Chunk_terrain::set_flat(
154 int tilex, int tiley,
155 const ShapeID& id
156 ) {
157 if (!undo_shapes) { // Create backup.
158 undo_shapes = new ShapeID[256];
159 std::memcpy(reinterpret_cast<char *>(undo_shapes),
160 reinterpret_cast<char *>(&shapes[0]),
161 sizeof(shapes));
162 }
163 shapes[16 * tiley + tilex] = id;
164 modified = true;
165 }
166
167 /*
168 * Commit changes.
169 *
170 * Output: True if this was edited, else false.
171 */
172
commit_edits()173 bool Chunk_terrain::commit_edits(
174 ) {
175 if (!undo_shapes)
176 return false;
177 delete [] undo_shapes;
178 undo_shapes = nullptr;
179 render_flats(); // Update with new data.
180 return true;
181 }
182
183 /*
184 * Undo changes. Note: We don't clear 'modified', since this could
185 * still have been moved to a different position.
186 */
187
abort_edits()188 void Chunk_terrain::abort_edits(
189 ) {
190 if (undo_shapes) {
191 std::memcpy(reinterpret_cast<char *>(&shapes[0]),
192 reinterpret_cast<char *>(undo_shapes),
193 sizeof(shapes));
194 delete [] undo_shapes;
195 undo_shapes = nullptr;
196 }
197 }
198
199 /*
200 * Figure max. queue size for given game window.
201 */
Figure_queue_size()202 static int Figure_queue_size(
203 ) {
204 //Game_window *gwin = Game_window::get_instance();
205 //int w = gwin->get_width(), h = gwin->get_height();
206 // Figure # chunks, rounding up.
207 //int cw = (w + c_chunksize - 1)/c_chunksize,
208 // ch = (h + c_chunksize - 1)/c_chunksize;
209 // Add extra in each dir.
210 return 100;//(cw + 3)*(ch + 3);
211 }
212
213 /*
214 * Create rendered_flats buffer.
215 */
216
render_flats()217 Image_buffer8 *Chunk_terrain::render_flats(
218 ) {
219 if (!rendered_flats) {
220 if (queue_size > Figure_queue_size()) {
221 // Grown too big. Remove last.
222 Chunk_terrain *last = render_queue->render_queue_prev;
223 last->free_rendered_flats();
224 render_queue->render_queue_prev =
225 last->render_queue_prev;
226 last->render_queue_prev->render_queue_next =
227 render_queue;
228 last->render_queue_next = last->render_queue_prev = nullptr;
229 queue_size--;
230 }
231 rendered_flats = new Image_buffer8(c_chunksize, c_chunksize);
232 }
233 // Go through array of tiles.
234 for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
235 for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++)
236 paint_tile(tilex, tiley);
237 return rendered_flats;
238 }
239
240 /*
241 * Free pre-rendered landscape.
242 */
243
free_rendered_flats()244 void Chunk_terrain::free_rendered_flats() {
245 delete rendered_flats;
246 rendered_flats = nullptr;
247 }
248
249 /*
250 * This method is only used in 'terrain-editor' mode, NOT in normal
251 * gameplay.
252 */
253
render_all(int cx,int cy)254 void Chunk_terrain::render_all(
255 int cx, int cy // Chunk rendering too.
256 ) {
257 Image_window8 *iwin = gwin->get_win();
258 int ctx = cx * c_tiles_per_chunk;
259 int cty = cy * c_tiles_per_chunk;
260 int scrolltx = gwin->get_scrolltx();
261 int scrollty = gwin->get_scrollty();
262 // Go through array of tiles.
263 for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
264 for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++) {
265 Shape_frame *shape = get_shape(tilex, tiley);
266 if (!shape)
267 continue;
268 if (!shape->is_rle())
269 iwin->copy8(shape->get_data(), c_tilesize,
270 c_tilesize,
271 (ctx + tilex - scrolltx)*c_tilesize,
272 (cty + tiley - scrollty)*c_tilesize);
273 else { // RLE.
274 int x;
275 int y;
276 Tile_coord tile(ctx + tilex, cty + tiley, 0);
277 gwin->get_shape_location(tile, x, y);
278 sman->paint_shape(x, y, shape);
279 }
280 }
281 }
282
283 /*
284 * Write out to a chunk.
285 *
286 * Output: Length of data stored.
287 */
288
write_flats(unsigned char * chunk_data,bool v2_chunks)289 int Chunk_terrain::write_flats(
290 unsigned char *chunk_data,
291 bool v2_chunks // 3 bytes/entry.
292 ) {
293 unsigned char *start = chunk_data;
294 for (int ty = 0; ty < c_tiles_per_chunk; ty++)
295 for (int tx = 0; tx < c_tiles_per_chunk; tx++) {
296 ShapeID id = get_flat(tx, ty);
297 int shapenum = id.get_shapenum();
298 int framenum = id.get_framenum();
299 if (v2_chunks) {
300 *chunk_data++ = shapenum & 0xff;
301 *chunk_data++ = (shapenum >> 8) & 0xff;
302 *chunk_data++ = framenum;
303 } else {
304 *chunk_data++ = shapenum & 0xff;
305 *chunk_data++ = ((shapenum >> 8) & 3) | (framenum << 2);
306 }
307 }
308 return chunk_data - start;
309 }
310
311