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