1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/conf/configuration.h"
25 #include "ultima/nuvie/gui/widgets/console.h"
26 #include "ultima/nuvie/files/nuvie_io_file.h"
27 #include "ultima/nuvie/files/nuvie_bmp_file.h"
28 #include "ultima/nuvie/files/u6_lib_n.h"
29 #include "ultima/nuvie/files/u6_lzw.h"
30 #include "ultima/nuvie/core/game.h"
31 #include "ultima/nuvie/screen/game_palette.h"
32 #include "ultima/nuvie/screen/dither.h"
33 #include "ultima/nuvie/misc/u6_misc.h"
34 #include "ultima/nuvie/core/look.h"
35 #include "ultima/nuvie/core/game_clock.h"
36 #include "ultima/nuvie/core/tile_manager.h"
37 #include "ultima/nuvie/gui/gui.h"
38 
39 namespace Ultima {
40 namespace Nuvie {
41 
42 #define NUM_ORIGINAL_TILES 2048
43 
44 static char article_tbl[][5] = {"", "a ", "an ", "the "};
45 
46 static const uint16 U6_ANIM_SRC_TILE[32] = {0x16, 0x16, 0x1a, 0x1a, 0x1e, 0x1e, 0x12, 0x12,
47 											0x1a, 0x1e, 0x16, 0x12, 0x16, 0x1a, 0x1e, 0x12,
48 											0x1a, 0x1e, 0x1e, 0x12, 0x12, 0x16, 0x16, 0x1a,
49 											0x12, 0x16, 0x1e, 0x1a, 0x1a, 0x1e, 0x12, 0x16
50 										   };
51 
52 //static const uint16 U6_WALL_TYPES[1][2] = {{156,176}};
53 
54 static const Tile gump_cursor = {
55 	0,
56 	false,
57 	false,
58 	false,
59 	false,
60 	false,
61 	true,
62 	false,
63 	false,
64 	0,
65 	//uint8 qty;
66 	//uint8 flags;
67 
68 	0,
69 	0,
70 	0,
71 
72 	{
73 		15, 15, 15, 15, 255, 255, 255, 255, 255, 255, 255, 255, 15, 15, 15, 15,
74 		15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
75 		15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
76 		15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
77 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
78 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
79 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
80 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
81 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
82 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
83 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
84 		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
85 		15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
86 		15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
87 		15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15,
88 		15, 15, 15, 15, 255, 255, 255, 255, 255, 255, 255, 255, 15, 15, 15, 15
89 	}
90 };
91 
TileManager(Configuration * cfg)92 TileManager::TileManager(Configuration *cfg)
93 	: desc_buf(NULL) {
94 	config = cfg;
95 	look = NULL;
96 	game_counter = rgame_counter = 0;
97 	memset(tileindex, 0, sizeof(tileindex));
98 	memset(tile, 0, sizeof(tile));
99 	memset(&animdata, 0, sizeof animdata);
100 
101 	extendedTiles = NULL;
102 	numTiles = NUM_ORIGINAL_TILES;
103 
104 	config->value("config/GameType", game_type);
105 }
106 
~TileManager()107 TileManager::~TileManager() {
108 // remove tiles
109 	free(desc_buf);
110 	delete look;
111 	if (extendedTiles) {
112 		free(extendedTiles);
113 	}
114 }
115 
loadTiles()116 bool TileManager::loadTiles() {
117 	Std::string maptiles_path, masktype_path, path;
118 	NuvieIOFileRead objtiles_vga;
119 	NuvieIOFileRead tileindx_vga;
120 	NuvieIOFileRead file;
121 	U6Lib_n lib_file;
122 	U6Lzw *lzw;
123 	uint32 tile_offset;
124 
125 	unsigned char *tile_data = NULL;
126 	uint32 maptiles_size = 0;
127 	uint32 objtiles_size;
128 
129 	unsigned char *masktype = NULL;
130 	uint32 masktype_size;
131 	uint16 i;
132 
133 	Dither *dither;
134 
135 	dither = Game::get_game()->get_dither();
136 
137 
138 	config_get_path(config, "maptiles.vga", maptiles_path);
139 	config_get_path(config, "masktype.vga", masktype_path);
140 
141 	lzw = new U6Lzw();
142 
143 	switch (game_type) {
144 	case NUVIE_GAME_U6 :
145 		tile_data = lzw->decompress_file(maptiles_path, maptiles_size);
146 		if (tile_data == NULL) {
147 			ConsoleAddError("Decompressing " + maptiles_path);
148 			return false;
149 		}
150 
151 		masktype = lzw->decompress_file(masktype_path, masktype_size);
152 		if (masktype == NULL) {
153 			ConsoleAddError("Decompressing " + masktype_path);
154 			return false;
155 		}
156 		break;
157 	case NUVIE_GAME_MD :
158 	case NUVIE_GAME_SE :
159 		if (lib_file.open(maptiles_path, 4, game_type) == false) {
160 			ConsoleAddError("Opening " + maptiles_path);
161 			return false;
162 		}
163 		maptiles_size = lib_file.get_item_size(0);
164 
165 		tile_data = lib_file.get_item(0);
166 		lib_file.close();
167 
168 		if (lib_file.open(masktype_path, 4, game_type) == false) {
169 			ConsoleAddError("Opening " + masktype_path);
170 			return false;
171 		}
172 		//masktype_size = lib_file.get_item_size(0);
173 
174 		masktype = lib_file.get_item(0);
175 		lib_file.close();
176 		break;
177 	}
178 
179 	if (tile_data == NULL) {
180 		ConsoleAddError("Loading maptiles.vga");
181 		return false;
182 	}
183 
184 	if (masktype == NULL) {
185 		ConsoleAddError("Loading masktype.vga");
186 		return false;
187 	}
188 
189 	config_get_path(config, "objtiles.vga", path);
190 	if (objtiles_vga.open(path) == false) {
191 		ConsoleAddError("Opening " + path);
192 		return false;
193 	}
194 
195 	objtiles_size = objtiles_vga.get_size();
196 
197 	tile_data = (unsigned char *)nuvie_realloc(tile_data, maptiles_size + objtiles_size);
198 
199 	objtiles_vga.readToBuf(&tile_data[maptiles_size], objtiles_size);
200 
201 	config_get_path(config, "tileindx.vga", path);
202 
203 	if (tileindx_vga.open(path) == false) {
204 		ConsoleAddError("Opening " + path);
205 		return false;
206 	}
207 
208 	for (i = 0; i < 2048; i++) {
209 		tile_offset = tileindx_vga.read2() * 16;
210 		tile[i].tile_num = i;
211 
212 		tile[i].transparent = false;
213 
214 		switch (masktype[i]) {
215 		case U6TILE_TRANS :
216 			tile[i].transparent = true;
217 			memcpy(tile[i].data, &tile_data[tile_offset], 256);
218 			break;
219 
220 		case U6TILE_PLAIN :
221 			memcpy(tile[i].data, &tile_data[tile_offset], 256);
222 			break;
223 
224 		case U6TILE_PBLCK :
225 			tile[i].transparent = true;
226 			decodePixelBlockTile(&tile_data[tile_offset], i);
227 			break;
228 		}
229 
230 		dither->dither_bitmap(tile[i].data, 16, 16, tile[i].transparent);
231 
232 		tileindex[i] = i; //set all tile indexs to default value. this is changed in update() for animated tiles
233 	}
234 
235 	loadAnimData();
236 	loadTileFlag();
237 
238 	free(masktype);
239 	free(tile_data);
240 
241 	look = new Look(config);
242 	if (look->init() == false) {
243 		ConsoleAddError("Initialising Look Class");
244 		return false;
245 	}
246 
247 	desc_buf = (char *)malloc(look->get_max_len() + 6); // add space for "%03d \n\0" or "the \n\0"
248 	if (desc_buf == NULL) {
249 		ConsoleAddError("Allocating desc_buf");
250 		return false;
251 	}
252 
253 	loadAnimMask();
254 
255 #ifdef TILEMANAGER_DEBUG
256 
257 	look->print();
258 
259 	DEBUG(0, LEVEL_DEBUGGING, "Dumping tile flags:");
260 	for (i = 0; i < 2048; i++) {
261 		bool plural;
262 		DEBUG(1, LEVEL_DEBUGGING, "%04d : ", i);
263 		print_b(LEVEL_DEBUGGING, tile[i].flags1);
264 		DEBUG(1, LEVEL_DEBUGGING, " ");
265 		print_b(LEVEL_DEBUGGING, tile[i].flags2);
266 		DEBUG(1, LEVEL_DEBUGGING, " ");
267 		print_b(LEVEL_DEBUGGING, tile[i].flags3);
268 		DEBUG(1, LEVEL_DEBUGGING, " %s\n", look->get_description(i, &plural));
269 	}
270 #endif
271 
272 	delete lzw;
273 
274 	return true;
275 }
276 
get_tile(uint16 tile_num)277 Tile *TileManager::get_tile(uint16 tile_num) {
278 	if (tile_num < NUM_ORIGINAL_TILES) {
279 		return &tile[tileindex[tile_num]];
280 	}
281 
282 	return get_extended_tile(tile_num);
283 }
284 
get_anim_base_tile(uint16 tile_num)285 Tile *TileManager::get_anim_base_tile(uint16 tile_num) {
286 	return &tile[tileindex[U6_ANIM_SRC_TILE[tile_num - 16] / 2]];
287 }
288 
get_original_tile(uint16 tile_num)289 Tile *TileManager::get_original_tile(uint16 tile_num) {
290 	if (tile_num < NUM_ORIGINAL_TILES) {
291 		return &tile[tile_num];
292 	}
293 
294 	return get_extended_tile(tile_num);
295 }
296 
297 
get_extended_tile(uint16 tile_num)298 Tile *TileManager::get_extended_tile(uint16 tile_num) {
299 	if (tile_num <= numTiles) {
300 		return &extendedTiles[tile_num - 2048];
301 	}
302 	return &tile[0];
303 }
304 
305 // set entry in tileindex[] to tile num
set_tile_index(uint16 tile_index,uint16 tile_num)306 void TileManager::set_tile_index(uint16 tile_index, uint16 tile_num) {
307 	tileindex[tile_index] = tile_num;
308 }
309 
310 
set_anim_loop(uint16 tile_num,sint8 loopc,uint8 loop)311 void TileManager::set_anim_loop(uint16 tile_num, sint8 loopc, uint8 loop) {
312 	for (uint32 i = 0; i < 32; i++)
313 		if (animdata.tile_to_animate[i] == tile_num) {
314 			animdata.loop_count[i] = loopc;
315 			animdata.loop[i] = loop;
316 		}
317 }
318 
319 
lookAtTile(uint16 tile_num,uint16 qty,bool show_prefix)320 const char *TileManager::lookAtTile(uint16 tile_num, uint16 qty, bool show_prefix) {
321 	const char *desc;
322 	bool plural;
323 	Tile *tileP;
324 
325 	tileP = get_original_tile(tile_num);
326 
327 	if (qty > 1)
328 		plural = true;
329 	else
330 		plural = false;
331 
332 	desc = look->get_description(tileP->tile_num, &plural);
333 	if (show_prefix == false)
334 		return desc;
335 
336 	if (qty > 0 &&
337 	        (plural || Game::get_game()->get_game_type() == NUVIE_GAME_SE))
338 		sprintf(desc_buf, "%u %s", qty, desc);
339 	else
340 		sprintf(desc_buf, "%s%s", article_tbl[tileP->article_n], desc);
341 
342 	DEBUG(0, LEVEL_DEBUGGING, "%s (%x): flags1:", desc_buf, tile_num);
343 	print_b(LEVEL_INFORMATIONAL, tileP->flags1);
344 	DEBUG(1, LEVEL_DEBUGGING, " f2:");
345 	print_b(LEVEL_INFORMATIONAL, tileP->flags2);
346 	DEBUG(1, LEVEL_DEBUGGING, " f3:");
347 	print_b(LEVEL_INFORMATIONAL, tileP->flags3);
348 	DEBUG(1, LEVEL_DEBUGGING, "\n");
349 
350 	return desc_buf;
351 }
352 
tile_is_stackable(uint16 tile_num)353 bool TileManager::tile_is_stackable(uint16 tile_num) {
354 	return look->has_plural(tile_num); // luteijn: FIXME, doesn't take into account Zu Ylem, Silver Snake Venom, and possibly other stackables that don't have a plural defined.
355 }
356 
update()357 void TileManager::update() {
358 	uint16 i;
359 	uint16 current_anim_frame = 0;
360 	uint16 prev_tileindex;
361 	uint8 current_hour = Game::get_game()->get_clock()->get_hour();
362 	static sint8 last_hour = -1;
363 
364 // cycle animated tiles
365 
366 	for (i = 0; i < animdata.number_of_tiles_to_animate; i++) {
367 		if (animdata.loop_count[i] != 0) {
368 			if (animdata.loop[i] == 0) // get next frame
369 				current_anim_frame = (game_counter & animdata.and_masks[i]) >> animdata.shift_values[i];
370 			else if (animdata.loop[i] == 1) // get previous frame
371 				current_anim_frame = (rgame_counter & animdata.and_masks[i]) >> animdata.shift_values[i];
372 			prev_tileindex = tileindex[animdata.tile_to_animate[i]];
373 			tileindex[animdata.tile_to_animate[i]] = tileindex[animdata.first_anim_frame[i] + current_anim_frame];
374 			// loop complete if back to first frame (and not infinite loop)
375 			if (animdata.loop_count[i] > 0
376 			        && tileindex[animdata.tile_to_animate[i]] != prev_tileindex
377 			        && tileindex[animdata.tile_to_animate[i]] == tileindex[animdata.first_anim_frame[i]])
378 				--animdata.loop_count[i];
379 		} else // not animating
380 			tileindex[animdata.tile_to_animate[i]] = tileindex[animdata.first_anim_frame[i]];
381 	}
382 
383 	if (Game::get_game()->anims_paused() == false) { // update counter
384 		if (game_counter == 65535)
385 			game_counter = 0;
386 		else
387 			game_counter++;
388 		if (rgame_counter == 0)
389 			rgame_counter = 65535;
390 		else
391 			rgame_counter--;
392 	}
393 // cycle time-based animations
394 	if (current_hour != last_hour)
395 		update_timed_tiles(current_hour);
396 	last_hour = current_hour;
397 }
398 
399 
loadTileFlag()400 bool TileManager::loadTileFlag() {
401 	Std::string filename;
402 	NuvieIOFileRead file;
403 	uint16 i;
404 
405 	config_get_path(config, "tileflag", filename);
406 
407 	if (file.open(filename) == false)
408 		return false;
409 
410 	for (i = 0; i < 2048; i++) {
411 		tile[i].flags1 = file.read1();
412 		if (tile[i].flags1 & 0x2)
413 			tile[i].passable = false;
414 		else
415 			tile[i].passable = true;
416 
417 		if (tile[i].flags1 & 0x1)
418 			tile[i].water = true;
419 		else
420 			tile[i].water = false;
421 
422 		if (tile[i].flags1 & 0x8)
423 			tile[i].damages = true;
424 		else
425 			tile[i].damages = false;
426 	}
427 
428 	for (i = 0; i < 2048; i++) {
429 		tile[i].flags2 = file.read1();
430 		if (tile[i].flags2 & 0x10)
431 			tile[i].toptile = true;
432 		else
433 			tile[i].toptile = false;
434 
435 		if ((tile[i].flags2 & 0x4) || (tile[i].flags2 & 0x8))
436 			tile[i].boundary = true;
437 		else
438 			tile[i].boundary = false;
439 
440 
441 		if (tile[i].flags2 & 0x40)
442 			tile[i].dbl_height = true;
443 		else
444 			tile[i].dbl_height = false;
445 
446 		if (tile[i].flags2 & 0x80)
447 			tile[i].dbl_width = true;
448 		else
449 			tile[i].dbl_width = false;
450 	}
451 
452 	file.seek(0x1400);
453 
454 	for (i = 0; i < 2048; i++) { // '', 'a', 'an', 'the'
455 		tile[i].flags3 = file.read1();
456 		tile[i].article_n = (tile[i].flags3 & 0xC0) >> 6;
457 	}
458 
459 	return true;
460 }
461 
loadAnimData()462 bool TileManager::loadAnimData() {
463 	Std::string filename;
464 	NuvieIOFileRead file;
465 	uint8 i;
466 	int gameType;
467 	config->value("config/GameType", gameType);
468 	config_get_path(config, "animdata", filename);
469 
470 	if (file.open(filename) == false)
471 		return false;
472 
473 	if (file.get_size() != 194)
474 		return false;
475 
476 	animdata.number_of_tiles_to_animate = file.read2();
477 
478 	for (i = 0; i < 32; i++) {
479 		animdata.tile_to_animate[i] = file.read2();
480 	}
481 
482 	for (i = 0; i < 32; i++) {
483 		animdata.first_anim_frame[i] = file.read2();
484 	}
485 
486 	for (i = 0; i < 32; i++) {
487 		animdata.and_masks[i] = file.read1();
488 	}
489 
490 	for (i = 0; i < 32; i++) {
491 		animdata.shift_values[i] = file.read1();
492 	}
493 
494 	for (i = 0; i < 32; i++) { // FIXME: any data on which tiles don't start as animated?
495 		animdata.loop[i] = 0; // loop forwards
496 		if ((gameType == NUVIE_GAME_U6 &&
497 		        (animdata.tile_to_animate[i] == 862 // Crank
498 		         || animdata.tile_to_animate[i] == 1009 // Crank
499 		         || animdata.tile_to_animate[i] == 1020)) // Chain
500 		        || (gameType == NUVIE_GAME_MD
501 		            && ((animdata.tile_to_animate[i] >= 1 && animdata.tile_to_animate[i] <= 4) // cistern
502 		                || (animdata.tile_to_animate[i] >= 16 && animdata.tile_to_animate[i] <= 23) // canal
503 		                || (animdata.tile_to_animate[i] >= 616 && animdata.tile_to_animate[i] <= 627) // watch --pu62 lists as 416-427
504 		                || animdata.tile_to_animate[i] == 1992
505 		                || animdata.tile_to_animate[i] == 1993
506 		                || animdata.tile_to_animate[i] == 1980
507 		                || animdata.tile_to_animate[i] == 1981)))
508 
509 			animdata.loop_count[i] = 0; // don't start animated
510 		else
511 			animdata.loop_count[i] = -1; // infinite animation
512 	}
513 
514 	return true;
515 }
516 
decodePixelBlockTile(unsigned char * tile_data,uint16 tile_num)517 void TileManager::decodePixelBlockTile(unsigned char *tile_data, uint16 tile_num) {
518 	uint8 len;
519 	uint8 i;
520 	uint16 disp;
521 	uint8 x;
522 	unsigned char *ptr;
523 	unsigned char *data_ptr;
524 
525 // num_blocks = tile_data[0];
526 
527 	ptr = &tile_data[1];
528 
529 	data_ptr = tile[tile_num].data;
530 
531 	memset(data_ptr, 0xff, 256); //set all pixels to transparent.
532 
533 	for (i = 0; ; i++) {
534 		disp = (ptr[0] + (ptr[1] << 8));
535 
536 		x = disp % 160 + (disp >= 1760 ? 160 : 0);
537 
538 		len = ptr[2];
539 
540 		if (len == 0)
541 			break;
542 
543 		data_ptr += x;
544 
545 		memcpy(data_ptr, &ptr[3], len);
546 
547 		data_ptr += len;
548 
549 		ptr += (3 + len);
550 	}
551 
552 	return;
553 }
554 
555 
loadAnimMask()556 bool TileManager::loadAnimMask() {
557 	Std::string filename;
558 	U6Lzw lzw;
559 	uint16 i;
560 	unsigned char *animmask;
561 	unsigned char *mask_ptr;
562 	uint32 animmask_size;
563 
564 	unsigned char *tile_data;
565 	uint16 bytes2clear;
566 	uint16 displacement;
567 	int gameType;
568 
569 	config->value("config/GameType", gameType);
570 	if (gameType != NUVIE_GAME_U6)               //only U6 has animmask.vga
571 		return true;
572 
573 	config_get_path(config, "animmask.vga", filename);
574 
575 	animmask = lzw.decompress_file(filename, animmask_size);
576 
577 	if (animmask == NULL)
578 		return false;
579 
580 	for (i = 0; i < 32; i++) { // Make the 32 tiles from index 16 onwards transparent with data from animmask.vga
581 		tile_data = tile[16 + i].data;
582 		tile[16 + i].transparent = true;
583 
584 		mask_ptr = animmask + i * 64;
585 		bytes2clear = mask_ptr[0];
586 
587 		if (bytes2clear != 0)
588 			memset(tile_data, 0xff, bytes2clear);
589 
590 		tile_data += bytes2clear;
591 		mask_ptr++;
592 
593 		displacement = mask_ptr[0];
594 		bytes2clear = mask_ptr[1];
595 
596 		mask_ptr += 2;
597 
598 		for (; displacement != 0 && bytes2clear != 0; mask_ptr += 2) {
599 			tile_data += displacement;
600 
601 			memset(tile_data, 0xff, bytes2clear);
602 			tile_data += bytes2clear;
603 
604 			displacement = mask_ptr[0];
605 			bytes2clear = mask_ptr[1];
606 		}
607 	}
608 
609 	free(animmask);
610 
611 	return true;
612 }
613 
614 
615 /* Update tiles for timed-based animations.
616  */
update_timed_tiles(uint8 hour)617 void TileManager::update_timed_tiles(uint8 hour) {
618 	uint16 new_tile;
619 	if (Game::get_game()->get_game_type() == NUVIE_GAME_U6) {
620 		// sundials
621 		if (hour >= 5 && hour <= 6)
622 			new_tile = 328;
623 		else if (hour >= 7 && hour <= 8)
624 			new_tile = 329;
625 		else if (hour >= 9 && hour <= 10)
626 			new_tile = 330;
627 		else if (hour >= 11 && hour <= 12)
628 			new_tile = 331;
629 		else if (hour >= 13 && hour <= 14)
630 			new_tile = 332;
631 		else if (hour >= 15 && hour <= 16)
632 			new_tile = 333;
633 		else if (hour >= 17 && hour <= 18)
634 			new_tile = 334;
635 		else if (hour >= 19 && hour <= 20)
636 			new_tile = 335;
637 		else // 9pm to 5am
638 			new_tile = 861;
639 		set_tile_index(861, new_tile);
640 	}
641 
642 }
643 
set_anim_first_frame(uint16 anim_number,uint16 new_start_tile_num)644 void TileManager::set_anim_first_frame(uint16 anim_number, uint16 new_start_tile_num) {
645 	if (anim_number < animdata.number_of_tiles_to_animate) {
646 		animdata.first_anim_frame[anim_number] = new_start_tile_num;
647 	}
648 }
649 
650 /* Returns tile rotated about the center by `rotate' degrees. (8-bit; clipped to
651  * standard 16x16 size) It must be deleted after use.
652  * **Fixed-point rotate function taken from the SDL Graphics Extension library
653  * (SGE) (c)1999-2003 Anders Lindstr�m, licensed under LGPL v2+.**
654  */
get_rotated_tile(Tile * tileP,float rotate,uint8 src_y_offset)655 Tile *TileManager::get_rotated_tile(Tile *tileP, float rotate, uint8 src_y_offset) {
656 	Tile *new_tile = new Tile(*tileP); // retain properties of original tileP
657 	get_rotated_tile(tileP, new_tile, rotate, src_y_offset);
658 
659 	return new_tile;
660 }
661 
get_rotated_tile(Tile * tileP,Tile * dest_tile,float rotate,uint8 src_y_offset)662 void TileManager::get_rotated_tile(Tile *tileP, Tile *dest_tile, float rotate, uint8 src_y_offset) {
663 	unsigned char tile_data[256];
664 
665 	memset(&dest_tile->data, 255, 256); // fill output with transparent color
666 
667 	int32 dy, sx, sy;
668 	int16 rx, ry;
669 	uint16 px = 8, py = 8; // rotate around these coordinates
670 	uint16 xmin = 0, xmax = 15, ymin = 0, ymax = 15; // size
671 	uint16 sxmin = xmin, sxmax = xmax, symin = ymin, symax = ymax;
672 	uint16 qx = 8, qy = 8; // ?? don't remember
673 
674 	float theta = float(rotate * M_PI / 180.0); /* Convert to radians.  */
675 
676 	int32 const stx = int32((sin(theta)) * 8192.0);
677 	int32 const ctx = int32((cos(theta)) * 8192.0);
678 	int32 const sty = int32((sin(theta)) * 8192.0);
679 	int32 const cty = int32((cos(theta)) * 8192.0);
680 	int32 const mx = int32(px * 8192.0);
681 	int32 const my = int32(py * 8192.0);
682 
683 	int32 const dx = xmin - qx;
684 	int32 const ctdx = ctx * dx;
685 	int32 const stdx = sty * dx;
686 
687 	int32 const src_pitch = 16;
688 	int32 const dst_pitch = 16;
689 	uint8 const *src_row = (uint8 *)&tileP->data;
690 	uint8 *dst_pixels = (uint8 *)&dest_tile->data;
691 	uint8 *dst_row;
692 
693 	if (src_y_offset > 0 && src_y_offset < 16) { //shift source down before rotating. This is used by bolt and arrow tiles.
694 		memset(&tile_data, 255, 256);
695 		memcpy(&tile_data[src_y_offset * 16], &tileP->data, 256 - (src_y_offset * 16));
696 		src_row = (uint8 *)&tile_data;
697 	}
698 
699 	for (uint32 y = ymin; y < ymax; y++) {
700 		dy = y - qy;
701 
702 		sx = int32(ctdx  + stx * dy + mx); /* Compute source anchor points */
703 		sy = int32(cty * dy - stdx  + my);
704 
705 		/* Calculate pointer to dst surface */
706 		dst_row = (uint8 *)dst_pixels + y * dst_pitch;
707 
708 		for (uint32 x = xmin; x < xmax; x++) {
709 			rx = int16(sx >> 13); /* Convert from fixed-point */
710 			ry = int16(sy >> 13);
711 
712 			/* Make sure the source pixel is actually in the source image. */
713 			if ((rx >= sxmin) && (rx <= sxmax) && (ry >= symin) && (ry <= symax))
714 				*(dst_row + x) = *(src_row + ry * src_pitch + rx);
715 
716 			sx += ctx;  /* Incremental transformations */
717 			sy -= sty;
718 		}
719 	}
720 
721 
722 	//memcpy(&dest_tile->data, &tile_data, 256); // replace data
723 
724 	return;
725 }
726 
727 
728 #if 0 /* old */
729 Tile *TileManager::get_rotated_tile(Tile *tile, float rotate) {
730 	float angle = (rotate != 0) ? (rotate * M_PI) / 180.0 : 0; // radians
731 	const float mul_x1 = cos(angle);  // | x1  y1 |
732 	const float mul_y1 = sin(angle);  // | x2  y2 |
733 	const float mul_x2 = -mul_y1;
734 	const float mul_y2 = mul_x1;
735 	unsigned char tile_data[256];
736 	unsigned char *input = (unsigned char *)&tile->data, *output;
737 
738 	memset(&tile_data, 255, 256); // fill output with transparent color
739 
740 	for (sint8 y = -8; y < 8; y++) { // scan input pixels
741 		for (sint8 x = -8; x < 8; x++) {
742 			sint8 rx = (sint8)rint((x * mul_x1) + (y * mul_x2)); // calculate target pixel
743 			sint8 ry = (sint8)rint((x * mul_y1) + (y * mul_y2));
744 			if (rx >= -8 && rx <= 7 && ry >= -8 && ry <= 7) {
745 				output = (unsigned char *)&tile_data;
746 				output += (ry + 8) * 16;
747 				output[rx + 8] = input[x + 8]; // copy
748 				if (rx <= 6) output[rx + 8 + 1] = input[x + 8]; // copy again to adjacent pixel
749 			}
750 		}
751 		input += 16; // next line
752 	}
753 
754 	Tile *new_tile = new Tile(*tile); // retain properties of original tile
755 	memcpy(&new_tile->data, &tile_data, 256); // replace data
756 	return (new_tile);
757 }
758 #endif
759 
get_cursor_tile()760 Tile *TileManager::get_cursor_tile() {
761 	Tile *cursor_tile = NULL;
762 	switch (game_type) {
763 	case NUVIE_GAME_U6 :
764 		cursor_tile = get_tile(365);
765 		break;
766 
767 	case NUVIE_GAME_MD :
768 		cursor_tile = get_tile(265);
769 		break;
770 
771 	case NUVIE_GAME_SE :
772 		cursor_tile = get_tile(381);
773 		break;
774 	}
775 
776 	return cursor_tile;
777 }
778 
get_use_tile()779 Tile *TileManager::get_use_tile() {
780 	Tile *use_tile = NULL;
781 	switch (game_type) {
782 	case NUVIE_GAME_U6 :
783 		use_tile = get_tile(364);
784 		break;
785 
786 	case NUVIE_GAME_MD :
787 		use_tile = get_tile(264);
788 		break;
789 
790 	case NUVIE_GAME_SE :
791 		use_tile = get_tile(380);
792 		break;
793 	}
794 
795 	return use_tile;
796 }
797 
get_gump_cursor_tile()798 const Tile *TileManager::get_gump_cursor_tile() {
799 	return &gump_cursor;
800 }
801 
loadCustomTiles(const Std::string filename,bool overwrite_tiles,bool copy_tileflags,uint16 tile_num_start_offset)802 Tile *TileManager::loadCustomTiles(const Std::string filename, bool overwrite_tiles, bool copy_tileflags, uint16 tile_num_start_offset) {
803 	NuvieBmpFile bmp;
804 
805 	if (bmp.load(filename) == false) {
806 		return NULL;
807 	}
808 
809 	unsigned char *tile_data = bmp.getRawIndexedData();
810 
811 	uint16 w = bmp.getWidth();
812 	uint16 h = bmp.getHeight();
813 	uint16 pitch = w;
814 
815 	if (w % 16 != 0 || h % 16 != 0) {
816 		return NULL;
817 	}
818 
819 	w = w / 16;
820 	h = h / 16;
821 
822 	uint16 num_tiles = w * h;
823 
824 	Tile *newTilePtr = NULL;
825 	Tile *origTile = NULL;
826 	if (overwrite_tiles) {
827 		newTilePtr = get_original_tile(tile_num_start_offset);
828 	} else {
829 		newTilePtr = addNewTiles(num_tiles);
830 	}
831 
832 	if (copy_tileflags) {
833 		origTile = get_tile(tile_num_start_offset);
834 	}
835 
836 	Tile *t = newTilePtr;
837 
838 	Dither *dither = Game::get_game()->get_dither();
839 
840 	for (uint16 y = 0; y < h; y++) {
841 		for (uint16 x = 0; x < w; x++) {
842 			unsigned char *data = tile_data + (y * 16 * pitch) + (x * 16);
843 			for (uint16 i = 0; i < 16; i++) {
844 				memcpy(&t->data[i * 16], data, 16);
845 				data += pitch;
846 			}
847 
848 			if (origTile) {
849 				copyTileMetaData(t, origTile);
850 				origTile++;
851 			}
852 			dither->dither_bitmap(t->data, 16, 16, t->transparent);
853 			t++;
854 		}
855 	}
856 
857 	return newTilePtr;
858 }
859 
copyTileMetaData(Tile * dest,Tile * src)860 void TileManager::copyTileMetaData(Tile *dest, Tile *src) {
861 	dest->passable = src->passable;
862 	dest->water = src->water;
863 	dest->toptile = src->toptile;
864 	dest->dbl_width = src->dbl_width;
865 	dest->dbl_height = src->dbl_height;
866 	dest->transparent = src->transparent;
867 	dest->boundary = src->boundary;
868 	dest->damages = src->damages;
869 	dest->article_n = src->article_n;
870 
871 	dest->flags1 = src->flags1;
872 	dest->flags2 = src->flags2;
873 	dest->flags3 = src->flags3;
874 }
875 
addNewTiles(uint16 num_tiles)876 Tile *TileManager::addNewTiles(uint16 num_tiles) {
877 	Tile *tileDataPtr = (Tile *)realloc(extendedTiles, sizeof(Tile) * (numTiles - NUM_ORIGINAL_TILES + num_tiles));
878 	if (tileDataPtr != NULL) {
879 		extendedTiles = tileDataPtr;
880 	}
881 
882 	tileDataPtr += (numTiles - NUM_ORIGINAL_TILES);
883 
884 	Tile *t = tileDataPtr;
885 	for (uint16 i = 0; i < num_tiles; i++, t++) {
886 		t->tile_num = numTiles + i;
887 	}
888 
889 	numTiles += num_tiles;
890 
891 	return tileDataPtr;
892 }
893 
freeCustomTiles()894 void TileManager::freeCustomTiles() {
895 	if (extendedTiles) {
896 		free(extendedTiles);
897 		extendedTiles = NULL;
898 		numTiles = NUM_ORIGINAL_TILES;
899 	}
900 }
901 
exportTilesetToBmpFile(Std::string filename,bool fixupU6Shoreline)902 void TileManager::exportTilesetToBmpFile(Std::string filename, bool fixupU6Shoreline) {
903 	NuvieBmpFile bmp;
904 
905 	unsigned char pal[256 * 4];
906 
907 	Game::get_game()->get_palette()->loadPaletteIntoBuffer(pal);
908 
909 	//Magic background colour
910 	pal[255 * 4] = 0;
911 	pal[255 * 4 + 1] = 0xdf;
912 	pal[255 * 4 + 2] = 0xfc;
913 
914 	bmp.initNewBlankImage(32 * 16, 64 * 16, pal);
915 
916 	unsigned char *data = bmp.getRawIndexedData();
917 
918 	for (uint8 i = 0; i < 64; i++) {
919 		for (uint8 j = 0; j < 32; j++) {
920 			if (fixupU6Shoreline && game_type == NUVIE_GAME_U6 && (i * 32 + j) >= 16 && (i * 32 + j) < 48) { //lay down the base tile for shoreline tiles
921 				writeBmpTileData(&data[i * 16 * 512 + j * 16], get_anim_base_tile(i * 32 + j), false);
922 				writeBmpTileData(&data[i * 16 * 512 + j * 16], &tile[tileindex[i * 32 + j]], true);
923 			} else {
924 				writeBmpTileData(&data[i * 16 * 512 + j * 16], &tile[tileindex[i * 32 + j]], false);
925 			}
926 		}
927 	}
928 	bmp.save(filename);
929 }
930 
writeBmpTileData(unsigned char * data,Tile * t,bool transparent)931 void TileManager::writeBmpTileData(unsigned char *data, Tile *t, bool transparent) {
932 	for (uint8 y = 0; y < 16; y++) {
933 		for (uint8 x = 0; x < 16; x++) {
934 			if (!transparent || t->data[y * 16 + x] != 255) {
935 				data[x] = t->data[y * 16 + x];
936 			}
937 		}
938 		data += 512;
939 	}
940 }
941 
anim_play_repeated(uint8 anim_index)942 void TileManager::anim_play_repeated(uint8 anim_index) {
943 	if (anim_index < get_number_of_animations()) {
944 		animdata.loop_count[anim_index] = -1; // infinite animation
945 	}
946 }
947 
anim_stop_playing(uint8 anim_index)948 void TileManager::anim_stop_playing(uint8 anim_index) {
949 	if (anim_index < get_number_of_animations()) {
950 		animdata.loop_count[anim_index] = 0; // stop animation
951 	}
952 }
953 
954 } // End of namespace Nuvie
955 } // End of namespace Ultima
956