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/misc/u6_misc.h"
26 #include "ultima/nuvie/misc/u6_llist.h"
27 #include "ultima/nuvie/actors/actor.h"
28 #include "ultima/nuvie/actors/actor_manager.h"
29 #include "ultima/nuvie/views/view_manager.h"
30 #include "ultima/nuvie/gui/widgets/map_window.h"
31 #include "ultima/nuvie/core/events.h"
32 #include "ultima/nuvie/gui/widgets/msg_scroll.h"
33 #include "ultima/nuvie/gui/widgets/msg_scroll_new_ui.h"
34 #include "ultima/nuvie/core/effect.h" /* for initial fade-in */
35 #include "ultima/nuvie/core/tile_manager.h"
36 #include "ultima/nuvie/sound/sound_manager.h"
37 #include "ultima/nuvie/gui/gui.h"
38 #include "ultima/nuvie/core/game_clock.h"
39 #include "ultima/nuvie/screen/game_palette.h"
40 #include "ultima/nuvie/core/party.h"
41 #include "ultima/nuvie/core/weather.h"
42 #include "ultima/nuvie/script/script.h"
43 #include "ultima/nuvie/core/u6_objects.h"
44 #include "ultima/nuvie/gui/widgets/command_bar.h"
45 #include "ultima/nuvie/views/actor_view.h"
46 #include "ultima/nuvie/views/inventory_view.h"
47 #include "ultima/nuvie/gui/widgets/background.h"
48 #include "ultima/nuvie/keybinding/keys.h"
49
50 namespace Ultima {
51 namespace Nuvie {
52
53 #define TMP_MAP_BORDER 3
54
55 #define WRAP_VIEWP(p,p1,s) ((p1-p) < 0 ? (p1-p) + s : p1-p)
56
57 // This should make the mouse-cursor hovering identical to that in U6.
58 static const uint8 movement_array[9 * 9] = {
59 9, 9, 2, 2, 2, 2, 2, 3, 3,
60 9, 9, 9, 2, 2, 2, 3, 3, 3,
61 8, 9, 9, 2, 2, 2, 3, 3, 4,
62 8, 8, 8, 9, 2, 3, 4, 4, 4,
63 8, 8, 8, 8, 1, 4, 4, 4, 4,
64 8, 8, 8, 7, 6, 5, 4, 4, 4,
65 8, 7, 7, 6, 6, 6, 5, 5, 4,
66 7, 7, 7, 6, 6, 6, 5, 5, 5,
67 7, 7, 6, 6, 6, 6, 6, 5, 5
68 };
69
70 static const Tile grid_tile = {
71 0,
72 false,
73 false,
74 false,
75 false,
76 false,
77 true,
78 false,
79 false,
80 0,
81 //uint8 qty;
82 //uint8 flags;
83
84 0,
85 0,
86 0,
87
88 {
89 54, 255, 255, 255, 58, 255, 255, 255, 58, 255, 255, 255, 58, 255, 255, 255,
90 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
91 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
92 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
93 58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
94 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
95 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
96 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
97 58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
98 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
99 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
100 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
101 58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
102 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
103 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
104 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
105
106 }
107 };
108
MapWindow(Configuration * cfg,Map * m)109 MapWindow::MapWindow(Configuration *cfg, Map *m): GUI_Widget(NULL, 0, 0, 0, 0) {
110
111 config = cfg;
112 config->value("config/GameType", game_type);
113
114 uint16 x_off = Game::get_game()->get_game_x_offset();
115 uint16 y_off = Game::get_game()->get_game_y_offset();
116
117 GUI_Widget::Init(NULL, x_off, y_off, 0, 0);
118
119 map = m;
120
121 screen = NULL;
122 //surface = NULL;
123 anim_manager = NULL;
124
125 cur_x = 0;
126 mousecenter_x = 0;
127 cur_y = 0;
128 mousecenter_y = 0;
129 cur_x_add = cur_y_add = 0;
130 vel_x = vel_y = 0;
131 last_boundary_fill_x = last_boundary_fill_y = 0;
132
133 cursor_x = 0;
134 cursor_y = 0;
135 show_cursor = false;
136 show_use_cursor = false;
137 show_grid = false;
138 x_ray_view = X_RAY_OFF;
139 freeze_blacking_location = false;
140 enable_blacking = true;
141
142 new_thumbnail = false;
143 thumbnail = NULL;
144 overlay = NULL;
145 overlay_level = MAP_OVERLAY_DEFAULT;
146
147 cur_level = 0;
148 map_width = map->get_width(cur_level);
149
150 tmp_map_buf = NULL;
151
152 selected_obj = NULL;
153 look_obj = NULL;
154 look_actor = NULL;
155 walking = false;
156 looking = false;
157 config->value(config_get_game_key(config) + "/map_tile_lighting", using_map_tile_lighting, game_type == NUVIE_GAME_MD ? false : true);
158 config->value("config/input/enable_doubleclick", enable_doubleclick, true);
159 config->value("config/input/look_on_left_click", look_on_left_click, true);
160 set_use_left_clicks();
161 config->value("config/input/walk_with_left_button", walk_with_left_button, true);
162 set_walk_button_mask();
163 config->value("config/cheats/min_brightness", min_brightness, 0);
164 original_obj_loc = MapCoord(0, 0, 0);
165
166 roof_mode = Game::get_game()->is_roof_mode();
167 roof_tiles = NULL;
168
169 draw_brit_lens_anim = false;
170 draw_garg_lens_anim = false;
171
172 window_updated = true;
173 roof_display = ROOF_DISPLAY_NORMAL;
174
175 lighting_update_required = true;
176
177 set_interface();
178 }
179
~MapWindow()180 MapWindow::~MapWindow() {
181 set_overlay(NULL); // free
182 free(tmp_map_buf);
183 delete anim_manager;
184 if (roof_tiles) {
185 SDL_FreeSurface(roof_tiles);
186 }
187 }
188
init(TileManager * tm,ObjManager * om,ActorManager * am)189 bool MapWindow::init(TileManager *tm, ObjManager *om, ActorManager *am) {
190 // int game_type; Why is this local, and retrieved again here? --SB-X
191
192 game = Game::get_game();
193
194 tile_manager = tm;
195 obj_manager = om;
196 actor_manager = am;
197 uint16 map_w = 11, map_h = 11;
198 border_width = game->get_background()->get_border_width();
199 if (!game->is_orig_style()) {
200 uint16 game_width = game->get_game_width();
201 uint16 game_height = game->get_game_height();
202
203 if (game->is_original_plus_cutoff_map()) {
204 map_center_xoff = 0;
205 game_width -= border_width; // don't go over border
206 } else if (game->is_original_plus_full_map()) {
207 map_center_xoff = (border_width / 16) % 16;
208 } else { // new style
209 map_center_xoff = 0;
210 }
211 map_w = game_width / 16;
212 map_h = game_height / 16;
213 if (game_width % 16 != 0 || map_w % 2 == 0) { // not just the right size
214 map_w += 1;
215 if (map_w % 2 == 0) // need odd number of tiles to center properly
216 map_w += 1;
217 }
218 if (game_height % 16 != 0 || map_h % 2 == 0) { // not just the right size
219 map_h += 1;
220 if (map_h % 2 == 0) // need odd number of tiles to center properly
221 map_h += 1;
222 }
223 offset_x -= (map_w * 16 - game_width) / 2;
224 offset_y -= (map_h * 16 - game_height) / 2;
225 } else
226 map_center_xoff = 0;
227 anim_manager = new AnimManager(offset_x, offset_y);
228
229 cursor_tile = tile_manager->get_cursor_tile();
230 use_tile = tile_manager->get_use_tile();
231
232 area.left = offset_x;
233 area.top = offset_y;
234
235 set_windowSize(map_w, map_h);
236
237 // hide the window until game is fully loaded and does fade-in
238 get_overlay(); // this allocates `overlay`
239 overlay_level = MAP_OVERLAY_ONTOP;
240 assert(SDL_FillRect(overlay, NULL, game->get_palette()->get_bg_color()) == 0);
241
242 wizard_eye_info.eye_tile = tile_manager->get_tile(TILE_U6_WIZARD_EYE);
243 wizard_eye_info.moves_left = 0;
244 wizard_eye_info.caller = NULL;
245
246 if (roof_mode)
247 loadRoofTiles();
248
249 return true;
250 }
251
set_use_left_clicks()252 void MapWindow::set_use_left_clicks() {
253 if (enable_doubleclick || look_on_left_click)
254 set_accept_mouseclick(true, USE_BUTTON); // allow left clicks
255 else
256 set_accept_mouseclick(false, USE_BUTTON); // disallow left clicks
257 }
258
set_windowSize(uint16 width,uint16 height)259 bool MapWindow::set_windowSize(uint16 width, uint16 height) {
260 win_width = width;
261 win_height = height;
262 area.setWidth(win_width * 16);
263 area.setHeight(win_height * 16);
264
265 // We make the temp map +1 bigger on the top and left edges
266 // and +2 bigger on the bottom and right edges
267
268 // The +1 is for the boundary fill function
269
270 // The additional +1 on the right/bottom edges is needed
271 // to hide objects on boundarys when wall is in darkness
272
273 tmp_map_width = win_width + (TMP_MAP_BORDER * 2);// + 1;
274 tmp_map_height = win_height + (TMP_MAP_BORDER * 2);// + 1;
275
276 tmp_map_buf = (uint16 *)nuvie_realloc(tmp_map_buf, tmp_map_width * tmp_map_height * sizeof(uint16));
277 if (tmp_map_buf == NULL)
278 return false;
279
280 // if(surface != NULL)
281 // delete surface;
282 // surface = new Surface;
283
284 // if(surface->init(win_width*16,win_height*16) == false)
285 // return false;
286 if (game->is_orig_style()) {
287 clip_rect.left = area.left + 8;
288 clip_rect.setWidth((win_width - 1) * 16);
289 clip_rect.setHeight((win_height - 1) * 16);
290
291 if (game_type == NUVIE_GAME_U6)
292 clip_rect.top = area.top + 8;
293 else {
294 clip_rect.top = area.top + 16;
295 clip_rect.bottom -= 16;
296 }
297 } else {
298 clip_rect.left = game->get_game_x_offset();
299 clip_rect.top = game->get_game_y_offset();
300 if (game->is_original_plus_cutoff_map())
301 clip_rect.setWidth(game->get_game_width() - border_width - 1);
302 else
303 clip_rect.setWidth(game->get_game_width());
304 clip_rect.setHeight(game->get_game_height());
305 }
306 anim_manager->set_area(clip_rect);
307
308 reset_mousecenter();
309
310 updateBlacking();
311
312 return true;
313 }
314
set_walk_button_mask()315 void MapWindow::set_walk_button_mask() {
316 if (walk_with_left_button)
317 walk_button_mask = (BUTTON_MASK(USE_BUTTON) | BUTTON_MASK(WALK_BUTTON));
318 else
319 walk_button_mask = BUTTON_MASK(WALK_BUTTON);
320 }
321
set_show_cursor(bool state)322 void MapWindow::set_show_cursor(bool state) {
323 ActorView *actor_view = game->get_view_manager()->get_actor_view();
324 InventoryView *inventory_view = game->get_view_manager()->get_inventory_view();
325 if (actor_view)
326 actor_view->set_show_cursor(false);
327 if (inventory_view)
328 inventory_view->set_show_cursor(false);
329
330 show_cursor = state;
331 }
332
set_show_use_cursor(bool state)333 void MapWindow::set_show_use_cursor(bool state) {
334 ActorView *actor_view = game->get_view_manager()->get_actor_view();
335 InventoryView *inventory_view = game->get_view_manager()->get_inventory_view();
336 if (actor_view)
337 actor_view->set_show_cursor(false);
338 if (inventory_view)
339 inventory_view->set_show_cursor(false);
340 show_use_cursor = state;
341 }
342
set_show_grid(bool state)343 void MapWindow::set_show_grid(bool state) {
344 show_grid = state;
345 }
346
347 /**
348 * cheat_off is needed to turn the X-ray cheat off (set to X_RAY_OFF)
349 * Convoluted logic is so we don't slow down boundaryFill with an extra check
350 */
set_x_ray_view(X_RayType state,bool cheat_off)351 void MapWindow::set_x_ray_view(X_RayType state, bool cheat_off) {
352 if (x_ray_view == X_RAY_CHEAT_ON) {
353 if (state == X_RAY_ON) // X_RAY_CHEAT_ON takes precedence to preserve X-ray cheat
354 return;
355 else if (state == X_RAY_OFF) {
356 if (!cheat_off) { // not turning X-ray cheat off
357 if (game->are_cheats_enabled()) // don't turn off when cheats are enabled
358 return;
359 else // need to preserve X-ray cheat setting when cheats are off
360 state = X_RAY_CHEAT_OFF;
361 }
362 }
363 } else if (x_ray_view == X_RAY_CHEAT_OFF) {
364 if (state == X_RAY_OFF) // X_RAY_CHEAT_OFF takes precedence to preserve X-ray cheat
365 return;
366 else if (state == X_RAY_ON) // need to preserve X-ray cheat setting when cheats are off
367 state = X_RAY_CHEAT_ON;
368 }
369 x_ray_view = state;
370 updateBlacking();
371 }
372
set_freeze_blacking_location(bool state)373 void MapWindow::set_freeze_blacking_location(bool state) {
374 freeze_blacking_location = state;
375 }
376
set_enable_blacking(bool state)377 void MapWindow::set_enable_blacking(bool state) {
378 enable_blacking = state;
379 updateBlacking();
380 }
381
set_walking(bool state)382 void MapWindow::set_walking(bool state) {
383 if (state && game->get_view_manager()->gumps_are_active()) //we don't allow walking while gumps are active.
384 return;
385
386 walking = state;
387 }
388
moveLevel(uint8 new_level)389 void MapWindow::moveLevel(uint8 new_level) {
390 cur_level = new_level;
391
392 updateBlacking();
393 }
394
moveMap(sint16 new_x,sint16 new_y,sint8 new_level,uint8 new_x_add,uint8 new_y_add)395 void MapWindow::moveMap(sint16 new_x, sint16 new_y, sint8 new_level, uint8 new_x_add, uint8 new_y_add) {
396
397 map_width = map->get_width(new_level);
398
399 if (new_x < 0) {
400 new_x = map_width + new_x;
401 } else {
402 new_x %= map_width;
403 }
404
405 //printf("cur_x = %d\n",new_x);
406 cur_x = new_x;
407 cur_y = new_y;
408 cur_level = new_level;
409 cur_x_add = new_x_add;
410 cur_y_add = new_y_add;
411 updateBlacking();
412
413 }
414
moveMapRelative(sint16 rel_x,sint16 rel_y)415 void MapWindow::moveMapRelative(sint16 rel_x, sint16 rel_y) {
416 moveMap(cur_x + rel_x, cur_y + rel_y, cur_level);
417 }
418
419 /* Move map by relative pixel amount.
420 */
shiftMapRelative(sint16 rel_x,sint16 rel_y)421 void MapWindow::shiftMapRelative(sint16 rel_x, sint16 rel_y) {
422 uint8 tile_pitch = 16;
423 uint32 total_px = (cur_x * tile_pitch) + cur_x_add,
424 total_py = (cur_y * tile_pitch) + cur_y_add;
425 total_px += rel_x;
426 total_py += rel_y;
427 moveMap(total_px / tile_pitch, total_py / tile_pitch, cur_level,
428 total_px % tile_pitch, total_py % tile_pitch);
429 }
430
431 /* Center MapWindow on a location.
432 */
centerMap(uint16 x,uint16 y,uint8 z)433 void MapWindow::centerMap(uint16 x, uint16 y, uint8 z) {
434 moveMap(x - ((win_width - 1 - map_center_xoff) / 2) , y - ((win_height - 1) / 2), z);
435 }
436
centerMapOnActor(Actor * actor)437 void MapWindow::centerMapOnActor(Actor *actor) {
438 uint16 x;
439 uint16 y;
440 uint8 z;
441
442 actor->get_location(&x, &y, &z);
443
444 centerMap(x, y, z);
445
446 return;
447 }
448
centerCursor()449 void MapWindow::centerCursor() {
450
451 cursor_x = (win_width - 1 - map_center_xoff) / 2;
452 cursor_y = (win_height - 1) / 2;
453
454 return;
455 }
456
moveCursor(sint16 new_x,sint16 new_y)457 void MapWindow::moveCursor(sint16 new_x, sint16 new_y) {
458 if (new_x < 0 || new_x >= win_width)
459 return;
460
461 if (new_y < 0 || new_y >= win_height)
462 return;
463
464 cursor_x = new_x;
465 cursor_y = new_y;
466
467 return;
468 }
469
moveCursorRelative(sint16 rel_x,sint16 rel_y)470 void MapWindow::moveCursorRelative(sint16 rel_x, sint16 rel_y) {
471 moveCursor(cursor_x + rel_x, cursor_y + rel_y);
472 }
473
is_on_screen(uint16 x,uint16 y,uint8 z)474 bool MapWindow::is_on_screen(uint16 x, uint16 y, uint8 z) {
475 if (z == cur_level && WRAP_VIEWP(cur_x, x, map_width) < win_width && y >= cur_y && y < cur_y + win_height) {
476 if (tile_is_black(x, y) == false) {
477 return true;
478 }
479 }
480
481 return false;
482 }
483
484 /**
485 * Can we display an object at this location on the tmp map buffer.
486 *
487 * @param x coord on the tmp buf
488 * @param y coord on the tmp buf
489 * @param obj object to display
490 * @return
491 */
can_display_obj(uint16 x,uint16 y,Obj * obj)492 bool MapWindow::can_display_obj(uint16 x, uint16 y, Obj *obj) {
493 uint16 tile_num = tmp_map_buf[y * tmp_map_width + x];
494 if (tile_num == 0) //don't draw object if area is in darkness.
495 return false;
496 else {
497 if (x >= tmp_map_width - 1 || y >= tmp_map_height - 1)
498 return false;
499
500 // We don't show objects on walls if the area to the right or bottom of the wall is in darkness
501 if (tmp_map_buf[y * tmp_map_width + (x + 1)] == 0 || tmp_map_buf[(y + 1)*tmp_map_width + x] == 0) {
502 Tile *tile = tile_manager->get_tile(tile_num);
503 if (((tile->flags1 & TILEFLAG_WALL) || (game_type == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_BARS)))
504 return false;
505 }
506 }
507
508 return true;
509 }
510
tile_is_black(uint16 x,uint16 y,Obj * obj)511 bool MapWindow::tile_is_black(uint16 x, uint16 y, Obj *obj) {
512 if (game->using_hackmove())
513 return false;
514 if (!MapCoord(x, y, cur_level).is_visible()) // tmpBufTileIsBlack will crash if called (doesn't happen in gdb)
515 return true;
516 uint16 wrapped_x = WRAP_VIEWP(cur_x, x, map_width);
517 if (tmpBufTileIsBlack(wrapped_x + TMP_MAP_BORDER, y - cur_y + TMP_MAP_BORDER))
518 return true;
519 else if (obj) {
520 Tile *tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
521 if (!tile || (tmpBufTileIsBlack(wrapped_x + TMP_MAP_BORDER + 1, y - cur_y + TMP_MAP_BORDER) && !(tile->flags1 & TILEFLAG_WALL))
522 || (tmpBufTileIsBlack(wrapped_x + TMP_MAP_BORDER, y - cur_y + TMP_MAP_BORDER + 1) && !(tile->flags1 & TILEFLAG_WALL)))
523 return true;
524 }
525
526 return false;
527 }
528
look(uint16 x,uint16 y,bool show_prefix)529 const char *MapWindow::look(uint16 x, uint16 y, bool show_prefix) {
530 Actor *actor;
531
532 if (tmp_map_buf[(y + TMP_MAP_BORDER) * tmp_map_width + (x + TMP_MAP_BORDER)] == 0) //black area
533 return "darkness."; // nothing to see here. ;)
534
535 uint16 wrapped_x = WRAPPED_COORD(cur_x + x, cur_level);
536 actor = actor_manager->get_actor(wrapped_x, cur_y + y, cur_level);
537 if (actor != NULL && actor->is_visible())
538 return actor_manager->look_actor(actor, show_prefix);
539
540 return map->look(wrapped_x, cur_y + y, cur_level);
541 }
542
543
get_objAtCursor(bool for_use)544 Obj *MapWindow::get_objAtCursor(bool for_use /* = false */) {
545 MapCoord coord = get_cursorCoord();
546 return get_objAtCoord(coord, OBJ_SEARCH_TOP, OBJ_EXCLUDE_IGNORED, for_use);
547 }
548
get_objAtCoord(MapCoord coord,bool top_obj,bool include_ignored_objects,bool for_use)549 Obj *MapWindow::get_objAtCoord(MapCoord coord, bool top_obj, bool include_ignored_objects, bool for_use /* = false */) {
550 if (tile_is_black(coord.x, coord.y))
551 return NULL; // nothing to see here. ;)
552
553 Obj *obj = obj_manager->get_obj(coord.x, coord.y, coord.z, top_obj, include_ignored_objects);
554 // Savage Empire Create Object from Tile
555 if (for_use && game_type == NUVIE_GAME_SE && obj == NULL) {
556 Script *script = game->get_script();
557 uint16 map_win_x = WRAP_VIEWP(cur_x, coord.x, map_width);
558 uint16 map_win_y = coord.y - cur_y;
559 // Check that x,y is in tmp_map_buf
560 if (is_on_screen(coord.x, coord.y, coord.z)) {
561 uint16 tile_n = tmp_map_buf[(map_win_y + TMP_MAP_BORDER) * tmp_map_width + (map_win_x + TMP_MAP_BORDER)];
562 uint16 obj_n = script->call_get_tile_to_object_mapping(tile_n);
563 if (obj_n != 0) {
564 obj = obj_manager->get_tile_obj(obj_n);
565 obj->x = coord.x;
566 obj->y = coord.y;
567 obj->z = coord.z;
568 }
569 }
570 }
571 return obj;
572 }
573
get_actorAtCursor()574 Actor *MapWindow::get_actorAtCursor() {
575 //Actor *actor;
576
577 if (tmp_map_buf[(cursor_y + TMP_MAP_BORDER) * tmp_map_width + (cursor_x + TMP_MAP_BORDER)] == 0) //black area
578 return NULL; // nothing to see here. ;)
579
580 return actor_manager->get_actor(WRAPPED_COORD(cur_x + cursor_x, cur_level), cur_y + cursor_y, cur_level);
581 }
582
get_cursorCoord()583 MapCoord MapWindow::get_cursorCoord() {
584 return (MapCoord(WRAPPED_COORD(cur_x + cursor_x, cur_level), cur_y + cursor_y, cur_level));
585 }
586
get_level(uint8 * level)587 void MapWindow::get_level(uint8 *level) {
588 *level = cur_level;
589 }
590
get_pos(uint16 * x,uint16 * y,uint8 * px,uint8 * py)591 void MapWindow::get_pos(uint16 *x, uint16 *y, uint8 *px, uint8 *py) {
592 *x = cur_x;
593 *y = cur_y;
594 if (px)
595 *px = cur_x_add;
596 if (py)
597 *py = cur_y_add;
598 }
599
get_windowSize(uint16 * width,uint16 * height)600 void MapWindow::get_windowSize(uint16 *width, uint16 *height) {
601 *width = win_width;
602 *height = win_height;
603 }
604
605 /* Returns true if the location at the coordinates is visible on the map window.
606 */
in_window(uint16 x,uint16 y,uint8 z)607 bool MapWindow::in_window(uint16 x, uint16 y, uint8 z) {
608 return ((z == cur_level && WRAP_VIEWP(cur_x, x, map_width) < win_width
609 && y >= cur_y && y <= (cur_y + win_height)));
610 }
611
612
613 /* Update player position if walking to mouse cursor. Update map position.
614 */
update()615 void MapWindow::update() {
616 GameClock *clock = game->get_clock();
617 Events *event = game->get_event();
618 static bool game_started = false; // set to true on the first update()
619 static uint32 last_update_time = clock->get_ticks();
620 uint32 update_time = clock->get_ticks();
621
622 // do fade-in on the first update (game has loaded now)
623 if (game_started == false) {
624 // new FadeEffect(FADE_PIXELATED_ONTOP, FADE_IN, 0x31);
625 new GameFadeInEffect(game->get_palette()->get_bg_color());
626 game_started = true;
627 }
628
629 anim_manager->update(); // update animations
630
631 if (vel_x || vel_y) { // this slides the map
632 if ((update_time - last_update_time) >= 100) { // only move every 10th sec
633 sint32 sx = vel_x / 10, sy = vel_y / 10;
634 if (vel_x && !sx) // move even if vel_x/vel_y was < 10
635 sx = (vel_x < 0) ? -1 : 1;
636 if (vel_y && !sy)
637 sy = (vel_y < 0) ? -1 : 1;
638
639 shiftMapRelative(sx, sy);
640 last_update_time = update_time;
641 }
642 }
643
644 if (walking) {
645 if (Events::get()->getButtonState() & walk_button_mask) {
646 if (game->user_paused())
647 return;
648
649 int mx, my; // bit-AND buttons with mouse state to test
650 screen->get_mouse_location(&mx, &my);
651
652 if (is_wizard_eye_mode()) {
653 // int wx, wy;
654 // mouseToWorldCoords(mx, my, wx, wy);
655 sint16 rx, ry;
656 get_movement_direction((uint16)mx, (uint16)my, rx, ry);
657 moveMapRelative((rx == 0) ? 0 : rx < 0 ? -1 : 1,
658 (ry == 0) ? 0 : ry < 0 ? -1 : 1);
659 wizard_eye_update();
660 } else {
661 //DEBUG(0, LEVEL_DEBUGGING, "MOUSE WALKING...\n");
662 event->walk_to_mouse_cursor((uint32)mx,
663 (uint32)my);
664 }
665 } else
666 walking = false;
667 }
668
669 KeyBinder *keybinder = game->get_keybinder();
670 if (keybinder->is_joy_repeat_enabled() && (event->get_mode() == MOVE_MODE || is_wizard_eye_mode())
671 && keybinder->get_next_joy_repeat_time() < clock->get_ticks()) {
672 Common::KeyCode key = keybinder->get_key_from_joy_walk_axes();
673 if (key != Common::KEYCODE_INVALID) {
674 Common::Event sdl_event;
675 sdl_event.type = Common::EVENT_KEYDOWN;
676 sdl_event.kbd.keycode = key;
677 sdl_event.kbd.flags = 0;
678 if (GUI::get_gui()->HandleEvent(&sdl_event) == GUI_PASS)
679 event->handleEvent(&sdl_event);
680 }
681 }
682 }
683
684 // moved from updateBlacking() so you don't have to update all blacking (SB-X)
updateAmbience()685 void MapWindow::updateAmbience() {
686 lighting_update_required = true;
687 }
688
689 // moved from updateBlacking() so you don't have to update all blacking (SB-X)
createLightOverlay()690 void MapWindow::createLightOverlay() {
691 //Dusk starts at 19:00
692 //It's completely dark by 20:00
693 //Dawn starts at 5:00
694 //It's completely bright by 6:00
695 //Dusk and dawn operate by changing the ambient light, not by changing the radius of the avatar's light globe
696
697 if (!screen)
698 return;
699 bool dawn_or_dusk = false;
700 uint8 cur_min_brightness;
701 if (game->are_cheats_enabled())
702 cur_min_brightness = min_brightness;
703 else
704 cur_min_brightness = 0;
705
706 GameClock *clock = game->get_clock();
707 Weather *weather = game->get_weather();
708
709 int h = clock->get_hour();
710 int a;
711 if (x_ray_view >= X_RAY_ON)
712 a = 255;
713 else if (in_dungeon_level())
714 a = cur_min_brightness;
715 else if (weather->is_eclipse()) //solar eclipse
716 a = cur_min_brightness;
717 else if (h == 19) { //Dusk -- Smooth transition between 255 and min_brightness during first 59 minutes
718 if (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH) {
719 dawn_or_dusk = true;
720 a = 255 - (uint8)((255.0f - cur_min_brightness) * (float)clock->get_minute() / 59.0f);
721 } else {
722 a = 20 * (6 - clock->get_minute() / 10);
723 if (a < cur_min_brightness)
724 a = cur_min_brightness;
725 }
726 } else if (h == 5) { //Dawn -- Smooth transition between min_brightness and 255 during first 59 minutes
727 if (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH) {
728 dawn_or_dusk = true;
729 a = cur_min_brightness + (255.0f - cur_min_brightness) * (float)clock->get_minute() / 59.0f;
730 } else {
731 a = 20 * (1 + clock->get_minute() / 10);
732 if (a < cur_min_brightness)
733 a = cur_min_brightness;
734 }
735 } else if (h > 5 && h < 19) //Day
736 a = 255;
737 else //Night
738 a = cur_min_brightness;
739
740 if (a > 255)
741 a = 255;
742 bool party_light_source;
743 // smooth seems to need an enormous range in order to have smooth transitions
744 if (a < (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH ? 248 : 81) &&
745 (game->get_party()->has_light_source() || clock->get_timer(GAMECLOCK_TIMER_U6_LIGHT) != 0)) { //FIXME U6 specific
746 party_light_source = true;
747 if (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH) {
748 if (!dawn_or_dusk) // preserve a when dusk or dawn so we have the correct opacity
749 a = cur_min_brightness;
750 } else
751 a = 80;
752 } else
753 party_light_source = false;
754 screen->set_ambient(a);
755
756 //Clear the opacity map
757 screen->clearalphamap8(0, 0, win_width, win_height, screen->get_ambient(), party_light_source);
758
759 updateLighting();
760
761 lighting_update_required = false;
762 }
763
updateLighting()764 void MapWindow::updateLighting() {
765 uint16 x, y;
766 if (using_map_tile_lighting) {
767 uint16 *ptr = tmp_map_buf;
768 for (y = 0; y < tmp_map_height; y++) {
769 for (x = 0; x < tmp_map_width; x++) {
770 if (tmp_map_buf[x + y * tmp_map_width] != 0) {
771 Tile *tile = tile_manager->get_tile(*ptr);
772 if (GET_TILE_LIGHT_LEVEL(tile) > 0)
773 screen->drawalphamap8globe(x - TMP_MAP_BORDER, y - TMP_MAP_BORDER, GET_TILE_LIGHT_LEVEL(tile));
774
775 U6LList *obj_list = obj_manager->get_obj_list(cur_x - TMP_MAP_BORDER + x, cur_y - TMP_MAP_BORDER + y, cur_level); //FIXME wrapped coords.
776 if (obj_list) {
777 for (U6Link *link = obj_list->start(); link != NULL; link = link->next) {
778 Obj *obj = (Obj *)link->data;
779 tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(obj) + obj->frame_n); //FIXME do we need to check the light for each tile in a multi-tile object.
780 if (GET_TILE_LIGHT_LEVEL(tile) > 0 && can_display_obj(x, y, obj))
781 screen->drawalphamap8globe(x - TMP_MAP_BORDER, y - TMP_MAP_BORDER, GET_TILE_LIGHT_LEVEL(tile));
782 }
783 }
784 }
785 ptr++;
786 }
787 }
788
789 for (Std::vector<TileInfo>::iterator ti = m_ViewableMapTiles.begin();
790 ti != m_ViewableMapTiles.end(); ti++) {
791 if (GET_TILE_LIGHT_LEVEL((*ti).t) > 0)
792 screen->drawalphamap8globe((*ti).x, (*ti).y, GET_TILE_LIGHT_LEVEL((*ti).t));
793 }
794 }
795
796 /* draw light coming from the actor
797 Wisps can change the light level depending on their current tile so we can't use actor->light for an actor's innate lighting.
798 */
799 Actor *actor;
800
801 for (uint16 i = 0; i < 256; i++) {
802 actor = actor_manager->get_actor(i);
803
804 if (actor->z == cur_level) {
805 if (actor->x >= cur_x - TMP_MAP_BORDER && actor->x < cur_x + win_width + TMP_MAP_BORDER) {
806 if (actor->y >= cur_y - TMP_MAP_BORDER && actor->y < cur_y + win_height + TMP_MAP_BORDER) {
807 if (tmp_map_buf[(actor->y - cur_y + TMP_MAP_BORDER) * tmp_map_width + (actor->x - cur_x + TMP_MAP_BORDER)] != 0) {
808 uint8 light = actor->get_light_level();
809 if (light > 0) {
810 screen->drawalphamap8globe(actor->x - cur_x, actor->y - cur_y, light);
811 }
812 }
813 }
814 }
815 }
816 }
817 }
818
updateBlacking()819 void MapWindow::updateBlacking() {
820 generateTmpMap();
821
822 updateAmbience();
823
824 m_ViewableObjects.clear();
825 /// m_ViewableObjTiles.clear();
826
827 draw_brit_lens_anim = false;
828 draw_garg_lens_anim = false;
829
830 window_updated = true;
831 }
832
Display(bool full_redraw)833 void MapWindow::Display(bool full_redraw) {
834 uint16 i, j;
835 uint16 *map_ptr;
836 // uint16 map_width;
837 Tile *tile;
838 //byte *ptr;
839
840 if (lighting_update_required) {
841 createLightOverlay();
842 }
843
844 //map_ptr = map->get_map_data(cur_level);
845 // map_width = map->get_width(cur_level);
846
847 //map_ptr += cur_y * map_width + cur_x;
848 map_ptr = tmp_map_buf;
849 map_ptr += (TMP_MAP_BORDER * tmp_map_width + TMP_MAP_BORDER);// * sizeof(uint16); //remember our tmp map is TMP_MAP_BORDER bigger all around.
850
851 for (i = 0; i < win_height; i++) {
852 for (j = 0; j < win_width; j++) {
853 sint16 draw_x = area.left + (j * 16), draw_y = area.top + (i * 16);
854 //draw_x -= (cur_x_add <= draw_x) ? cur_x_add : draw_x;
855 //draw_y -= (cur_y_add <= draw_y) ? cur_y_add : draw_y;
856 draw_x -= cur_x_add;
857 draw_y -= cur_y_add;
858 if (map_ptr[j] == 0)
859 screen->clear(draw_x, draw_y, 16, 16, &clip_rect); //blackout tile.
860 else {
861 if (map_ptr[j] >= 16 && map_ptr[j] < 48) { //lay down the base tile for shoreline tiles
862 tile = tile_manager->get_anim_base_tile(map_ptr[j]);
863 screen->blit(draw_x, draw_y, (byte *)tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
864 }
865
866 tile = tile_manager->get_tile(map_ptr[j]);
867 screen->blit(draw_x, draw_y, (byte *)tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
868
869 }
870
871 }
872 //map_ptr += map_width;
873 map_ptr += tmp_map_width ;//* sizeof(uint16);
874 }
875
876 drawObjs();
877
878 //drawAnims();
879
880 if (roof_mode && roof_display != ROOF_DISPLAY_OFF) {
881 drawRoofs();
882 }
883
884 if (game->get_clock()->get_timer(GAMECLOCK_TIMER_U6_STORM) != 0) //FIXME u6 specific.
885 drawRain();
886
887 if (show_grid) {
888 drawGrid();
889 }
890
891 if (show_cursor) {
892 screen->blit(area.left + cursor_x * 16, area.top + cursor_y * 16, (byte *)cursor_tile->data, 8, 16, 16, 16, true, &clip_rect);
893 }
894
895 if (show_use_cursor) {
896 screen->blit(area.left + cursor_x * 16, area.top + cursor_y * 16, (byte *)use_tile->data, 8, 16, 16, 16, true, &clip_rect);
897 }
898
899 // screen->fill(0,8,8,win_height*16-16,win_height*16-16);
900
901 screen->blitalphamap8(area.left, area.top, &clip_rect);
902
903 if (game->get_clock()->get_timer(GAMECLOCK_TIMER_U6_INFRAVISION) != 0)
904 drawActors();
905
906 if (overlay && overlay_level == MAP_OVERLAY_DEFAULT)
907 screen->blit(area.left, area.top, (byte *)(overlay->getPixels()), overlay->format.bpp(), overlay->w, overlay->h, overlay->pitch, true, &clip_rect);
908
909 drawAnims(true);
910
911 if (new_thumbnail)
912 create_thumbnail();
913
914 if (is_wizard_eye_mode()) {
915 uint16 we_x = mousecenter_x * 16 + area.left;
916 if (game->is_original_plus_full_map())
917 we_x -= ((map_center_xoff + 1) / 2) * 16;
918 screen->blit(we_x, mousecenter_y * 16 + area.top, (byte *)wizard_eye_info.eye_tile->data, 8, 16, 16, 16, true, &clip_rect);
919 }
920
921 if (game->is_orig_style())
922 drawBorder();
923
924 if (overlay && overlay_level == MAP_OVERLAY_ONTOP)
925 screen->blit(area.left, area.top, (byte *)(overlay->getPixels()), overlay->format.bpp(), overlay->w, overlay->h, overlay->pitch, true, &clip_rect);
926
927 // ptr = (byte *)screen->get_pixels();
928 // ptr += 8 * screen->get_pitch() + 8;
929
930 // screen->blit(8,8,ptr,8,(win_width-1) * 16,(win_height-1) * 16, win_width * 16, false);
931
932 if (game->is_orig_style())
933 screen->update(area.left + 8, area.top + 8, win_width * 16 - 16, win_height * 16 - 16);
934 else if (game->is_original_plus_cutoff_map())
935 screen->update(Game::get_game()->get_game_x_offset(), Game::get_game()->get_game_y_offset(), game->get_game_width() - border_width - 1, game->get_game_height());
936 else
937 screen->update(Game::get_game()->get_game_x_offset(), Game::get_game()->get_game_y_offset(), game->get_game_width(), game->get_game_height());
938
939 if (window_updated) {
940 window_updated = false;
941 game->get_sound_manager()->update_map_sfx();
942 }
943
944 }
945
drawActors()946 void MapWindow::drawActors() {
947 uint16 i;
948 Actor *actor;
949
950 for (i = 0; i < 256; i++) {
951 actor = actor_manager->get_actor(i);
952
953 if (actor->z == cur_level) {
954 uint8 x = WRAP_VIEWP(cur_x, actor->x, map_width);
955 if (x < win_width) { //actor->x >= cur_x && actor->x < cur_x + win_width)
956 if (actor->y >= cur_y && actor->y < cur_y + win_height) {
957 if (tmp_map_buf[(actor->y - cur_y + TMP_MAP_BORDER) * tmp_map_width + (x + TMP_MAP_BORDER)] != 0) {
958 drawActor(actor);
959 }
960 }
961 }
962 }
963 }
964 }
965
966 //FIX need a function for multi-tile actors.
drawActor(Actor * actor)967 inline void MapWindow::drawActor(Actor *actor) {
968 if (actor->is_visible() /* && actor->obj_n != 0*/
969 && (!(actor->obj_flags & OBJ_STATUS_INVISIBLE) || actor->is_in_party() || actor == actor_manager->get_player())
970 && actor->get_corpser_flag() == false) {
971 Tile *tile = tile_manager->get_tile(actor->get_tile_num() + actor->frame_n);
972 Tile *rtile = 0;
973
974 if (actor->obj_flags & OBJ_STATUS_INVISIBLE) {
975 rtile = new Tile(*tile);
976 for (int x = 0; x < 256; x++)
977 if (rtile->data[x] != 0x00)
978 rtile->data[x] = 0xFF;
979 else
980 rtile->data[x] = 0x0B;
981 } else if (actor->status_flags & ACTOR_STATUS_PROTECTED) { // actually this doesn't appear when using a protection ring
982 rtile = new Tile(*tile);
983 for (int x = 0; x < 256; x++)
984 if (rtile->data[x] == 0x00)
985 rtile->data[x] = 0x0C;
986 } else if (actor->is_cursed()) {
987 rtile = new Tile(*tile);
988 for (int x = 0; x < 256; x++)
989 if (rtile->data[x] == 0x00)
990 rtile->data[x] = 0x9;
991 }
992 uint16 wrapped_x = WRAP_VIEWP(cur_x, actor->x, map_width);
993 if (rtile != 0) {
994 drawNewTile(rtile, wrapped_x, actor->y - cur_y, false);
995 drawNewTile(rtile, wrapped_x, actor->y - cur_y, true);
996 delete rtile;
997 } else {
998 drawTile(tile, wrapped_x, actor->y - cur_y, false);
999 drawTile(tile, wrapped_x, actor->y - cur_y, true);
1000 if (game->get_clock()->get_timer(GAMECLOCK_TIMER_U6_INFRAVISION) != 0) {
1001 Std::list<Obj *> *surrounding_objs = actor->get_surrounding_obj_list();
1002
1003 if (surrounding_objs) {
1004 Std::list<Obj *>::iterator obj_iter;
1005 for (obj_iter = surrounding_objs->begin(); obj_iter != surrounding_objs->end(); obj_iter++) {
1006 Obj *obj = *obj_iter;
1007 Tile *t = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
1008 uint16 wrapped_obj_x = WRAP_VIEWP(cur_x, obj->x, map_width);
1009 drawTile(t, wrapped_obj_x, obj->y - cur_y, false);
1010 drawTile(t, wrapped_obj_x, obj->y - cur_y, true); // doesn't seem needed but will do it anyway (for now)
1011 }
1012 }
1013 }
1014 }
1015 }
1016 }
1017
drawObjs()1018 void MapWindow::drawObjs() {
1019 //FIX we need to make this more efficent.
1020
1021 drawObjSuperBlock(true, false); //draw force lower objects
1022 drawObjSuperBlock(false, false); //draw lower objects
1023
1024 drawActors();
1025
1026 drawAnims(false);
1027
1028 drawObjSuperBlock(false, true); //draw top objects
1029
1030 drawLensAnim();
1031 return;
1032 }
1033
drawLensAnim()1034 inline void MapWindow::drawLensAnim() {
1035 if (draw_brit_lens_anim) {
1036 if (cur_x < 0x399)
1037 drawTile(tile_manager->get_tile(TILE_U6_BRITANNIAN_LENS_ANIM_2), 0x398 - cur_x, 0x353 - cur_y, true);
1038 if (cur_x + win_width > 0x39a)
1039 drawTile(tile_manager->get_tile(TILE_U6_BRITANNIAN_LENS_ANIM_1), 0x39a - cur_x, 0x353 - cur_y, true);
1040 }
1041
1042 if (draw_garg_lens_anim) {
1043 if (cur_x < 0x39d)
1044 drawTile(tile_manager->get_tile(TILE_U6_GARGOYLE_LENS_ANIM_2), 0x39c - cur_x, 0x353 - cur_y, true);
1045 if (cur_x + win_width > 0x39e)
1046 drawTile(tile_manager->get_tile(TILE_U6_GARGOYLE_LENS_ANIM_1), 0x39e - cur_x, 0x353 - cur_y, true);
1047 }
1048 }
1049
drawObjSuperBlock(bool draw_lowertiles,bool toptile)1050 void MapWindow::drawObjSuperBlock(bool draw_lowertiles, bool toptile) {
1051 U6Link *link;
1052 U6LList *obj_list;
1053 Obj *obj;
1054 sint16 x, y;
1055 uint16 stop_x, stop_y;
1056
1057 if (cur_x < 0)
1058 stop_x = 0;
1059 else
1060 stop_x = cur_x;
1061
1062 if (cur_y < 0)
1063 stop_y = 0;
1064 else
1065 stop_y = cur_y;
1066
1067 for (y = cur_y + win_height; y >= stop_y; y--) {
1068 for (x = cur_x + win_width; x >= stop_x; x--) {
1069 obj_list = obj_manager->get_obj_list(x, y, cur_level);
1070 if (obj_list) {
1071 for (link = obj_list->start(); link != NULL; link = link->next) {
1072 obj = (Obj *)link->data;
1073 drawObj(obj, draw_lowertiles, toptile);
1074 }
1075 }
1076 }
1077 }
1078
1079 }
1080
drawObj(Obj * obj,bool draw_lowertiles,bool toptile)1081 inline void MapWindow::drawObj(Obj *obj, bool draw_lowertiles, bool toptile) {
1082 sint16 x, y;
1083 Tile *tile;
1084
1085 y = obj->y - cur_y;
1086 x = WRAP_VIEWP(cur_x, obj->x, map_width);
1087
1088 if (x < 0 || y < 0)
1089 return;
1090
1091 if (window_updated) {
1092 m_ViewableObjects.push_back(obj);
1093
1094 if (game_type == NUVIE_GAME_U6 && cur_level == 0 && obj->y == 0x353 && tmp_map_buf[(y + TMP_MAP_BORDER)*tmp_map_width + (x + TMP_MAP_BORDER)] != 0) {
1095 if (obj->obj_n == 394 && obj->x == 0x399) {
1096 draw_brit_lens_anim = true;
1097 } else if (obj->obj_n == 396 && obj->x == 0x39d) {
1098 draw_garg_lens_anim = true;
1099 }
1100 }
1101 }
1102
1103 //don't show invisible objects.
1104 if (obj->status & OBJ_STATUS_INVISIBLE)
1105 return;
1106
1107 tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj) + obj->frame_n);
1108
1109 if (draw_lowertiles == false && (tile->flags3 & 0x4) && toptile == false) //don't display force lower tiles.
1110 return;
1111
1112 if (draw_lowertiles == true && !(tile->flags3 & 0x4))
1113 return;
1114
1115 if (tmp_map_buf[(y + TMP_MAP_BORDER)*tmp_map_width + (x + TMP_MAP_BORDER)] == 0) //don't draw object if area is in darkness.
1116 return;
1117 else {
1118 // We don't show objects on walls if the area to the right or bottom of the wall is in darkness
1119 if (tmp_map_buf[(y + TMP_MAP_BORDER)*tmp_map_width + (x + TMP_MAP_BORDER + 1)] == 0 || tmp_map_buf[(y + TMP_MAP_BORDER + 1)*tmp_map_width + (x + TMP_MAP_BORDER)] == 0) {
1120 if ((!(tile->flags1 & TILEFLAG_WALL) || (game_type == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_BARS)))
1121 return;
1122 }
1123 }
1124
1125 drawTile(tile, x, obj->y - cur_y, toptile);
1126
1127 }
1128
1129 /* The pixeldata in the passed Tile pointer will be used if use_tile_data is
1130 * true, otherwise the current tile is derived from tile_num. This can't be
1131 * used with animated tiles. It only applies to the base tile in multi-tiles.
1132 */
drawTile(Tile * tile,uint16 x,uint16 y,bool toptile,bool use_tile_data)1133 inline void MapWindow::drawTile(Tile *tile, uint16 x, uint16 y, bool toptile,
1134 bool use_tile_data) {
1135 bool dbl_width, dbl_height;
1136 uint16 tile_num;
1137
1138 tile_num = tile->tile_num;
1139
1140 //don't show special marker tiles in MD unless "show eggs" is turned on.
1141 if (game_type == NUVIE_GAME_MD
1142 && tile_num > 2040 && tile_num < 2048
1143 && !obj_manager->is_showing_eggs())
1144 return;
1145
1146 /* shouldn't be needed for in_town check
1147 if(window_updated)
1148 {
1149 TileInfo ti;
1150 ti.t=tile;
1151 ti.left=x;
1152 ti.top=y;
1153 m_ViewableObjTiles.push_back(ti);
1154 }
1155 */
1156 dbl_width = tile->dbl_width;
1157 dbl_height = tile->dbl_height;
1158
1159 if (x < win_width && y < win_height)
1160 drawTopTile(use_tile_data ? tile : tile_manager->get_tile(tile_num), x, y, toptile);
1161
1162 if (dbl_width) {
1163 tile_num--;
1164 if (x > 0 && y < win_height) {
1165 tile = tile_manager->get_tile(tile_num);
1166 drawTopTile(tile, x - 1, y, toptile);
1167 }
1168 }
1169
1170 if (dbl_height) {
1171 tile_num--;
1172 if (y > 0 && x < win_width) {
1173 tile = tile_manager->get_tile(tile_num);
1174 drawTopTile(tile, x, y - 1, toptile);
1175 }
1176 }
1177
1178 if (x > 0 && dbl_width && y > 0 && dbl_height) {
1179 tile_num--;
1180 tile = tile_manager->get_tile(tile_num);
1181 drawTopTile(tile, x - 1, y - 1, toptile);
1182 }
1183
1184 }
1185
drawNewTile(Tile * tile,uint16 x,uint16 y,bool toptile)1186 inline void MapWindow::drawNewTile(Tile *tile, uint16 x, uint16 y, bool toptile) {
1187 drawTile(tile, x, y, toptile, true);
1188 }
1189
drawTopTile(Tile * tile,uint16 x,uint16 y,bool toptile)1190 inline void MapWindow::drawTopTile(Tile *tile, uint16 x, uint16 y, bool toptile) {
1191
1192
1193
1194 // if(tile->boundary)
1195 // {
1196 // screen->blit(cursor_tile->data,8,x*16,y*16,16,16,false);
1197 // }
1198 // FIXME: Don't use pixel offset (x_add,y_add) here, pass it via params?
1199 if (toptile) {
1200 if (tile->toptile)
1201 // screen->blit(x*16,y*16,tile->data,8,16,16,16,tile->transparent,&clip_rect);
1202 screen->blit(area.left + (x * 16) - cur_x_add, area.top + (y * 16) - cur_y_add, tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
1203 } else {
1204 if (!tile->toptile)
1205 // screen->blit(x*16,y*16,tile->data,8,16,16,16,tile->transparent,&clip_rect);
1206 screen->blit(area.left + (x * 16) - cur_x_add, area.top + (y * 16) - cur_y_add, tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
1207 }
1208 }
1209
drawBorder()1210 void MapWindow::drawBorder() {
1211 Tile *tile;
1212 Tile *tile1;
1213 uint16 i;
1214
1215 if (game_type != NUVIE_GAME_U6)
1216 return;
1217 uint16 orig_win_w = 11;
1218 uint16 orig_win_h = 11;
1219 uint16 x_off = Game::get_game()->get_game_x_offset();
1220 uint16 y_off = Game::get_game()->get_game_y_offset();
1221
1222 tile = tile_manager->get_tile(432);
1223 screen->blit(x_off, y_off, tile->data, 8, 16, 16, 16, true, &clip_rect); // upper left corner
1224
1225 tile = tile_manager->get_tile(434);
1226 screen->blit(x_off + (orig_win_w - 1) * 16, y_off, tile->data, 8, 16, 16, 16, true); // upper right corner (got rid of &clip_rect for original+)
1227
1228 tile = tile_manager->get_tile(435);
1229 screen->blit(x_off, y_off + (orig_win_h - 1) * 16, tile->data, 8, 16, 16, 16, true, &clip_rect); // lower left corner
1230
1231 tile = tile_manager->get_tile(437);
1232 screen->blit(x_off + (orig_win_w - 1) * 16, y_off + (orig_win_h - 1) * 16, tile->data, 8, 16, 16, 16, true); // lower right corner (got rid of &clip_rect for original+)
1233
1234 tile = tile_manager->get_tile(433);
1235 tile1 = tile_manager->get_tile(436);
1236
1237 for (i = 1; i < orig_win_w - 1; i++) {
1238 screen->blit(x_off + i * 16, y_off, tile->data, 8, 16, 16, 16, true, &clip_rect); // top row
1239 screen->blit(x_off + i * 16, y_off + (orig_win_h - 1) * 16, tile1->data, 8, 16, 16, 16, true, &clip_rect); // bottom row
1240 }
1241
1242 tile = tile_manager->get_tile(438);
1243 tile1 = tile_manager->get_tile(439);
1244
1245 for (i = 1; i < orig_win_h - 1; i++) {
1246 screen->blit(x_off, y_off + i * 16, tile->data, 8, 16, 16, 16, true, &clip_rect); // left column
1247 screen->blit(x_off + (orig_win_w - 1) * 16, y_off + i * 16, tile1->data, 8, 16, 16, 16, true); // right column (got rid of &clip_rect for original+)
1248 }
1249 }
1250
1251 //FIXME this won't work for wrapped maps like MD.
drawRoofs()1252 void MapWindow::drawRoofs() {
1253 if (cur_y < 1 || cur_y > 760) // FIXME We need to handle this properly
1254 return;
1255 if (roof_display == ROOF_DISPLAY_NORMAL && map->has_roof(WRAPPED_COORD(cur_x + (win_width - 1 - map_center_xoff) / 2, cur_level), cur_y + (win_height - 1) / 2, cur_level)) //Don't draw roof tiles if player is underneath.
1256 return;
1257 if (x_ray_view >= X_RAY_ON)
1258 return;
1259
1260 bool orig_style = game->is_orig_style();
1261
1262 uint16 *roof_map_ptr = map->get_roof_data(cur_level);
1263
1264 Common::Rect src(16, 16), dst(16, 16);
1265
1266 if (roof_map_ptr) {
1267 roof_map_ptr += cur_y * 1024 + cur_x;
1268 for (uint16 i = 0; i < win_height; i++) {
1269 for (uint16 j = 0; j < win_width; j++) {
1270 if (roof_map_ptr[j] != 0) {
1271 dst.left = area.left + (j * 16);
1272 dst.top = area.top + (i * 16);
1273 dst.left -= cur_x_add;
1274 dst.top -= cur_y_add;
1275
1276 src.left = (roof_map_ptr[j] % MAPWINDOW_ROOFTILES_IMG_W) * 16;
1277 src.top = (roof_map_ptr[j] / MAPWINDOW_ROOFTILES_IMG_W) * 16;
1278
1279 if (orig_style) {
1280 src.setWidth(16);
1281 src.setHeight(16);
1282 dst.setWidth(16);
1283 dst.setHeight(16);
1284
1285 if (i == 0) {
1286 src.top += 8;
1287 src.setHeight(8);
1288 dst.top += 8;
1289 dst.setHeight(8);
1290 } else if (i == win_height - 1) {
1291 src.setHeight(8);
1292 dst.setHeight(8);
1293 }
1294
1295 if (j == 0) {
1296 src.left += 8;
1297 src.setWidth(8);
1298 dst.left += 8;
1299 dst.setWidth(8);
1300 } else if (j == win_width - 1) {
1301 src.setWidth(8);
1302 dst.setWidth(8);
1303 }
1304 SDL_BlitSurface(roof_tiles, &src, surface, &dst);
1305 } else {
1306 byte *ptr = (byte *)roof_tiles->getPixels();
1307 ptr += src.left + src.top * 80;
1308 screen->blit(dst.left, dst.top, ptr, 8, 16, 16, 80, true, &clip_rect);
1309 }
1310 }
1311 }
1312 roof_map_ptr += 1024;
1313 }
1314 }
1315 }
1316
drawRain()1317 void MapWindow::drawRain() {
1318 int c;
1319 if (game->is_orig_style())
1320 c = win_width * win_height;
1321 else if (game->is_original_plus_cutoff_map())
1322 c = ((game->get_game_width() - border_width) * game->get_game_height()) / 256;
1323 else
1324 c = (game->get_game_width() * game->get_game_height()) / 256;
1325 for (int i = 0; i < c; i++) {
1326 uint16 x;
1327 uint16 y;
1328 if (game->is_orig_style()) {
1329 x = area.left + NUVIE_RAND() % ((win_width - 1) * 16 - 2) + 8;
1330 y = area.top + NUVIE_RAND() % ((win_height - 1) * 16 - 2) + 8;
1331 } else {
1332 if (game->is_original_plus_cutoff_map())
1333 x = game->get_game_x_offset() + NUVIE_RAND() % (game->get_game_width() - border_width - 2);
1334 else
1335 x = game->get_game_x_offset() + NUVIE_RAND() % (game->get_game_width() - 2);
1336 y = game->get_game_y_offset() + NUVIE_RAND() % (game->get_game_height() - 2);
1337 }
1338
1339 //FIXME the original does something with the palette if a pixel is black then draw gray etc.
1340 //We can't do this easily here because we don't have the original 8 bit display surface.
1341 screen->put_pixel(118, x, y);
1342 screen->put_pixel(118, x + 1, y + 1);
1343 screen->put_pixel(0, x + 2, y + 2);
1344 }
1345 }
1346
AddMapTileToVisibleList(uint16 tile_num,uint16 x,uint16 y)1347 void MapWindow::AddMapTileToVisibleList(uint16 tile_num, uint16 x, uint16 y) {
1348 if (x >= TMP_MAP_BORDER &&
1349 y >= TMP_MAP_BORDER &&
1350 x < tmp_map_width - TMP_MAP_BORDER &&
1351 y < tmp_map_height - TMP_MAP_BORDER) {
1352 TileInfo ti;
1353 ti.t = tile_manager->get_tile(tile_num);
1354 ti.x = (uint16)(x - TMP_MAP_BORDER);
1355 ti.y = (uint16)(y - TMP_MAP_BORDER);
1356 m_ViewableMapTiles.push_back(ti);
1357 }
1358 }
1359
drawGrid()1360 void MapWindow::drawGrid() {
1361 for (uint16 i = 0; i < win_height; i++) {
1362 for (uint16 j = 0; j < win_width; j++) {
1363 screen->blit(area.left + (j * 16) - cur_x_add, area.top + (i * 16) - cur_y_add,
1364 (const byte *)grid_tile.data, 8, 16, 16, 16, true);
1365 }
1366 }
1367 }
1368
generateTmpMap()1369 void MapWindow::generateTmpMap() {
1370 byte *map_ptr;
1371 uint16 pitch;
1372 uint16 x, y;
1373 Tile *tile;
1374
1375 m_ViewableMapTiles.clear();
1376
1377 map_ptr = map->get_map_data(cur_level);
1378 pitch = map->get_width(cur_level);
1379
1380 if (enable_blacking == false) {
1381 uint16 *ptr = tmp_map_buf;
1382 for (y = 0; y < tmp_map_height; y++) {
1383 for (x = 0; x < tmp_map_width; x++) {
1384 uint16 x1 = cur_x + x - TMP_MAP_BORDER;
1385 uint16 y1 = cur_y + y - TMP_MAP_BORDER;
1386 WRAP_COORD(x1, cur_level);
1387 WRAP_COORD(y1, cur_level);
1388 *ptr = map_ptr[y1 * pitch + x1];
1389 AddMapTileToVisibleList(*ptr, x, y);
1390
1391 ptr++;
1392 }
1393 }
1394 return;
1395 }
1396
1397 roof_display = ROOF_DISPLAY_NORMAL;
1398
1399 memset(tmp_map_buf, 0, tmp_map_width * tmp_map_height * sizeof(uint16));
1400
1401 if (freeze_blacking_location == false) {
1402 x = cur_x + ((win_width - 1 - map_center_xoff) / 2);
1403 y = cur_y + ((win_height - 1) / 2);
1404 } else { // SB-X
1405 x = last_boundary_fill_x;
1406 y = last_boundary_fill_y;
1407 }
1408
1409 WRAP_COORD(x, cur_level);
1410 WRAP_COORD(y, cur_level);
1411
1412 //This is for U6. Sherry needs to pass through walls
1413 //We shift the boundary fill start location off the wall tile so it flood
1414 //fills correctly. We move east for vertical wall tiles and south for
1415 //horizontal wall tiles.
1416 if (game_type == NUVIE_GAME_U6 && obj_manager->is_boundary(x, y, cur_level)) {
1417 tile = obj_manager->get_obj_tile(x, y, cur_level, false);
1418 if ((tile->flags1 & TILEFLAG_WALL_MASK) == (TILEFLAG_WALL_NORTH | TILEFLAG_WALL_SOUTH))
1419 x = WRAPPED_COORD(x + 1, cur_level);
1420 else
1421 y = WRAPPED_COORD(y + 1, cur_level);
1422 }
1423 last_boundary_fill_x = x;
1424 last_boundary_fill_y = y;
1425 boundaryFill(map_ptr, pitch, x, y);
1426
1427 reshapeBoundary();
1428
1429 if (roof_mode && floorTilesVisible())
1430 roof_display = ROOF_DISPLAY_OFF; // hide roof if a building's floor is showing.
1431 }
1432
boundaryFill(byte * map_ptr,uint16 pitch,uint16 x,uint16 y)1433 void MapWindow::boundaryFill(byte *map_ptr, uint16 pitch, uint16 x, uint16 y) {
1434 unsigned char current;
1435 uint16 *ptr;
1436 uint16 pos;
1437 uint16 tmp_x, tmp_y;
1438 uint16 p_cur_x, p_cur_y; //wrapped cur_x - 1 and wrapped cur_y - 1
1439
1440 p_cur_x = WRAPPED_COORD(cur_x - TMP_MAP_BORDER, cur_level);
1441 p_cur_y = WRAPPED_COORD(cur_y - TMP_MAP_BORDER, cur_level);
1442
1443 if (x == WRAPPED_COORD(p_cur_x - 1, cur_level) || x == WRAPPED_COORD(p_cur_x + tmp_map_width, cur_level))
1444 return;
1445
1446 if (y == WRAPPED_COORD(p_cur_y - 1, cur_level) || y == WRAPPED_COORD(p_cur_y + tmp_map_height, cur_level))
1447 return;
1448
1449 if (p_cur_y > y)
1450 tmp_y = pitch - p_cur_y + y;
1451 else
1452 tmp_y = y - p_cur_y;
1453
1454 pos = tmp_y * tmp_map_width;
1455
1456 if (p_cur_x > x)
1457 tmp_x = pitch - p_cur_x + x;
1458 else
1459 tmp_x = x - p_cur_x;
1460
1461 pos += tmp_x;
1462
1463 ptr = &tmp_map_buf[pos];
1464
1465 if (*ptr != 0)
1466 return;
1467
1468 current = map_ptr[y * pitch + x];
1469
1470 *ptr = (uint16)current;
1471
1472 AddMapTileToVisibleList(current, tmp_x, tmp_y);
1473
1474 if (x_ray_view <= X_RAY_OFF && map->is_boundary(x, y, cur_level)) { //hit the boundary wall tiles
1475 if (boundaryLookThroughWindow(*ptr, x, y) == false)
1476 return;
1477 else
1478 roof_display = ROOF_DISPLAY_OFF; //hide roof tiles if player is looking through window.
1479 }
1480
1481
1482 uint16 xp1, xm1;
1483 uint16 yp1, ym1;
1484
1485 xp1 = WRAPPED_COORD(x + 1, cur_level);
1486 xm1 = WRAPPED_COORD(x - 1, cur_level);
1487
1488 yp1 = WRAPPED_COORD(y + 1, cur_level);
1489 ym1 = WRAPPED_COORD(y - 1, cur_level);
1490
1491 boundaryFill(map_ptr, pitch, xp1, y);
1492 boundaryFill(map_ptr, pitch, x, yp1);
1493 boundaryFill(map_ptr, pitch, xp1, yp1);
1494 boundaryFill(map_ptr, pitch, xm1, ym1);
1495 boundaryFill(map_ptr, pitch, xm1, y);
1496 boundaryFill(map_ptr, pitch, x, ym1);
1497 boundaryFill(map_ptr, pitch, xp1, ym1);
1498 boundaryFill(map_ptr, pitch, xm1, yp1);
1499
1500
1501 return;
1502 }
1503
floorTilesVisible()1504 bool MapWindow::floorTilesVisible() {
1505 Actor *actor;
1506 uint16 a_x, a_y;
1507 uint8 a_z;
1508 actor = actor_manager->get_player();
1509 if (!actor)
1510 return false;
1511 actor->get_location(&a_x, &a_y, &a_z);
1512 uint16 cX = WRAPPED_COORD(a_x - 1, cur_level), eX = WRAPPED_COORD(a_x + 2, cur_level);
1513 uint16 cY = WRAPPED_COORD(a_y - 1, cur_level), eY = WRAPPED_COORD(a_y + 2, cur_level);
1514
1515 for (; cY != eY;) {
1516 for (; cX != eX;) {
1517 if (map->has_roof(cX, cY, cur_level) && !map->is_boundary(cX, cY, cur_level)) {
1518 Tile *t = obj_manager->get_obj_tile(cX, cY, cur_level, false);
1519 if (t && (t->flags1 & TILEFLAG_WALL))
1520 return true;
1521 }
1522 cX = WRAPPED_COORD(cX + 1, cur_level);
1523 }
1524 cX = WRAPPED_COORD(a_x - 1, cur_level);
1525 cY = WRAPPED_COORD(cY + 1, cur_level);
1526 }
1527
1528 return false;
1529 }
1530
boundaryLookThroughWindow(uint16 tile_num,uint16 x,uint16 y)1531 bool MapWindow::boundaryLookThroughWindow(uint16 tile_num, uint16 x, uint16 y) {
1532 Tile *tile;
1533 Actor *actor;
1534 uint16 a_x, a_y;
1535 uint8 a_z;
1536 Obj *obj;
1537
1538 tile = tile_manager->get_tile(tile_num);
1539 if (!(tile->flags2 & TILEFLAG_WINDOW)) {
1540 obj = obj_manager->get_objBasedAt(x, y, cur_level, true);
1541 if (obj) { //check for a windowed object.
1542 tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
1543 if (!(tile->flags2 & TILEFLAG_WINDOW))
1544 return false;
1545 } else
1546 return false;
1547 }
1548
1549 actor = actor_manager->get_player();
1550 actor->get_location(&a_x, &a_y, &a_z);
1551
1552 if (a_x == x) {
1553 if (a_y == WRAPPED_COORD(y - 1, cur_level) || a_y == WRAPPED_COORD(y + 1, cur_level))
1554 return true;
1555 }
1556
1557 if (a_y == y) {
1558 if (a_x == WRAPPED_COORD(x - 1, cur_level) || a_x == WRAPPED_COORD(x + 1, cur_level))
1559 return true;
1560 }
1561
1562 return false;
1563 }
1564
1565 //reshape walls based on new blacked out areas.
1566
reshapeBoundary()1567 void MapWindow::reshapeBoundary() {
1568 uint16 x, y;
1569 uint8 flag, original_flag;
1570 Tile *tile;
1571
1572 for (y = 1; y < tmp_map_height - 1; y++) {
1573 for (x = 1; x < tmp_map_width - 1; x++) {
1574 if (tmpBufTileIsBoundary(x, y)) {
1575 tile = tile_manager->get_tile(tmp_map_buf[y * tmp_map_width + x]);
1576
1577 if ((tile->tile_num >= 140 && tile->tile_num <= 187)) { //main U6 wall tiles FIX for WOU games
1578 flag = 0;
1579 original_flag = tile->flags1 & TILEFLAG_WALL_MASK;
1580 } else
1581 continue;
1582
1583 //generate the required wall flags
1584 if (tmpBufTileIsWall(x, y - 1, NUVIE_DIR_N))
1585 flag |= TILEFLAG_WALL_NORTH;
1586 if (tmpBufTileIsWall(x + 1, y, NUVIE_DIR_E))
1587 flag |= TILEFLAG_WALL_EAST;
1588 if (tmpBufTileIsWall(x, y + 1, NUVIE_DIR_S))
1589 flag |= TILEFLAG_WALL_SOUTH;
1590 if (tmpBufTileIsWall(x - 1, y, NUVIE_DIR_W))
1591 flag |= TILEFLAG_WALL_WEST;
1592
1593 //we want to keep existing tile if it is pointing to non-wall tiles which are not blacked
1594 //this is used to support cookfire walls which aren't considered walls in tileflags.
1595
1596 if (tmpBufTileIsBlack(x, y - 1) == false && (original_flag & TILEFLAG_WALL_NORTH))
1597 flag |= TILEFLAG_WALL_NORTH;
1598 if (tmpBufTileIsBlack(x + 1, y) == false && (original_flag & TILEFLAG_WALL_EAST))
1599 flag |= TILEFLAG_WALL_EAST;
1600 if (tmpBufTileIsBlack(x, y + 1) == false && (original_flag & TILEFLAG_WALL_SOUTH))
1601 flag |= TILEFLAG_WALL_SOUTH;
1602 if (tmpBufTileIsBlack(x - 1, y) == false && (original_flag & TILEFLAG_WALL_WEST))
1603 flag |= TILEFLAG_WALL_WEST;
1604
1605 if (flag == 0) //isolated border tiles
1606 continue;
1607
1608 if (flag == 48) { // 0011 top right corner
1609 if (tmpBufTileIsBlack(x, y - 1) && tmpBufTileIsBlack(x + 1, y)) { //replace with blacked corner tile
1610 //Oh dear! this is evil. FIX
1611 tmp_map_buf[y * tmp_map_width + x] = 266 + 2 * (((tile->tile_num - tile->tile_num % 16) - 140) / 16);
1612 continue;
1613 }
1614 }
1615
1616 if (flag == 192) { // 1100 bottom left corner
1617 if (tmpBufTileIsBlack(x, y + 1) && tmpBufTileIsBlack(x - 1, y)) { //replace with blacked corner tile
1618 //Oh dear! this is evil. FIX
1619 tmp_map_buf[y * tmp_map_width + x] = 266 + 1 + 2 * (((tile->tile_num - tile->tile_num % 16) - 140) / 16);
1620 continue;
1621 }
1622 }
1623
1624 if ((tile->flags1 & TILEFLAG_WALL_MASK) == flag) // complete match no work needed
1625 continue;
1626
1627 // Look for a suitable tile to transform into
1628
1629 // ERIC 05/03/05 flag |= TILEFLAG_WALL_NORTH | TILEFLAG_WALL_WEST;
1630
1631 if (((tile->flags1) & TILEFLAG_WALL_MASK) > flag && flag != 144) { // 1001 _| corner
1632 //flag |= TILEFLAG_WALL_NORTH | TILEFLAG_WALL_WEST;
1633 for (; ((tile->flags1) & TILEFLAG_WALL_MASK) != flag && (tile->flags1 & TILEFLAG_WALL_MASK);)
1634 tile = tile_manager->get_tile(tile->tile_num - 1);
1635 } else {
1636 //flag |= TILEFLAG_WALL_NORTH | TILEFLAG_WALL_WEST;
1637 for (; ((tile->flags1) & TILEFLAG_WALL_MASK) != flag && (tile->flags1 & TILEFLAG_WALL_MASK);)
1638 tile = tile_manager->get_tile(tile->tile_num + 1);
1639 }
1640
1641 if ((tile->flags1 & TILEFLAG_WALL_MASK) == flag)
1642 tmp_map_buf[y * tmp_map_width + x] = tile->tile_num;
1643 }
1644 }
1645 }
1646 }
1647
tmpBufTileIsBlack(uint16 x,uint16 y)1648 inline bool MapWindow::tmpBufTileIsBlack(uint16 x, uint16 y) {
1649 if (tmp_map_buf[y * tmp_map_width + x] == 0)
1650 return true;
1651
1652 return false;
1653 }
1654
tmpBufTileIsBoundary(uint16 x,uint16 y)1655 bool MapWindow::tmpBufTileIsBoundary(uint16 x, uint16 y) {
1656 uint16 tile_num;
1657 Tile *tile;
1658
1659 tile_num = tmp_map_buf[y * tmp_map_width + x];
1660
1661 if (tile_num == 0)
1662 return false;
1663
1664 tile = tile_manager->get_tile(tile_num);
1665
1666 if (tile->boundary)
1667 return true;
1668
1669 if (obj_manager->is_boundary(WRAPPED_COORD(cur_x - TMP_MAP_BORDER + x, cur_level), WRAPPED_COORD(cur_y - TMP_MAP_BORDER + y, cur_level), cur_level))
1670 return true;
1671
1672 return false;
1673 }
1674
tmpBufTileIsWall(uint16 x,uint16 y,uint8 direction)1675 bool MapWindow::tmpBufTileIsWall(uint16 x, uint16 y, uint8 direction) {
1676 uint16 tile_num;
1677 Tile *tile;
1678 uint8 mask = 0;
1679
1680 tile_num = tmp_map_buf[y * tmp_map_width + x];
1681
1682 if (tile_num == 0)
1683 return false;
1684
1685 switch (direction) {
1686 case NUVIE_DIR_N :
1687 mask = TILEFLAG_WALL_SOUTH;
1688 break;
1689 case NUVIE_DIR_S :
1690 mask = TILEFLAG_WALL_NORTH;
1691 break;
1692 case NUVIE_DIR_E :
1693 mask = TILEFLAG_WALL_WEST;
1694 break;
1695 case NUVIE_DIR_W :
1696 mask = TILEFLAG_WALL_EAST;
1697 break;
1698 }
1699
1700 tile = tile_manager->get_tile(tile_num);
1701
1702 if (tile->flags1 & TILEFLAG_WALL) {
1703 if (tile->flags1 & mask)
1704 return true;
1705 }
1706
1707 // if(obj_manager->is_boundary(cur_x-1+x, cur_y-1+y, cur_level))
1708 // return true;
1709
1710 tile = obj_manager->get_obj_tile(WRAPPED_COORD(cur_x - TMP_MAP_BORDER + x, cur_level), WRAPPED_COORD(cur_y - TMP_MAP_BORDER + y, cur_level), cur_level, false);
1711 if (tile != NULL) {
1712 if (tile->flags2 & TILEFLAG_BOUNDARY) {
1713 if (tile->flags1 & mask)
1714 return true;
1715 }
1716 }
1717
1718 return false;
1719 }
1720
1721 /* Returns MSG_SUCCESS if the obj can be dropped or moved by the actor at world coordinates x,y or an error msg.
1722 * There must be a direct path between the actor to the obj and also between the obj and destination.
1723 * Objs that can't be picked up will be blocked actors and unpassable areas. (z is always cur_level)
1724 */
can_drop_or_move_obj(uint16 x,uint16 y,Actor * actor,Obj * obj)1725 CanDropOrMoveMsg MapWindow::can_drop_or_move_obj(uint16 x, uint16 y, Actor *actor, Obj *obj) {
1726 bool in_inventory = obj->is_in_inventory();
1727 if (!in_inventory && obj->x == x && obj->y == y)
1728 return MSG_NOT_POSSIBLE;
1729 if (game->using_hackmove())
1730 return MSG_SUCCESS;
1731
1732 if (tile_is_black(x, y, obj)) {
1733 if (tile_is_black(x, y))
1734 return MSG_NOT_POSSIBLE;
1735 else
1736 return MSG_BLOCKED;
1737 }
1738 MapCoord actor_loc = actor->get_location();
1739 if (actor_manager->get_actor(x, y, actor_loc.z))
1740 return MSG_NOT_POSSIBLE;
1741
1742 Obj *dest_obj = NULL;
1743 if (game_type == NUVIE_GAME_U6) {
1744 dest_obj = obj_manager->get_obj(x, y, actor_loc.z); //FIXME this might not be right. We might want to exclude obj.
1745 } else {
1746 dest_obj = obj_manager->get_obj(x, y, actor_loc.z, OBJ_SEARCH_TOP, OBJ_EXCLUDE_IGNORED, obj);
1747 }
1748
1749 bool can_go_in_water = (game_type == NUVIE_GAME_U6
1750 && (obj->obj_n == OBJ_U6_SKIFF || obj->obj_n == OBJ_U6_RAFT));
1751 if (can_go_in_water && dest_obj) // it is drawn underneath so only allow on hackmove
1752 return MSG_NOT_POSSIBLE;
1753
1754 LineTestResult lt;
1755 MapCoord target_loc(x, y, actor_loc.z);
1756 MapCoord obj_loc(obj->x, obj->y, actor_loc.z);
1757
1758 if (in_inventory && !obj->get_actor_holding_obj()->is_onscreen()
1759 && obj->get_actor_holding_obj()->get_location().distance(target_loc) > 5)
1760 return MSG_OUT_OF_RANGE;
1761
1762 if (get_interface() == INTERFACE_IGNORE_BLOCK && map->can_put_obj(x, y, cur_level))
1763 return MSG_SUCCESS;
1764
1765 if (actor_loc.distance(target_loc) > 5 && get_interface() == INTERFACE_NORMAL)
1766 return MSG_OUT_OF_RANGE;
1767
1768 uint8 lt_flags;
1769 lt_flags = (game_type == NUVIE_GAME_U6) ? LT_HitMissileBoundary : 0; //FIXME this probably isn't quite right for MD/SE
1770 if (map->lineTest(actor_loc.x, actor_loc.y, x, y, actor_loc.z, lt_flags, lt, 0, obj)) {
1771 MapCoord hit_loc = MapCoord(lt.hit_x, lt.hit_y, lt.hit_level);
1772 if (obj_loc.distance(target_loc) != 1 || hit_loc.distance(target_loc) != 1) {
1773 if (lt.hitObj && target_loc == hit_loc) {
1774 if (obj_manager->can_store_obj(lt.hitObj, obj)) //if we are moving onto a container.
1775 return MSG_SUCCESS;
1776 }
1777 return MSG_BLOCKED;
1778 }
1779 // trying to push object one tile away from actor
1780 if (map->lineTest(obj->x, obj->y, x, y, actor_loc.z, lt_flags, lt, 0, obj)) {
1781 if (lt.hitObj) {
1782 if (obj_manager->can_store_obj(lt.hitObj, obj)) //if we are moving onto a container.
1783 return MSG_SUCCESS;
1784 /* else // I don't think these are needed
1785 {
1786 // We can place an object on a bench or table. Or on any other object if
1787 // the object is passable and not on a boundary.
1788
1789 Tile *obj_tile = obj_manager->get_obj_tile(lt.hitObj->obj_n, lt.hitObj->frame_n);
1790 if(!obj_tile) // shouldn't happen
1791 return MSG_NO_TILE;
1792 if((obj_tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP)
1793 || (obj_tile->passable && !map->is_boundary(lt.hit_x, lt.hit_y, lt.hit_level)) )
1794 return MSG_SUCCESS;
1795 }*/
1796 }
1797 return MSG_BLOCKED;
1798 }
1799 }
1800 Tile *tile;
1801 if (dest_obj)
1802 tile = obj_manager->get_obj_tile(dest_obj->obj_n, dest_obj->frame_n);
1803 else
1804 tile = map->get_tile(x, y, actor_loc.z);
1805
1806 if (!tile) // shouldn't happen
1807 return MSG_NO_TILE;
1808
1809 if ((can_go_in_water || !map->is_water(x, y, actor_loc.z))
1810 && ((tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP)
1811 || (tile->passable && !map->is_boundary(x, y, actor_loc.z))))
1812 return MSG_SUCCESS;
1813
1814 return MSG_NOT_POSSIBLE;
1815 }
1816
display_can_drop_or_move_msg(CanDropOrMoveMsg msg,string msg_text)1817 void MapWindow::display_can_drop_or_move_msg(CanDropOrMoveMsg msg, string msg_text) {
1818 if (msg == MSG_NOT_POSSIBLE)
1819 msg_text += "Not possible\n";
1820 else if (msg == MSG_BLOCKED)
1821 msg_text += "Blocked\n";
1822 else if (msg == MSG_OUT_OF_RANGE)
1823 msg_text += "Out of range\n";
1824 /* else if(msg == MSG_NO_TILE) // shouldn't be needed now that blacked out areas are checked first
1825 msg_text += "ERROR: No tile. Report me\n";*/
1826 game->get_scroll()->display_string(msg_text);
1827 }
1828
can_get_obj(Actor * actor,Obj * obj)1829 bool MapWindow::can_get_obj(Actor *actor, Obj *obj) {
1830 if (!obj)
1831 return false;
1832 if (get_interface() == INTERFACE_IGNORE_BLOCK)
1833 return true;
1834 if (obj->is_in_inventory())
1835 return false;
1836 if (obj->is_in_container())
1837 obj = obj->get_container_obj(true);
1838
1839 if (actor->get_z() != obj->z)
1840 return false;
1841
1842 LineTestResult lt;
1843 if (map->lineTest(actor->get_x(), actor->get_y(), obj->x, obj->y, obj->z, LT_HitUnpassable, lt, 0, obj)) {
1844 // Skip Check for SE Tile Objects - We are actually using the blocking item/tree
1845 Script *script = game->get_script();
1846 if (game_type != NUVIE_GAME_SE || !script->call_is_tile_object(obj->obj_n)) {
1847 return false;
1848 }
1849 }
1850
1851 if (game_type == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_SECRET_DOOR)
1852 return true;
1853 return !blocked_by_wall(actor, obj);
1854 }
1855
1856 /*
1857 * Check to make sure the obj isn't on the other side of a wall or trying to push through it.
1858 * The original engine didn't bother to check.
1859 */
blocked_by_wall(Actor * actor,Obj * obj)1860 bool MapWindow::blocked_by_wall(Actor *actor, Obj *obj) {
1861 if (game_type == NUVIE_GAME_U6 && obj->x == 282 && obj->y == 438 && cur_level == 0) // HACK for buggy location
1862 return false;
1863 Tile *tile = map->get_tile(obj->x, obj->y, cur_level);
1864 if (((tile->flags1 & TILEFLAG_WALL) && !game->get_usecode()->is_door(obj))
1865 && (((tile->flags1 & TILEFLAG_WALL_MASK) == 208 && actor->get_y() < obj->y) // can't get items that are south
1866 || ((tile->flags1 & TILEFLAG_WALL_MASK) == 176 && actor->get_x() < obj->x) // can't get items that are east
1867 || ((tile->flags1 & TILEFLAG_WALL_MASK) == 240 // northwest corner - used in SE (not sure if used in other games)
1868 && (actor->get_y() < obj->y || actor->get_x() < obj->x))))
1869 return true;
1870
1871 return false;
1872 }
1873
drag_accept_drop(int x,int y,int message,void * data)1874 bool MapWindow::drag_accept_drop(int x, int y, int message, void *data) {
1875 DEBUG(0, LEVEL_DEBUGGING, "MapWindow::drag_accept_drop()\n");
1876 uint16 mapWidth;
1877
1878 x -= area.left;
1879 y -= area.top;
1880
1881 x /= 16;
1882 y /= 16;
1883
1884 GUI::get_gui()->force_full_redraw();
1885
1886 if (message == GUI_DRAG_OBJ) {
1887 if (game->get_player()->is_in_vehicle() && !game->using_hackmove()) {
1888 game->get_event()->display_not_aboard_vehicle();
1889 return false;
1890 }
1891 mapWidth = map->get_width(cur_level);
1892 x = (cur_x + x) % mapWidth;
1893 y = (cur_y + y) % mapWidth;
1894
1895 Obj *obj = (Obj *)data;
1896 Actor *p = actor_manager->get_player();
1897 Actor *target_actor = map->get_actor(x, y, cur_level);
1898
1899 if (obj->is_in_inventory() == false) { //obj on map.
1900 if (can_get_obj(p, obj)) { //make sure there is a clear line from player to object
1901 if (target_actor) {
1902 game->get_event()->display_move_text(target_actor, obj);
1903
1904 if (target_actor == p || (target_actor->is_in_party())) {
1905 if (obj_manager->obj_is_damaging(obj, p)) {
1906 game->get_player()->subtract_movement_points(3);
1907 return false;
1908 }
1909 if ((!game->get_usecode()->has_getcode(obj) || game->get_usecode()->get_obj(obj, target_actor))
1910 && game->get_event()->can_move_obj_between_actors(obj, p, target_actor))
1911 return true;
1912 else {
1913 game->get_scroll()->message("\n\n");
1914 return false;
1915 }
1916 } else {
1917 game->get_scroll()->display_string("\n\nOnly within the party!");
1918 game->get_scroll()->message("\n\n");
1919 return false;
1920 }
1921 } else
1922 return true;
1923 }
1924 } else {
1925 if (game->get_usecode()->cannot_unready(obj)) {
1926 game->get_event()->unready(obj);
1927 return false;
1928 }
1929 if (target_actor) {
1930 Actor *owner = obj->get_actor_holding_obj();
1931 game->get_event()->display_move_text(target_actor, obj);
1932
1933 if (game->get_event()->can_move_obj_between_actors(obj, owner, target_actor) == false) {
1934 game->get_scroll()->message("\n\n");
1935 return false;
1936 } else
1937 return true;
1938 } else
1939 return true; //throw on ground
1940 }
1941 game->get_scroll()->display_string("Move-");
1942 game->get_scroll()->display_string(obj_manager->look_obj(obj)); // getting obj name
1943 game->get_scroll()->display_string("\nto ");
1944 game->get_scroll()->display_string(get_direction_name(x - obj->x , y - obj->y));
1945 game->get_scroll()->message(".\n\nCan't reach it\n\n");
1946 }
1947
1948 return false;
1949 }
1950
drag_perform_drop(int x,int y,int message,void * data)1951 void MapWindow::drag_perform_drop(int x, int y, int message, void *data) {
1952 DEBUG(0, LEVEL_DEBUGGING, "MapWindow::drag_perform_drop()\n");
1953 Events *event = game->get_event();
1954 uint16 mapWidth = map->get_width(cur_level);
1955
1956 x -= area.left;
1957 y -= area.top;
1958
1959 if (message == GUI_DRAG_OBJ) {
1960 x = (cur_x + x / 16) % mapWidth;
1961 y = (cur_y + y / 16) % mapWidth;
1962 Obj *obj = (Obj *)data;
1963
1964 if (obj->obj_n == OBJ_U6_LOCK_PICK && game_type == NUVIE_GAME_U6)
1965 game->get_usecode()->search_container(obj, false);
1966 Actor *a = map->get_actor(x, y, cur_level);
1967 if (a && (a->is_in_party() || a == actor_manager->get_player())) {
1968 if (a == actor_manager->get_player()) // get
1969 game->get_player()->subtract_movement_points(3);
1970 else // get plus move
1971 game->get_player()->subtract_movement_points(8);
1972 obj_manager->moveto_inventory(obj, a);
1973 game->get_scroll()->message("\n\n");
1974 } else {
1975 if (!obj->is_in_inventory() && !obj->is_in_container()) { // !need is_in_container to exclude container gumps
1976 move_on_drop(obj); // no longer determines whether to drop or move but can call usecode
1977 event->newAction(PUSH_MODE);
1978 event->select_obj(obj);
1979 event->pushTo(x - obj->x, y - obj->y, PUSH_FROM_OBJECT);
1980 event->endAction();
1981 return;
1982 }
1983 CanDropOrMoveMsg can_drop; // so we can skip quantity prompt
1984 if ((can_drop = can_drop_or_move_obj(x, y, actor_manager->get_player(), obj)) != MSG_SUCCESS) {
1985 game->get_scroll()->display_string("Drop-");
1986 game->get_scroll()->display_string(obj_manager->look_obj(obj));
1987 game->get_scroll()->display_string("\n\nlocation:\n\n");
1988 display_can_drop_or_move_msg(can_drop, "");
1989 game->get_scroll()->message("\n");
1990 return;
1991 }
1992 // drop on ground or into a container
1993 event->newAction(DROP_MODE);
1994 event->select_obj(obj);
1995 if (obj->qty <= 1 || !obj_manager->is_stackable(obj))
1996 event->select_target(x, y);
1997 else
1998 event->set_drop_target(x, y); // pre-select target
1999 }
2000 }
2001
2002 }
2003 // performs some usecode but used to decide whether to move or drop too
move_on_drop(Obj * obj)2004 bool MapWindow::move_on_drop(Obj *obj) {
2005 bool move = (get_interface() == INTERFACE_NORMAL);
2006 /* if(drop_with_move && move)
2007 return true;
2008
2009 if(obj_manager->obj_is_damaging(obj))
2010 return move;
2011 */
2012 if (game->get_usecode()->has_getcode(obj) && obj->is_in_inventory() == false) {
2013 if (game_type == NUVIE_GAME_U6) {
2014 switch (obj->obj_n) {
2015 case OBJ_U6_CHEST:
2016 case OBJ_U6_LOCK_PICK:
2017 case OBJ_U6_MOONSTONE:
2018 game->get_usecode()->get_obj(obj, actor_manager->get_player());
2019 return false;
2020 case OBJ_U6_SKIFF:
2021 return false;
2022 case OBJ_U6_TORCH:
2023 if (obj->frame_n == 0)
2024 return false;
2025 break;
2026 default :
2027 break;
2028 }
2029 }
2030
2031 return move;
2032 }
2033
2034 return false;
2035 }
2036
set_interface()2037 void MapWindow::set_interface() {
2038 Std::string interface_str;
2039 config->value("config/input/interface", interface_str, "normal");
2040 if (interface_str == "ignore_block" || Game::get_game()->using_hackmove()) // game variable is not initialized
2041 interface = INTERFACE_IGNORE_BLOCK;
2042 else if (interface_str == "fullscreen")
2043 interface = INTERFACE_FULLSCREEN;
2044 else
2045 interface = INTERFACE_NORMAL;
2046 }
2047
get_interface()2048 InterfaceType MapWindow::get_interface() {
2049 // check is easily exploited but would be annoying if checking for nearby enemies
2050 if (interface == INTERFACE_FULLSCREEN && game->get_party()->is_in_combat_mode())
2051 return INTERFACE_NORMAL;
2052 return interface;
2053 }
2054
is_interface_fullscreen_in_combat()2055 bool MapWindow::is_interface_fullscreen_in_combat() {
2056 if (interface == INTERFACE_FULLSCREEN && game->get_party()->is_in_combat_mode())
2057 return true;
2058 return false;
2059 }
2060
Idle(void)2061 GUI_status MapWindow::Idle(void) {
2062 return (GUI_Widget::Idle());
2063 }
2064
2065
2066 // single-click (press and release button)
MouseClick(int x,int y,Shared::MouseButton button)2067 GUI_status MapWindow::MouseClick(int x, int y, Shared::MouseButton button) {
2068 if (button == USE_BUTTON && look_on_left_click) {
2069 wait_for_mouseclick(button); // see MouseDelayed
2070 }
2071 return (MouseUp(x, y, button)); // do MouseUp so selected_obj is cleared
2072 }
2073
2074 // single-click; waited for double-click
MouseDelayed(int x,int y,Shared::MouseButton button)2075 GUI_status MapWindow::MouseDelayed(int x, int y, Shared::MouseButton button) {
2076 Events *event = game->get_event();
2077 if (!looking || game->user_paused() || event->cursor_mode
2078 || (event->get_mode() != MOVE_MODE && event->get_mode() != EQUIP_MODE)) {
2079 look_obj = NULL;
2080 look_actor = NULL;
2081 return (GUI_PASS);
2082 }
2083 game->get_scroll()->display_string("Look-");
2084 event->set_mode(LOOK_MODE);
2085 event->lookAtCursor(true, original_obj_loc.x, original_obj_loc.y, original_obj_loc.z, look_obj, look_actor);
2086 look_obj = NULL;
2087 look_actor = NULL;
2088
2089 return (MouseUp(x, y, button)); // do MouseUp so selected_obj is cleared
2090 }
2091
2092 // MouseDown; waited for MouseUp
MouseHeld(int x,int y,Shared::MouseButton button)2093 GUI_status MapWindow::MouseHeld(int x, int y, Shared::MouseButton button) {
2094 looking = false;
2095 if (walk_with_left_button)
2096 set_walking(true);
2097 return (GUI_PASS);
2098 }
2099
2100 // double-click
MouseDouble(int x,int y,Shared::MouseButton button)2101 GUI_status MapWindow::MouseDouble(int x, int y, Shared::MouseButton button) {
2102 Events *event = game->get_event();
2103
2104 // only USE if not doing anything in event
2105 if (enable_doubleclick && event->get_mode() == MOVE_MODE && !is_wizard_eye_mode()) {
2106 int wx, wy;
2107 mouseToWorldCoords(x, y, wx, wy);
2108 event->multiuse((uint16)wx, (uint16)wy);
2109 }
2110 looking = false;
2111 return (MouseUp(x, y, button)); // do MouseUp so selected_obj is cleared
2112 }
2113
MouseWheel(sint32 x,sint32 y)2114 GUI_status MapWindow::MouseWheel(sint32 x, sint32 y) {
2115 Game *g = Game::get_game();
2116
2117 if (g->is_new_style()) {
2118 if (y > 0)
2119 g->get_scroll()->move_scroll_up();
2120 if (y < 0)
2121 g->get_scroll()->move_scroll_down();
2122 } else {
2123 if (y > 0)
2124 g->get_scroll()->page_up();
2125 if (y < 0)
2126 g->get_scroll()->page_down();
2127 }
2128 return GUI_YUM;
2129 }
2130
MouseDown(int x,int y,Shared::MouseButton button)2131 GUI_status MapWindow::MouseDown(int x, int y, Shared::MouseButton button) {
2132 //DEBUG(0,LEVEL_DEBUGGING,"MapWindow::MouseDown, button = %i\n", button);
2133 Events *event = game->get_event();
2134 Actor *player = actor_manager->get_player();
2135 Obj *obj = get_objAtMousePos(x, y);
2136
2137 if (is_wizard_eye_mode()) {
2138 set_walking(true);
2139 return GUI_YUM;
2140 }
2141 if (event->is_looking_at_spellbook()) {
2142 event->cancelAction();
2143 return GUI_YUM;
2144 }
2145
2146 if (game->is_original_plus() && y <= Game::get_game()->get_game_y_offset() + 200
2147 && x >= Game::get_game()->get_game_x_offset() + game->get_game_width() - border_width) {
2148 looking = false;
2149 return GUI_PASS;
2150 }
2151 if (event->get_mode() == MOVE_MODE || event->get_mode() == EQUIP_MODE) {
2152 int wx, wy;
2153 mouseToWorldCoords(x, y, wx, wy);
2154
2155 if (button == WALK_BUTTON && game->get_command_bar()->get_selected_action() != -1) {
2156 if (game->get_command_bar()->try_selected_action() == false) // start new action
2157 return GUI_PASS; // false if new event doesn't need target
2158 } else if (wx == player->x && wy == player->y // PASS if Avatar is hit
2159 && (button == WALK_BUTTON || !enable_doubleclick)) {
2160 event->cancelAction(); // MOVE_MODE, so this should work
2161 return GUI_PASS;
2162 } else if (button == WALK_BUTTON
2163 || (!enable_doubleclick && button == USE_BUTTON
2164 && !game->is_dragging_enabled() && !look_on_left_click)) {
2165 set_walking(true);
2166 } else if (button == USE_BUTTON) { // you can also walk by holding the USE button
2167 if (look_on_left_click && !event->cursor_mode) { // need to preserve location because of click delay
2168 looking = true;
2169 original_obj_loc = MapCoord(wx, wy , cur_level);
2170 look_actor = actor_manager->get_actor(wx , wy, cur_level);
2171 look_obj = obj_manager->get_obj(wx , wy, cur_level);
2172 moveCursor(WRAP_VIEWP(cur_x, wx, map_width), wy - cur_y);
2173 }
2174 wait_for_mousedown(button);
2175 }
2176 }
2177
2178 if (event->get_mode() == INPUT_MODE || event->get_mode() == ATTACK_MODE) { // finish whatever action is being done, with mouse coordinates
2179 if (button != USE_BUTTON && button != WALK_BUTTON)
2180 return GUI_PASS;
2181 looking = false;
2182 select_target(x, y);
2183 return GUI_PASS;
2184 } else if (event->get_mode() != MOVE_MODE && event->get_mode() != EQUIP_MODE) {
2185 return GUI_PASS;
2186 }
2187
2188 if (!obj || button != DRAG_BUTTON)
2189 return GUI_PASS;
2190
2191 original_obj_loc = MapCoord(obj->x, obj->y, obj->z);
2192 int distance = player->get_location().distance(original_obj_loc);
2193 float weight = obj_manager->get_obj_weight(obj, OBJ_WEIGHT_EXCLUDE_CONTAINER_ITEMS);
2194
2195 if ((weight == 0 || player->get_actor_num() == 0
2196 || tile_is_black(obj->x, obj->y, obj)) && !game->using_hackmove())
2197 return GUI_PASS;
2198
2199 // checking interface directly to allow dragging in INTERFACE_FULLSCREEN when in combat
2200 if (distance > 1 && interface == INTERFACE_NORMAL)
2201 return GUI_PASS;
2202
2203 if (button == DRAG_BUTTON && game->is_dragging_enabled())
2204 selected_obj = obj;
2205
2206 return GUI_PASS;
2207 }
2208
MouseUp(int x,int y,Shared::MouseButton button)2209 GUI_status MapWindow::MouseUp(int x, int y, Shared::MouseButton button) {
2210 // cancel dragging and movement no matter what button is released
2211 if (selected_obj) {
2212 selected_obj = NULL;
2213 }
2214 walking = false;
2215 dragging = false;
2216
2217 return GUI_PASS;
2218 }
2219
MouseMotion(int x,int y,uint8 state)2220 GUI_status MapWindow::MouseMotion(int x, int y, uint8 state) {
2221 // Events *event = game->get_event();
2222 Tile *tile;
2223
2224 update_mouse_cursor((uint32)x, (uint32)y);
2225
2226 // DEBUG(0,LEVEL_DEBUGGING,"MapWindow::MouseMotion\n");
2227
2228 // if(selected_obj) // We don't want to walk if we are selecting an object to move.
2229 // walking = false;
2230 if (walking) { // No, we don't want to select an object to move if we are walking.
2231 selected_obj = NULL;
2232 dragging = false;
2233 }
2234
2235 if (selected_obj && !dragging) {
2236 int wx, wy;
2237 // ensure that the player can reach the selected object before
2238 // letting them drag it
2239 //mouseToWorldCoords(x, y, wx, wy);
2240 wx = selected_obj->x;
2241 wy = selected_obj->y;
2242 LineTestResult result;
2243 Actor *player = actor_manager->get_player();
2244
2245 if (map->lineTest(player->x, player->y, wx, wy, cur_level, LT_HitUnpassable, result)
2246 && !(result.hitObj && result.hitObj->x == wx && result.hitObj->y == wy)
2247 && get_interface() == INTERFACE_NORMAL)
2248 // something was in the way, so don't allow a drag
2249 return GUI_PASS;
2250 dragging = true;
2251 set_mousedown(0, DRAG_BUTTON); // cancel MouseHeld
2252 game->set_mouse_pointer(0); // arrow
2253 tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(selected_obj->obj_n) + selected_obj->frame_n);
2254 bool out_of_range;
2255 if (is_interface_fullscreen_in_combat() && player->get_location().distance(original_obj_loc) > 1)
2256 out_of_range = true;
2257 else
2258 out_of_range = false;
2259 return gui_drag_manager->start_drag(this, GUI_DRAG_OBJ, selected_obj, tile->data, 16, 16, 8, out_of_range);
2260 }
2261
2262 return GUI_PASS;
2263 }
2264
drag_drop_success(int x,int y,int message,void * data)2265 void MapWindow::drag_drop_success(int x, int y, int message, void *data) {
2266 //DEBUG(0,LEVEL_DEBUGGING,"MapWindow::drag_drop_success\n");
2267 dragging = false;
2268
2269 // handled by drop target
2270 // if (selected_obj)
2271 // obj_manager->remove_obj (selected_obj);
2272
2273 selected_obj = NULL;
2274 Redraw();
2275 }
2276
drag_drop_failed(int x,int y,int message,void * data)2277 void MapWindow::drag_drop_failed(int x, int y, int message, void *data) {
2278 DEBUG(0, LEVEL_DEBUGGING, "MapWindow::drag_drop_failed\n");
2279 dragging = false;
2280 selected_obj = NULL;
2281 }
2282
2283 // this does nothing
KeyDown(const Common::KeyState & key)2284 GUI_status MapWindow::KeyDown(const Common::KeyState &key) {
2285 if (is_wizard_eye_mode()) {
2286 KeyBinder *keybinder = Game::get_game()->get_keybinder();
2287 ActionType a = keybinder->get_ActionType(key);
2288 switch (keybinder->GetActionKeyType(a)) {
2289 case WEST_KEY:
2290 moveMapRelative(-1, 0);
2291 break;
2292 case EAST_KEY:
2293 moveMapRelative(1, 0);
2294 break;
2295 case SOUTH_KEY:
2296 moveMapRelative(0, 1);
2297 break;
2298 case NORTH_KEY:
2299 moveMapRelative(0, -1);
2300 break;
2301 case NORTH_EAST_KEY:
2302 moveMapRelative(1, -1);
2303 break;
2304 case SOUTH_EAST_KEY:
2305 moveMapRelative(1, 1);
2306 break;
2307 case NORTH_WEST_KEY:
2308 moveMapRelative(-1, -1);
2309 break;
2310 case SOUTH_WEST_KEY:
2311 moveMapRelative(-1, 1);
2312 break;
2313 case CANCEL_ACTION_KEY:
2314 wizard_eye_stop();
2315 break;
2316 default:
2317 keybinder->handle_always_available_keys(a);
2318 return GUI_YUM;
2319 }
2320 if (keybinder->GetActionKeyType(a) <= SOUTH_WEST_KEY)
2321 wizard_eye_update();
2322 return GUI_YUM;
2323 }
2324 return GUI_PASS;
2325 }
2326
get_objAtMousePos(int mx,int my)2327 Obj *MapWindow::get_objAtMousePos(int mx, int my) {
2328 int wx, wy;
2329 mouseToWorldCoords(mx, my, wx, wy);
2330
2331 return obj_manager->get_obj(wx, wy, cur_level);
2332 }
2333
2334
get_actorAtMousePos(int mx,int my)2335 Actor *MapWindow::get_actorAtMousePos(int mx, int my) {
2336 int wx, wy;
2337 mouseToWorldCoords(mx, my, wx, wy);
2338
2339 return actor_manager->get_actor(wx, wy, cur_level);
2340 }
2341
teleport_to_cursor()2342 void MapWindow::teleport_to_cursor() {
2343 int mx, my, wx, wy;
2344 screen->get_mouse_location(&mx, &my);
2345
2346 mouseToWorldCoords(mx, my, wx, wy);
2347 game->get_player()->move(wx, wy, cur_level, true);
2348 }
2349
select_target(int x,int y)2350 void MapWindow::select_target(int x, int y) {
2351 int wx, wy;
2352 mouseToWorldCoords(x, y, wx, wy);
2353 moveCursor(WRAP_VIEWP(cur_x, wx, map_width), wy - cur_y);
2354 game->get_event()->select_target(uint16(wx), uint16(wy), cur_level);
2355 }
2356
mouseToWorldCoords(int mx,int my,int & wx,int & wy)2357 void MapWindow::mouseToWorldCoords(int mx, int my, int &wx, int &wy) {
2358 int x = mx - area.left;
2359 int y = my - area.top;
2360
2361 int mapWidth = map->get_width(cur_level);
2362
2363 wx = (cur_x + x / 16) % mapWidth;
2364 wy = (cur_y + y / 16) % mapWidth;
2365 }
2366
drag_draw(int x,int y,int message,void * data)2367 void MapWindow::drag_draw(int x, int y, int message, void *data) {
2368 Tile *tile;
2369
2370 if (!selected_obj)
2371 return;
2372
2373 tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(selected_obj) + selected_obj->frame_n);
2374
2375 int nx = x - 8;
2376 int ny = y - 8;
2377
2378 if (nx + 16 >= screen->get_width())
2379 nx = screen->get_width() - 17;
2380 else if (nx < 0)
2381 nx = 0;
2382
2383 if (ny + 16 >= screen->get_height())
2384 ny = screen->get_height() - 17;
2385 else if (ny < 0)
2386 ny = 0;
2387
2388 screen->blit(nx, ny, tile->data, 8, 16, 16, 16, true);
2389 screen->update(nx, ny, 16, 16);
2390 }
2391
2392
2393 /* Display MapWindow animations. */
drawAnims(bool top_anims)2394 void MapWindow::drawAnims(bool top_anims) {
2395 if (!screen) // screen should be set early on
2396 return;
2397 else if (!anim_manager->get_surface()) // screen must be assigned to AnimManager
2398 anim_manager->set_surface(screen);
2399 anim_manager->display(top_anims);
2400 }
2401
2402
2403 /* Set mouse pointer to a movement-arrow for walking, or a crosshair. */
update_mouse_cursor(uint32 mx,uint32 my)2404 void MapWindow::update_mouse_cursor(uint32 mx, uint32 my) {
2405 Events *event = game->get_event();
2406 int wx = 0, wy = 0;
2407 sint16 rel_x, rel_y;
2408 uint8 mptr; // mouse-pointer is set here in get_movement_direction()
2409
2410 if (event->get_mode() != MOVE_MODE && event->get_mode() != INPUT_MODE)
2411 return;
2412
2413 // MousePos->WorldCoord->Direction&MousePointer
2414 if (game->is_orig_style())
2415 mouseToWorldCoords((int)mx, (int)my, wx, wy);
2416 get_movement_direction((uint16)mx, (uint16)my, rel_x, rel_y, &mptr);
2417 if (event->get_mode() == INPUT_MODE && mousecenter_x == (win_width / 2) && mousecenter_y == (win_height / 2)
2418 && !event->dont_show_target_cursor())
2419 game->set_mouse_pointer(1); // crosshairs
2420 else if (dragging || (game->is_orig_style() && (wx == cur_x || wy == cur_y || wx == WRAP_VIEWP(cur_x, win_width - 1, map_width) || wy == (cur_y + win_height - 1)))
2421 || (game->is_original_plus() && (my <= (uint32)Game::get_game()->get_game_y_offset() + 200 || game->is_original_plus_cutoff_map())
2422 && mx >= (uint32)Game::get_game()->get_game_x_offset() + game->get_game_width() - border_width))
2423 game->set_mouse_pointer(0); // arrow
2424 else
2425 game->set_mouse_pointer(mptr); // 1=crosshairs, 2to9=arrows
2426 }
2427
2428 /* Get relative movement direction from the MouseCenter coordinates to the
2429 * mouse coordinates mx,my, for walking with the mouse, etc. The mouse-pointer
2430 * number that should be used for that direction will be set to mptr.
2431 */
get_movement_direction(uint16 mx,uint16 my,sint16 & rel_x,sint16 & rel_y,uint8 * mptr)2432 void MapWindow::get_movement_direction(uint16 mx, uint16 my, sint16 &rel_x, sint16 &rel_y, uint8 *mptr) {
2433 uint16 cent_x = mousecenter_x,
2434 cent_y = mousecenter_y;
2435 if (game->is_original_plus_full_map() && game->get_event()->get_mode() != INPUT_MODE)
2436 cent_x -= (map_center_xoff + 1) / 2; // player is off center
2437
2438 mx = (mx - area.left) / 16;
2439 my = (my - area.top) / 16;
2440 uint16 dist_x = abs(mx - cent_x), dist_y = abs(my - cent_y);
2441
2442 rel_x = rel_y = 0;
2443 if (dist_x <= 4 && dist_y <= 4) {
2444 // use mapwindow coords (4,4 is center of mapwindow)
2445 uint8 cursor_num = movement_array[(9 * (4 + (my - cent_y))) + (4 + (mx - cent_x))];
2446 if (mptr) // set mouse-pointer number
2447 *mptr = cursor_num;
2448 if (cursor_num == 1) // nowhere
2449 return;
2450 if (cursor_num == 2) // up
2451 rel_y = -1;
2452 else if (cursor_num == 6) // down
2453 rel_y = 1;
2454 else if (cursor_num == 8) // left
2455 rel_x = -1;
2456 else if (cursor_num == 4) // right
2457 rel_x = 1;
2458 else if (cursor_num == 3) { // up-right
2459 rel_x = 1;
2460 rel_y = -1;
2461 } else if (cursor_num == 5) { // down-right
2462 rel_x = 1;
2463 rel_y = 1;
2464 } else if (cursor_num == 7) { // down-left
2465 rel_x = -1;
2466 rel_y = 1;
2467 } else if (cursor_num == 9) { // up-left
2468 rel_x = -1;
2469 rel_y = -1;
2470 }
2471 } else { // mapwindow is larger than the array; use 4 squares around center array
2472 if (dist_x <= 4 && my < cent_y) { // up
2473 rel_y = -1;
2474 if (mptr) *mptr = 2;
2475 } else if (dist_x <= 4 && my > cent_y) { // down
2476 rel_y = 1;
2477 if (mptr) *mptr = 6;
2478 } else if (mx < cent_x && dist_y <= 4) { // left
2479 rel_x = -1;
2480 if (mptr) *mptr = 8;
2481 } else if (mx > cent_x && dist_y <= 4) { // right
2482 rel_x = 1;
2483 if (mptr) *mptr = 4;
2484 } else if (mx > cent_x && my < cent_y) { // up-right
2485 rel_x = 1;
2486 rel_y = -1;
2487 if (mptr) *mptr = 3;
2488 } else if (mx > cent_x && my > cent_y) { // down-right
2489 rel_x = 1;
2490 rel_y = 1;
2491 if (mptr) *mptr = 5;
2492 } else if (mx < cent_x && my > cent_y) { // down-left
2493 rel_x = -1;
2494 rel_y = 1;
2495 if (mptr) *mptr = 7;
2496 } else if (mx < cent_x && my < cent_y) { // up-left
2497 rel_x = -1;
2498 rel_y = -1;
2499 if (mptr) *mptr = 9;
2500 }
2501 }
2502 }
2503
2504
2505 /* Revert mouse cursor to normal arrow. Stop walking. */
MouseLeave(uint8 state)2506 GUI_status MapWindow::MouseLeave(uint8 state) {
2507 if (game_type == NUVIE_GAME_MD) // magnifying glass - pointer 0 should be used too for some areas
2508 game->set_mouse_pointer(1);
2509 else
2510 game->set_mouse_pointer(0);
2511 walking = false;
2512 dragging = false;
2513 // NOTE: Don't clear selected_obj here! It's used to remove the object after
2514 // dragging.
2515 return (GUI_PASS);
2516 }
2517
make_thumbnail()2518 byte *MapWindow::make_thumbnail() {
2519 if (thumbnail)
2520 return NULL;
2521
2522 new_thumbnail = true;
2523
2524 GUI::get_gui()->Display(); // this calls MapWindow::display() which in turn calls create_thumbnail(). :-)
2525
2526 return thumbnail;
2527 }
2528
create_thumbnail()2529 void MapWindow::create_thumbnail() {
2530 Common::Rect src_rect;
2531
2532 src_rect.setWidth(MAPWINDOW_THUMBNAIL_SIZE * MAPWINDOW_THUMBNAIL_SCALE);
2533 src_rect.setHeight(src_rect.width());
2534
2535 src_rect.left = area.left + win_width * 8 - (src_rect.width() / 2); // area.left + (win_width * 16) / 2 - 120 / 2
2536 src_rect.top = area.top + win_height * 8 - (src_rect.height() / 2); // area.top + (win_height * 16) / 2 - 120 / 2
2537
2538 thumbnail = screen->copy_area(&src_rect, MAPWINDOW_THUMBNAIL_SCALE); //scale down x3
2539
2540 new_thumbnail = false;
2541 }
2542
free_thumbnail()2543 void MapWindow::free_thumbnail() {
2544 if (thumbnail) {
2545 delete[] thumbnail;
2546 thumbnail = NULL;
2547 }
2548
2549 return;
2550 }
2551
2552
2553 /* Returns a new 8bit copy of the mapwindow as displayed. Caller must free it. */
get_sdl_surface()2554 Graphics::ManagedSurface *MapWindow::get_sdl_surface() {
2555 return (get_sdl_surface(0, 0, area.width(), area.height()));
2556 }
2557
get_sdl_surface(uint16 x,uint16 y,uint16 w,uint16 h)2558 Graphics::ManagedSurface *MapWindow::get_sdl_surface(uint16 x, uint16 y, uint16 w, uint16 h) {
2559 Graphics::ManagedSurface *new_surface = NULL;
2560 byte *screen_area;
2561 Common::Rect copy_area(area.left + x, area.top + y, area.left + x + w, area.top + y + h);
2562
2563 GUI::get_gui()->Display();
2564 screen_area = screen->copy_area(©_area);
2565
2566 new_surface = screen->create_sdl_surface_8(screen_area, copy_area.width(), copy_area.height());
2567 // new_surface = screen->create_sdl_surface_from(screen_area, screen->get_bpp(),
2568 // copy_area.w, copy_area.h,
2569 // copy_area.w);
2570 free(screen_area);
2571 return (new_surface);
2572 }
2573
2574 /* Returns the overlay surface. A new 8bit overlay is created if necessary. */
get_overlay()2575 Graphics::ManagedSurface *MapWindow::get_overlay() {
2576 if (!overlay)
2577 overlay = new Graphics::ManagedSurface(area.width(), area.height(),
2578 Graphics::PixelFormat::createFormatCLUT8());
2579
2580 return (overlay);
2581 }
2582
2583 /* Set the overlay surface. The current overlay is deleted if necessary. */
set_overlay(Graphics::ManagedSurface * surfpt)2584 void MapWindow::set_overlay(Graphics::ManagedSurface *surfpt) {
2585 if (overlay && (overlay != surfpt))
2586 SDL_FreeSurface(overlay);
2587 overlay = surfpt;
2588 }
2589
2590 /* Returns true if town tiles are within 5 tiles of the player */
in_town()2591 bool MapWindow::in_town() {
2592 MapCoord player_loc = actor_manager->get_player()->get_location();
2593
2594 for (Std::vector<TileInfo>::iterator ti = m_ViewableMapTiles.begin();
2595 ti != m_ViewableMapTiles.end(); ti++)
2596 if (MapCoord((*ti).x + cur_x, (*ti).y + cur_y, cur_level).distance(player_loc) <= 5 && // make sure tile is close enough
2597 ((*ti).t->flags1 & TILEFLAG_WALL) && ((*ti).t->flags1 & TILEFLAG_WALL_MASK)) { //only wall tiles with wall direction bits set.
2598 return true;
2599 }
2600 return false;
2601 }
2602
wizard_eye_start(MapCoord location,uint16 duration,CallBack * caller)2603 void MapWindow::wizard_eye_start(MapCoord location, uint16 duration, CallBack *caller) {
2604 wizard_eye_info.moves_left = duration;
2605 wizard_eye_info.caller = caller;
2606
2607 wizard_eye_info.prev_x = cur_x;
2608 wizard_eye_info.prev_y = cur_y;
2609
2610 set_x_ray_view(X_RAY_ON);
2611 sint16 map_x = location.x - (win_width / 2);
2612 if (game->is_original_plus_full_map())
2613 map_x += ((map_center_xoff + 1) / 2);
2614 moveMap(map_x, location.y - (win_height / 2) , cur_level); // FIXME - map should already be centered on the caster so why are we doing this?
2615 grab_focus();
2616 }
2617
wizard_eye_stop()2618 void MapWindow::wizard_eye_stop() {
2619 if (wizard_eye_info.moves_left > 0) {
2620 wizard_eye_info.moves_left = 0;
2621 wizard_eye_update();
2622 }
2623 }
2624
wizard_eye_update()2625 void MapWindow::wizard_eye_update() {
2626 if (wizard_eye_info.moves_left > 0)
2627 wizard_eye_info.moves_left--;
2628
2629 if (wizard_eye_info.moves_left == 0) {
2630 set_x_ray_view(X_RAY_OFF);
2631 moveMap(wizard_eye_info.prev_x, wizard_eye_info.prev_y, cur_level);
2632 wizard_eye_info.caller->callback(EFFECT_CB_COMPLETE, (CallBack *)this, NULL);
2633 release_focus();
2634 }
2635 }
2636
set_roof_mode(bool roofs)2637 void MapWindow::set_roof_mode(bool roofs) {
2638 roof_mode = roofs;
2639 if (roof_mode) {
2640 if (roof_tiles)
2641 return;
2642 else
2643 loadRoofTiles();
2644 } else {
2645 if (roof_tiles) {
2646 SDL_FreeSurface(roof_tiles);
2647 roof_tiles = NULL;
2648 }
2649 }
2650 }
2651
loadRoofTiles()2652 void MapWindow::loadRoofTiles() {
2653 Std::string imagefile = map->getRoofTilesetFilename();
2654 roof_tiles = SDL_LoadBMP(imagefile.c_str());
2655 if (roof_tiles && game->is_orig_style()) {
2656 SDL_SetColorKey(roof_tiles, SDL_TRUE, SDL_MapRGB(roof_tiles->format, 0, 0x70, 0xfc));
2657 }
2658 }
2659
in_dungeon_level()2660 bool MapWindow::in_dungeon_level() {
2661 if (game_type == NUVIE_GAME_MD) {
2662 return (cur_level == 1 || cur_level > 3); //FIXME this should probably be moved into script.
2663 }
2664 return (cur_level != 0 && cur_level != 5);
2665 }
2666
2667 } // End of namespace Nuvie
2668 } // End of namespace Ultima
2669