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