Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | SDLPoP, a port/conversion of the DOS game Prince of Persia. |
||
| 3 | Copyright (C) 2013-2018 Dávid Nagy |
||
| 4 | |||
| 5 | This program is free software: you can redistribute it and/or modify |
||
| 6 | it under the terms of the GNU General Public License as published by |
||
| 7 | the Free Software Foundation, either version 3 of the License, or |
||
| 8 | (at your option) any later version. |
||
| 9 | |||
| 10 | This program is distributed in the hope that it will be useful, |
||
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 13 | GNU General Public License for more details. |
||
| 14 | |||
| 15 | You should have received a copy of the GNU General Public License |
||
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
| 17 | |||
| 18 | The authors of this program may be contacted at http://forum.princed.org |
||
| 19 | */ |
||
| 20 | |||
| 21 | #include "common.h" |
||
| 22 | |||
| 23 | #ifdef USE_SCREENSHOT |
||
| 24 | |||
| 25 | // TODO: Use incrementing numbers and a separate folder, like DOSBox? Or allow custom filenames. |
||
| 26 | const char screenshot_filename[] = "screenshot.png"; |
||
| 27 | |||
| 28 | #define EVENT_OFFSET 0 // Add this number to displayed event numbers. Use 1 for Apoplexy compatibility. |
||
| 29 | |||
| 30 | #define NUMBER_OF_ROOMS 24 |
||
| 31 | |||
| 32 | // Save a screenshot. |
||
| 33 | void save_screenshot() { |
||
| 34 | IMG_SavePNG(onscreen_surface_, screenshot_filename); |
||
| 35 | printf("Saved screenshot to \"%s\".\n", screenshot_filename); |
||
| 36 | } |
||
| 37 | |||
| 38 | // Switch to the given room and draw it. |
||
| 39 | void switch_to_room(int room) { |
||
| 40 | drawn_room = room; |
||
| 41 | load_room_links(); |
||
| 42 | |||
| 43 | if (tbl_level_type[current_level]) { |
||
| 44 | gen_palace_wall_colors(); |
||
| 45 | } |
||
| 46 | |||
| 47 | // for guards |
||
| 48 | Guard.direction = dir_56_none; |
||
| 49 | guardhp_curr = 0; // otherwise guard HPs stay on screen |
||
| 50 | draw_guard_hp(0, 10); // otherwise guard HPs still stay on screen if some guards have extra HP |
||
| 51 | enter_guard(); // otherwise the guard won't show up |
||
| 52 | check_shadow(); // otherwise the shadow won't appear on level 6 |
||
| 53 | |||
| 54 | // for potion bubbles |
||
| 55 | for (int tilepos=0;tilepos<30;tilepos++) { |
||
| 56 | int tile_type = curr_room_tiles[tilepos] & 0x1F; |
||
| 57 | if (tile_type == tiles_10_potion) { |
||
| 58 | int modifier = curr_room_modif[tilepos]; |
||
| 59 | if ((modifier & 7) == 0) curr_room_modif[tilepos]++; |
||
| 60 | } |
||
| 61 | } |
||
| 62 | |||
| 63 | redraw_screen(1); |
||
| 64 | } |
||
| 65 | |||
| 66 | bool event_used[256] = {false}; |
||
| 67 | bool has_trigger_potion = false; |
||
| 68 | |||
| 69 | // delta vectors for room links |
||
| 70 | const int dx[4] = {-1, +1, 0, 0}; |
||
| 71 | const int dy[4] = { 0, 0, -1, +1}; |
||
| 72 | |||
| 73 | int xpos[NUMBER_OF_ROOMS+1] = {0}; |
||
| 74 | int ypos[NUMBER_OF_ROOMS+1] = {0}; |
||
| 75 | |||
| 76 | // Show annotations for non-visible things, like: room bounds, room numbers, door events, loose floors, potion types, special events, ... |
||
| 77 | // (this will make the function even more like a cheat) |
||
| 78 | // TODO: guard HPs, skill? fake tiles? |
||
| 79 | void draw_extras() { |
||
| 80 | // ambiguous tiles |
||
| 81 | // The editor branch has something similar... |
||
| 82 | for (int tilepos=0;tilepos<30;tilepos++) { |
||
| 83 | int tile_type = curr_room_tiles[tilepos] & 0x1F; |
||
| 84 | int modifier = curr_room_modif[tilepos]; |
||
| 85 | int row = tilepos/10; |
||
| 86 | int col = tilepos%10; |
||
| 87 | int y = row * 63 + 3; |
||
| 88 | int x = col * 32; |
||
| 89 | |||
| 90 | // special floors |
||
| 91 | rect_type floor_rect = {y+60-3, x, y+63-3, x+32}; |
||
| 92 | |||
| 93 | // loose floors |
||
| 94 | if (tile_type == tiles_11_loose) { |
||
| 95 | int color = color_15_brightwhite; |
||
| 96 | if (curr_room_tiles[tilepos] & 0x20) color = color_13_brightmagenta; // stable loose floor |
||
| 97 | show_text_with_color(&floor_rect, 0, -1, "~~~~", color); |
||
| 98 | } |
||
| 99 | |||
| 100 | // buttons |
||
| 101 | if (tile_type == tiles_15_opener) { |
||
| 102 | show_text_with_color(&floor_rect, 0, -1, "^^^^", color_10_brightgreen); |
||
| 103 | } |
||
| 104 | if (tile_type == tiles_6_closer) { |
||
| 105 | //show_text_with_color(&floor_rect, 0, -1, "XXXX", color_12_brightred); |
||
| 106 | floor_rect.top -= 2; |
||
| 107 | show_text_with_color(&floor_rect, 0, -1, "xxxx", color_12_brightred); // Only the top half is visible, looks like an inverted "^" or a tiny "v". |
||
| 108 | } |
||
| 109 | |||
| 110 | bool is_trob_here = false; |
||
| 111 | for (int index = 0; index < trobs_count; ++index) { |
||
| 112 | trob = trobs[index]; |
||
| 113 | if (trob.room == drawn_room && trob.tilepos == tilepos) { |
||
| 114 | is_trob_here = true; |
||
| 115 | break; |
||
| 116 | } |
||
| 117 | } |
||
| 118 | |||
| 119 | if (!is_trob_here) { // It's not stuck if it's currently animated. |
||
| 120 | // harmless spikes |
||
| 121 | if (tile_type == tiles_2_spike) { |
||
| 122 | if (modifier >= 5) { // harmless |
||
| 123 | rect_type spike_rect = {y+50, x, y+60, x+32}; |
||
| 124 | show_text_with_color(&spike_rect, 0, -1, "safe", color_10_brightgreen); |
||
| 125 | } |
||
| 126 | } |
||
| 127 | |||
| 128 | // stuck chompers |
||
| 129 | if (tile_type == tiles_18_chomper) { |
||
| 130 | int frame = (modifier & 0x7F); |
||
| 131 | if (frame != 0) { |
||
| 132 | rect_type chomper_rect = {y, x-10, y+60, x+32+10}; |
||
| 133 | int color = color_10_brightgreen; |
||
| 134 | if (frame == 2) color = color_12_brightred; |
||
| 135 | show_text_with_color(&chomper_rect, 0, 0, "stuck", color); |
||
| 136 | } |
||
| 137 | } |
||
| 138 | } |
||
| 139 | |||
| 140 | // potion types |
||
| 141 | if (tile_type == tiles_10_potion) { |
||
| 142 | struct pot_type { |
||
| 143 | int color; |
||
| 144 | const char* text; |
||
| 145 | } pot_types[7] = { |
||
| 146 | {color_7_lightgray, "x"}, // empty |
||
| 147 | {color_12_brightred, "+1"}, // heal |
||
| 148 | {color_12_brightred, "+++"}, // life |
||
| 149 | {color_10_brightgreen, "slow\nfall"}, // slow fall |
||
| 150 | {color_10_brightgreen, "flip"}, // upside down |
||
| 151 | {color_9_brightblue, "-1"}, // hurt |
||
| 152 | {color_9_brightblue, "trig"}, // open |
||
| 153 | }; |
||
| 154 | int potion_type = modifier >> 3; |
||
| 155 | int color; |
||
| 156 | const char* text; |
||
| 157 | char temp_text[4]; |
||
| 158 | if (potion_type >= 0 && potion_type < 7) { |
||
| 159 | color = pot_types[potion_type].color; |
||
| 160 | text = pot_types[potion_type].text; |
||
| 161 | } else { |
||
| 162 | color = color_15_brightwhite; |
||
| 163 | snprintf(temp_text, sizeof(temp_text), "%d", potion_type); |
||
| 164 | text = temp_text; |
||
| 165 | } |
||
| 166 | rect_type pot_rect = {y+40, x, y+60, x+32}; |
||
| 167 | show_text_with_color(&pot_rect, 0, -1, text, color); |
||
| 168 | } |
||
| 169 | |||
| 170 | // triggered door events |
||
| 171 | if (tile_type == tiles_6_closer || tile_type == tiles_15_opener |
||
| 172 | // These tiles are triggered even if they are not buttons! |
||
| 173 | /* |
||
| 174 | || (current_level == 1 && drawn_room == 5 && tilepos == 2) // triggered at start |
||
| 175 | || (current_level == 13 && drawn_room == 24 && tilepos == 0) // triggered when player enters any room from the right after Jaffar died |
||
| 176 | */ |
||
| 177 | || (has_trigger_potion && drawn_room == 8 && tilepos == 0) // triggered when player drinks an open potion |
||
| 178 | ) { |
||
| 179 | int first_event = modifier; |
||
| 180 | int last_event = modifier; |
||
| 181 | while (last_event<256 && get_doorlink_next(last_event)) last_event++; |
||
| 182 | /* |
||
| 183 | char events[10]; |
||
| 184 | if (modifier==last_event) { |
||
| 185 | snprintf(events, sizeof(events), "%d", first_event+EVENT_OFFSET); |
||
| 186 | } else { // from-to |
||
| 187 | snprintf(events, sizeof(events), "%d:%d", first_event+EVENT_OFFSET, last_event+EVENT_OFFSET); |
||
| 188 | } |
||
| 189 | */ |
||
| 190 | char events[256*4] = ""; // More than enough space to list all the numbers from 0 to 255. |
||
| 191 | int events_pos = 0; |
||
| 192 | for (int event=first_event; event<=last_event && events_pos<sizeof(events); event++) { |
||
| 193 | int len = snprintf(events+events_pos, sizeof(events)-events_pos, "%d ", event+EVENT_OFFSET); |
||
| 194 | if (len < 0) break; // snprintf might return -1 if the buffer is too small. |
||
| 195 | events_pos += len; |
||
| 196 | } |
||
| 197 | --events_pos; |
||
| 198 | if (events_pos>0 && events_pos<sizeof(events)) events[events_pos]='\0'; // trim trailing space |
||
| 199 | rect_type buttonmod_rect = {y/*+50-3*/, x, y+60-3, x+32}; |
||
| 200 | show_text_with_color(&buttonmod_rect, 0, 1, events, color_14_brightyellow); |
||
| 201 | } |
||
| 202 | |||
| 203 | // TODO: Add an option to merge events pointing to the same tile? |
||
| 204 | |||
| 205 | // door events that point here |
||
| 206 | char events[256*4] = ""; |
||
| 207 | int events_pos = 0; |
||
| 208 | for (int event=0; event<256 && events_pos<sizeof(events); event++) { |
||
| 209 | if (event_used[event] && get_doorlink_room(event) == drawn_room && get_doorlink_tile(event) == tilepos) { |
||
| 210 | int len = snprintf(events+events_pos, sizeof(events)-events_pos, "%d ", event+EVENT_OFFSET); |
||
| 211 | if (len < 0) break; |
||
| 212 | events_pos += len; |
||
| 213 | } |
||
| 214 | } |
||
| 215 | --events_pos; |
||
| 216 | if (events_pos>0 && events_pos<sizeof(events)) events[events_pos]='\0'; // trim trailing space |
||
| 217 | if (*events) { |
||
| 218 | //printf("room %d, tile %d, events: %s\n", drawn_room, tilepos, events); // debug |
||
| 219 | rect_type events_rect = {y,x,y+63-3,x+32-7}; |
||
| 220 | show_text_with_color(&events_rect, 0, 1, events, color_14_brightyellow); |
||
| 221 | } |
||
| 222 | |||
| 223 | // special events |
||
| 224 | char* special_event = NULL; |
||
| 225 | if (current_level == 0 && drawn_room == 24) { |
||
| 226 | special_event = "exit"; // exit by entering this room |
||
| 227 | } |
||
| 228 | // not marked: level 1 falling entry |
||
| 229 | if (current_level == 1 && drawn_room == 5 && tilepos == 2) { |
||
| 230 | special_event = "start\ntrig"; // triggered at start |
||
| 231 | } |
||
| 232 | if (current_level == 3 && drawn_room == 7 && col == 0) { |
||
| 233 | special_event = "<-\nchk point"; // checkpoint activation |
||
| 234 | } |
||
| 235 | if (current_level == 3 && drawn_room == 7 && tilepos == 4) { |
||
| 236 | special_event = "removed"; // loose floor is removed |
||
| 237 | } |
||
| 238 | if (current_level == 3 && drawn_room == 2 && tile_type == tiles_4_gate) { |
||
| 239 | special_event = "loud"; // closing can be heard everywhere |
||
| 240 | } |
||
| 241 | if (current_level == 3 && drawn_room == 2 && tilepos == 6) { |
||
| 242 | special_event = "check point"; // restart at checkpoint |
||
| 243 | // TODO: Show this room even if it is unreachable from the start via room links? |
||
| 244 | } |
||
| 245 | if (current_level == 3 && drawn_room == 1 && tilepos == 15 && tile_type == tiles_21_skeleton) { |
||
| 246 | special_event = "skel wake"; // skeleton wakes |
||
| 247 | } |
||
| 248 | if (current_level == 3 && drawn_room == 3 && tilepos == 14) { |
||
| 249 | special_event = "skel cont"; // skeleton continue |
||
| 250 | } |
||
| 251 | if (current_level == 4 && drawn_room == 4 && tilepos == 4) { |
||
| 252 | special_event = "mirror"; // mirror appears |
||
| 253 | } |
||
| 254 | // not marked: level 4 mirror clip |
||
| 255 | // not marked: level 5 shadow, required opening gate |
||
| 256 | if (current_level == 5 && drawn_room == 24 && tilepos == 3 && tile_type == tiles_10_potion) { |
||
| 257 | special_event = "stolen"; // stolen potion |
||
| 258 | } |
||
| 259 | // not marked: level 6 shadow (it's already visible) |
||
| 260 | if (current_level == 6 && drawn_room == 1 && row == 2) { |
||
| 261 | special_event = "exit\ndown"; // exit by falling |
||
| 262 | } |
||
| 263 | // not marked: level 7 falling entry |
||
| 264 | if (current_level == 8 && drawn_room == 16 && tilepos == 9) { |
||
| 265 | special_event = "mouse"; // mouse comes |
||
| 266 | } |
||
| 267 | if (current_level == 12 && drawn_room == 15 && tilepos == 1 && tile_type == tiles_22_sword) { |
||
| 268 | special_event = "disapp"; // sword disappears |
||
| 269 | } |
||
| 270 | if (current_level == 12 && drawn_room == 18 && col == 9) { |
||
| 271 | special_event = "disapp\n->"; // sword disappears |
||
| 272 | } |
||
| 273 | // not marked: level 12 shadow |
||
| 274 | if (current_level == 12 && row == 0 && (drawn_room == 2 || (drawn_room == 13 && col >= 6))) { |
||
| 275 | special_event = "floor"; // floors appear |
||
| 276 | } |
||
| 277 | if (current_level == 12 && drawn_room == 23) { |
||
| 278 | special_event = "exit"; // exit by entering this room |
||
| 279 | } |
||
| 280 | if (current_level == 13 && (drawn_room == level.roomlinks[23-1].up || drawn_room == level.roomlinks[16-1].up) && (tilepos >= 22 && tilepos <= 27)) { |
||
| 281 | special_event = "fall"; // falling loose floors |
||
| 282 | } |
||
| 283 | if (current_level == 13 && drawn_room == 3 && col == 9) { |
||
| 284 | special_event = "meet\n->"; // meet Jaffar |
||
| 285 | } |
||
| 286 | // not marked: flash |
||
| 287 | if (current_level == 13 && drawn_room == 24 && tilepos == 0) { |
||
| 288 | special_event = "Jffr\ntrig"; // triggered when player enters any room from the right after Jaffar died |
||
| 289 | } |
||
| 290 | if (current_level == 14 && drawn_room == 5) { |
||
| 291 | special_event = "end"; // end of game |
||
| 292 | } |
||
| 293 | if (has_trigger_potion && drawn_room == 8 && tilepos == 0) { |
||
| 294 | special_event = "blue\ntrig"; // triggered when player drinks an open potion |
||
| 295 | } |
||
| 296 | if (special_event) { |
||
| 297 | rect_type event_rect = {y,x-10,y+63,x+32+10}; |
||
| 298 | show_text_with_color(&event_rect, 0, 0, special_event, color_14_brightyellow); |
||
| 299 | } |
||
| 300 | |||
| 301 | // Attempt to show broken room links: |
||
| 302 | byte* roomlinks = (byte*)(&level.roomlinks[drawn_room-1]); |
||
| 303 | for (int direction = 0; direction < 4; direction++) { |
||
| 304 | int other_room = roomlinks[direction]; |
||
| 305 | if (other_room >= 1 && other_room <= NUMBER_OF_ROOMS) { |
||
| 306 | int other_x = xpos[drawn_room] + dx[direction]; |
||
| 307 | int other_y = ypos[drawn_room] + dy[direction]; |
||
| 308 | // If the linked room was placed elsewhere: Write the number of the linked room to the corresponding edge of the room. |
||
| 309 | if (xpos[other_room] != other_x || ypos[other_room] != other_y) { |
||
| 310 | int center_x = 160+dx[direction]*150; |
||
| 311 | int center_y = 96+dy[direction]*85; |
||
| 312 | rect_type text_rect = {center_y-6, center_x-10, center_y+6, center_x+10}; |
||
| 313 | char room_num[4]; |
||
| 314 | snprintf(room_num, sizeof(room_num), "%d", other_room); |
||
| 315 | method_5_rect(&text_rect, 0, color_4_red); |
||
| 316 | show_text_with_color(&text_rect, 0, 0, room_num, color_15_brightwhite); |
||
| 317 | } |
||
| 318 | } |
||
| 319 | } |
||
| 320 | |||
| 321 | // start pos |
||
| 322 | if (level.start_room == drawn_room && level.start_pos == tilepos) { |
||
| 323 | byte start_dir = level.start_dir; |
||
| 324 | if (current_level == 1 || current_level == 13) start_dir ^= 0xFF; // falling/running entry |
||
| 325 | char* start_text = (start_dir == dir_0_right) ? "start\n->" : "start\n<-"; |
||
| 326 | rect_type start_rect = {y,x-10,y+63,x+32+10}; |
||
| 327 | show_text_with_color(&start_rect, 0, 0, start_text, color_14_brightyellow); |
||
| 328 | } |
||
| 329 | |||
| 330 | } |
||
| 331 | |||
| 332 | // room number |
||
| 333 | char room_num[4]; |
||
| 334 | snprintf(room_num, sizeof(room_num), "%d", drawn_room); |
||
| 335 | rect_type text_rect = {10, 10, 21, 30}; |
||
| 336 | method_5_rect(&text_rect, 0, color_8_darkgray); |
||
| 337 | show_text_with_color(&text_rect, 0, 0, room_num, color_15_brightwhite); |
||
| 338 | |||
| 339 | // grid lines |
||
| 340 | rect_type vline = {0,0,192,1}; |
||
| 341 | method_5_rect(&vline, 0, color_12_brightred); |
||
| 342 | rect_type hline = {3,0,4,320}; |
||
| 343 | method_5_rect(&hline, 0, color_12_brightred); |
||
| 344 | } |
||
| 345 | |||
| 346 | // Save a "screenshot" of the whole level. |
||
| 347 | void save_level_screenshot(bool want_extras) { |
||
| 348 | // TODO: Disable in the intro or if a cutscene is active? |
||
| 349 | |||
| 350 | // Restrict this to cheat mode. After all, it's like using H/J/U/N or opening the level in an editor. |
||
| 351 | if (!cheats_enabled) return; |
||
| 352 | |||
| 353 | upside_down = 0; |
||
| 354 | |||
| 355 | //printf("random_seed = 0x%08X\n", random_seed); |
||
| 356 | |||
| 357 | // First, figure out where to put each room. |
||
| 358 | // We don't stop on broken room links, because the resulting map might still be usable. |
||
| 359 | |||
| 360 | bool processed[NUMBER_OF_ROOMS+1] = {false}; |
||
| 361 | for (int room=1;room<=NUMBER_OF_ROOMS;room++) { |
||
| 362 | xpos[drawn_room] = -999; |
||
| 363 | ypos[drawn_room] = -999; |
||
| 364 | } |
||
| 365 | xpos[drawn_room] = 0; |
||
| 366 | ypos[drawn_room] = 0; |
||
| 367 | int queue[NUMBER_OF_ROOMS] = {drawn_room}; // We start mapping from the current room. |
||
| 368 | int queue_start = 0; |
||
| 369 | int queue_end = 1; |
||
| 370 | |||
| 371 | while (queue_start < queue_end) { |
||
| 372 | int room = queue[queue_start++]; |
||
| 373 | byte* roomlinks = (byte*)(&level.roomlinks[room-1]); |
||
| 374 | for (int direction = 0; direction < 4; direction++) { |
||
| 375 | int other_room = roomlinks[direction]; |
||
| 376 | if (other_room >= 1 && other_room <= NUMBER_OF_ROOMS && !processed[other_room]) { |
||
| 377 | int other_x = xpos[room] + dx[direction]; |
||
| 378 | int other_y = ypos[room] + dy[direction]; |
||
| 379 | xpos[other_room] = other_x; |
||
| 380 | ypos[other_room] = other_y; |
||
| 381 | processed[other_room] = true; |
||
| 382 | queue[queue_end++] = other_room; |
||
| 383 | } |
||
| 384 | } |
||
| 385 | } |
||
| 386 | |||
| 387 | int min_x=0, max_x=0, min_y=0, max_y=0; |
||
| 388 | for (int room=1;room<=NUMBER_OF_ROOMS;room++) { |
||
| 389 | if (xpos[room] < min_x) min_x = xpos[room]; |
||
| 390 | if (xpos[room] > max_x) max_x = xpos[room]; |
||
| 391 | if (ypos[room] < min_y) min_y = ypos[room]; |
||
| 392 | if (ypos[room] > max_y) max_y = ypos[room]; |
||
| 393 | } |
||
| 394 | |||
| 395 | int map_width = max_x-min_x+1; |
||
| 396 | int map_height = max_y-min_y+1; |
||
| 397 | |||
| 398 | #define MAX_MAP_SIZE NUMBER_OF_ROOMS |
||
| 399 | int map[MAX_MAP_SIZE][MAX_MAP_SIZE] = {{0}}; |
||
| 400 | for (int room=1;room<=NUMBER_OF_ROOMS;room++) { |
||
| 401 | if (processed[room]) { |
||
| 402 | int y = ypos[room] - min_y; |
||
| 403 | int x = xpos[room] - min_x; |
||
| 404 | if (x>=0 && y>=0 && x<MAX_MAP_SIZE && y<MAX_MAP_SIZE) { |
||
| 405 | if (map[y][x]) { |
||
| 406 | printf("Warning: room %d was mapped to the same place as room %d!\n", room, map[y][x]); |
||
| 407 | // Force broken link display for room links pointing into this room: |
||
| 408 | // TODO: Try to find some other place for this room? |
||
| 409 | xpos[room] = -999; |
||
| 410 | ypos[room] = -999; |
||
| 411 | } else { |
||
| 412 | map[y][x] = room; |
||
| 413 | } |
||
| 414 | } |
||
| 415 | } |
||
| 416 | } |
||
| 417 | |||
| 418 | // Debug printout of arrangement. |
||
| 419 | /* |
||
| 420 | printf("LEVEL %d\n", current_level); |
||
| 421 | for (int y=0;y<map_height;y++) { |
||
| 422 | for (int x=0;x<map_width;x++) { |
||
| 423 | int room = map[y][x]; |
||
| 424 | if (room) { |
||
| 425 | printf(" %2d", room); |
||
| 426 | } else { |
||
| 427 | printf(" "); |
||
| 428 | } |
||
| 429 | } |
||
| 430 | printf("\n"); |
||
| 431 | } |
||
| 432 | printf("\n"); |
||
| 433 | */ |
||
| 434 | |||
| 435 | // Now we have the arrangement, let's make the picture! |
||
| 436 | |||
| 437 | int image_width = map_width*320; |
||
| 438 | int image_height = map_height*189+3+8; |
||
| 439 | |||
| 440 | SDL_Surface* map_surface = SDL_CreateRGBSurface(0, image_width, image_height, 32, 0xFF, 0xFF<<8, 0xFF<<16, 0xFF<<24); |
||
| 441 | if (map_surface == NULL) { |
||
| 442 | sdlperror("SDL_CreateRGBSurface (map_surface)"); |
||
| 443 | //exit(1); |
||
| 444 | return; |
||
| 445 | } |
||
| 446 | |||
| 447 | // TODO: Background color for places where there is no room? |
||
| 448 | |||
| 449 | // TODO: Add an option for displaying all unreachable rooms? |
||
| 450 | |||
| 451 | has_trigger_potion = false; |
||
| 452 | |||
| 453 | // Is there a trigger potion on the level? |
||
| 454 | for (int room=1;room<=NUMBER_OF_ROOMS;room++) { |
||
| 455 | if (processed[room]) { |
||
| 456 | get_room_address(room); |
||
| 457 | for (int tilepos=0;tilepos<30;tilepos++) { |
||
| 458 | int tile_type = curr_room_tiles[tilepos] & 0x1F; |
||
| 459 | if (tile_type == tiles_10_potion && curr_room_modif[tilepos] >> 3 == 6) { |
||
| 460 | has_trigger_potion = true; |
||
| 461 | } |
||
| 462 | } |
||
| 463 | } |
||
| 464 | } |
||
| 465 | |||
| 466 | memset(event_used, 0, sizeof(event_used)); |
||
| 467 | |||
| 468 | // Find out which door events are used: |
||
| 469 | for (int room=1;room<=NUMBER_OF_ROOMS;room++) { |
||
| 470 | if (processed[room]) { |
||
| 471 | get_room_address(room); |
||
| 472 | for (int tilepos=0;tilepos<30;tilepos++) { |
||
| 473 | int tile_type = curr_room_tiles[tilepos] & 0x1F; |
||
| 474 | if (tile_type == tiles_6_closer || tile_type == tiles_15_opener |
||
| 475 | // These tiles are triggered even if they are not buttons! |
||
| 476 | // TODO: Force displaying of special trigger rooms even if they are unreachable via room links? |
||
| 477 | /* |
||
| 478 | || (current_level == 1 && room == 5 && tilepos == 2) // triggered at start |
||
| 479 | || (current_level == 13 && room == 24 && tilepos == 0) // triggered when player enters any room from the right after Jaffar died |
||
| 480 | */ |
||
| 481 | || (has_trigger_potion && room == 8 && tilepos == 0) // triggered when player drinks an open potion |
||
| 482 | ) { |
||
| 483 | int modifier = curr_room_modif[tilepos]; |
||
| 484 | for (int index = modifier; index < 256; index++) { |
||
| 485 | event_used[index] = true; |
||
| 486 | if (!get_doorlink_next(index)) break; |
||
| 487 | } |
||
| 488 | } |
||
| 489 | } |
||
| 490 | } |
||
| 491 | } |
||
| 492 | |||
| 493 | // debug |
||
| 494 | /* |
||
| 495 | printf("Used events:"); |
||
| 496 | for (int event=0;event<256;event++) { |
||
| 497 | if (event_used[event]) { |
||
| 498 | printf(" %d", event+EVENT_OFFSET); |
||
| 499 | } |
||
| 500 | } |
||
| 501 | printf("\n"); |
||
| 502 | */ |
||
| 503 | /* |
||
| 504 | for (int event=0;event<256;event++) { |
||
| 505 | if (event_used[event]) { |
||
| 506 | printf("event %d: room %d tile %d %s\n", |
||
| 507 | event+EVENT_OFFSET, get_doorlink_room(event), get_doorlink_tile(event), |
||
| 508 | get_doorlink_next(event) ? "+next" : ""); |
||
| 509 | } |
||
| 510 | } |
||
| 511 | */ |
||
| 512 | |||
| 513 | screen_updates_suspended = true; |
||
| 514 | int old_room = drawn_room; |
||
| 515 | for (int y=0;y<map_height;y++) { |
||
| 516 | for (int x=0;x<map_width;x++) { |
||
| 517 | int room = map[y][x]; |
||
| 518 | if (room) { |
||
| 519 | SDL_Rect dest_rect; |
||
| 520 | dest_rect.x = x*320; |
||
| 521 | dest_rect.y = y*189; |
||
| 522 | switch_to_room(room); |
||
| 523 | |||
| 524 | if (want_extras) draw_extras(); |
||
| 525 | |||
| 526 | // TODO: Hide the status bar, or maybe show some custom text on it? |
||
| 527 | |||
| 528 | SDL_BlitSurface(onscreen_surface_, NULL, map_surface, &dest_rect); |
||
| 529 | } |
||
| 530 | } |
||
| 531 | } |
||
| 532 | switch_to_room(old_room); |
||
| 533 | screen_updates_suspended = false; |
||
| 534 | |||
| 535 | IMG_SavePNG(map_surface, screenshot_filename); |
||
| 536 | printf("Saved level screenshot to \"%s\".\n", screenshot_filename); |
||
| 537 | |||
| 538 | SDL_FreeSurface(map_surface); |
||
| 539 | |||
| 540 | //printf("random_seed = 0x%08X\n", random_seed); |
||
| 541 | } |
||
| 542 | |||
| 543 | bool want_auto = false; |
||
| 544 | bool want_auto_whole_level = false; |
||
| 545 | bool want_auto_extras = false; |
||
| 546 | |||
| 547 | void init_screenshot() { |
||
| 548 | // Command-line options to automatically save a screenshot at startup. |
||
| 549 | const char* screenshot_param = check_param("--screenshot"); |
||
| 550 | if (screenshot_param != NULL) { |
||
| 551 | // We require megahit+levelnumber. |
||
| 552 | if (start_level < 0) { |
||
| 553 | printf("You must supply a level number if you want to make an automatic screenshot!\n"); |
||
| 554 | exit(1); |
||
| 555 | } else { |
||
| 556 | want_auto = true; |
||
| 557 | want_auto_whole_level = (check_param("--screenshot-level") != NULL); |
||
| 558 | want_auto_extras = (check_param("--screenshot-level-extras") != NULL); |
||
| 559 | } |
||
| 560 | } |
||
| 561 | } |
||
| 562 | |||
| 563 | // TODO: Don't open a window if the user wants an auto screenshot. |
||
| 564 | |||
| 565 | // To skip cutscenes, etc. |
||
| 566 | bool want_auto_screenshot() { |
||
| 567 | return want_auto; |
||
| 568 | } |
||
| 569 | |||
| 570 | // Called when the level is drawn for the first time. |
||
| 571 | void auto_screenshot() { |
||
| 572 | if (!want_auto) return; |
||
| 573 | |||
| 574 | if (want_auto_whole_level) { |
||
| 575 | save_level_screenshot(want_auto_extras); |
||
| 576 | } else { |
||
| 577 | save_screenshot(); |
||
| 578 | } |
||
| 579 | |||
| 580 | quit(1); |
||
| 581 | } |
||
| 582 | |||
| 583 | #endif |
||
| 584 |