Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Routines to display title screens...
  23.  *
  24.  */
  25.  
  26.  
  27. #include "dxxsconf.h"
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31.  
  32. #if DXX_USE_OGL
  33. #include "ogl_init.h"
  34. #endif
  35. #include "pstypes.h"
  36. #include "timer.h"
  37. #include "key.h"
  38. #include "gr.h"
  39. #include "palette.h"
  40. #include "iff.h"
  41. #include "pcx.h"
  42. #include "physfsx.h"
  43. #include "u_mem.h"
  44. #include "joy.h"
  45. #include "titles.h"
  46. #include "gamefont.h"
  47. #include "gameseq.h"
  48. #include "dxxerror.h"
  49. #include "robot.h"
  50. #include "textures.h"
  51. #include "screens.h"
  52. #include "multi.h"
  53. #include "player.h"
  54. #include "digi.h"
  55. #include "text.h"
  56. #include "kmatrix.h"
  57. #include "piggy.h"
  58. #include "songs.h"
  59. #include "newmenu.h"
  60. #include "state.h"
  61. #if defined(DXX_BUILD_DESCENT_II)
  62. #include "movie.h"
  63. #endif
  64. #include "menu.h"
  65. #include "mouse.h"
  66. #include "console.h"
  67. #include "args.h"
  68. #include "strutil.h"
  69.  
  70. #include "compiler-range_for.h"
  71. #include "partial_range.h"
  72. #include <memory>
  73.  
  74. #if defined(DXX_BUILD_DESCENT_I)
  75. constexpr std::true_type EMULATING_D1{};
  76. #endif
  77.  
  78. #if defined(DXX_BUILD_DESCENT_II)
  79. #define SHAREWARE_ENDING_FILENAME       "ending.tex"
  80. #endif
  81. #define DEFAULT_BRIEFING_BKG            "brief03.pcx"
  82.  
  83. namespace dcx {
  84.  
  85. static std::array<color_t, 7> Briefing_text_colors;
  86. static color_t *Current_color;
  87. static color_t Erase_color;
  88.  
  89. // added by Jan Bobrowski for variable-size menu screen
  90. static int rescale_x(const grs_bitmap &cv_bitmap, int x)
  91. {
  92.         return x * cv_bitmap.bm_w / 320;
  93. }
  94.  
  95. static int rescale_y(const grs_bitmap &cv_bitmap, int y)
  96. {
  97.         return y * cv_bitmap.bm_h / 200;
  98. }
  99.  
  100. static int get_message_num(const char *&message)
  101. {
  102.         char *p;
  103.         auto num = strtoul(message, &p, 10);
  104.         while (*p && *p++ != '\n')              //      Get and drop eoln
  105.                 ;
  106.         message = p;
  107.         return num;
  108. }
  109.  
  110. namespace {
  111.  
  112. struct title_screen : ignore_window_pointer_t
  113. {
  114.         grs_main_bitmap title_bm;
  115.         fix64 timer;
  116.         int allow_keys;
  117. };
  118.  
  119. }
  120.  
  121. static window_event_result title_handler(window *, const d_event &event, title_screen *ts)
  122. {
  123.         window_event_result result;
  124.  
  125.         switch (event.type)
  126.         {
  127.                 case EVENT_MOUSE_BUTTON_DOWN:
  128.                         if (event_mouse_get_button(event) != 0)
  129.                                 return window_event_result::ignored;
  130.                         else if (ts->allow_keys)
  131.                         {
  132.                                 return window_event_result::close;
  133.                         }
  134.                         break;
  135.  
  136.                 case EVENT_KEY_COMMAND:
  137.                         if ((result = call_default_handler(event)) == window_event_result::ignored)
  138.                                 if (ts->allow_keys)
  139.                                 {
  140.                                         return window_event_result::close;
  141.                                 }
  142.                         return result;
  143.  
  144.                 case EVENT_JOYSTICK_BUTTON_DOWN:
  145.                         if (ts->allow_keys)
  146.                         {
  147.                                 return window_event_result::close;
  148.                         }
  149.                         break;
  150.  
  151.                 case EVENT_IDLE:
  152.                         timer_delay2(50);
  153.  
  154.                         if (timer_query() > ts->timer)
  155.                         {
  156.                                 return window_event_result::close;
  157.                         }
  158.                         break;
  159.  
  160.                 case EVENT_WINDOW_DRAW:
  161.                         gr_set_default_canvas();
  162.                         show_fullscr(*grd_curcanv, ts->title_bm);
  163.                         break;
  164.  
  165.                 case EVENT_WINDOW_CLOSE:
  166.                         break;
  167.  
  168.                 default:
  169.                         break;
  170.         }
  171.         return window_event_result::ignored;
  172. }
  173.  
  174. static void show_title_screen(const char * filename, int allow_keys, int from_hog_only )
  175. {
  176.         char new_filename[PATH_MAX] = "";
  177.  
  178.         auto ts = std::make_unique<title_screen>();
  179.         ts->allow_keys = allow_keys;
  180.  
  181.         (void)from_hog_only;
  182.  
  183.         strcat(new_filename,filename);
  184.         filename = new_filename;
  185.  
  186.         ts->timer = timer_query() + i2f(3);
  187.         const auto pcx_error = pcx_read_bitmap(filename, ts->title_bm, gr_palette);
  188.         if (pcx_error != pcx_result::SUCCESS)
  189.         {
  190.                 con_printf(CON_URGENT, "%s:%u: error: loading briefing screen <%s> failed: PCX load error: %s (%u)", __FILE__, __LINE__, filename, pcx_errormsg(pcx_error), static_cast<unsigned>(pcx_error));
  191.                 return;
  192.         }
  193.  
  194.         gr_palette_load( gr_palette );
  195.  
  196.         const auto wind = window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, title_handler, ts.get());
  197.         if (!wind)
  198.         {
  199.                 return;
  200.         }
  201.  
  202.         event_process_all();
  203. }
  204.  
  205. static void show_first_found_title_screen(const char *oem, const char *share, const char *macshare)
  206. {
  207.         const char *filename = oem;
  208.         if ((PHYSFSX_exists(filename, 1)) ||
  209.                 (filename = share, PHYSFSX_exists(filename, 1)) ||
  210.                 (filename = macshare, PHYSFSX_exists(filename, 1))
  211.                 )
  212.                 show_title_screen(filename, 1, 1);
  213. }
  214.  
  215. }
  216.  
  217. namespace dsx {
  218. #if defined(DXX_BUILD_DESCENT_II)
  219. int intro_played;
  220. static int DefineBriefingBox(const grs_bitmap &, const char *&buf);
  221. #endif
  222.  
  223. void show_titles(void)
  224. {
  225. #if defined(DXX_BUILD_DESCENT_I)
  226.         songs_play_song(SONG_TITLE, 1);
  227. #endif
  228.         if (CGameArg.SysNoTitles)
  229.                 return;
  230. #if defined(DXX_BUILD_DESCENT_I)
  231.  
  232.         show_first_found_title_screen(
  233.                 "macplay.pcx"// Mac Shareware
  234.                 "mplaycd.pcx"// Mac Registered
  235.                 "iplogo1.pcx"   // PC. Only down here because it's lowres ;-)
  236.         );
  237.         const bool resolution_at_least_640_480 = (SWIDTH >= 640 && SHEIGHT >= 480);
  238.         auto &logo_hires_pcx = "logoh.pcx";
  239.         auto &descent_hires_pcx = "descenth.pcx";
  240.         show_title_screen((resolution_at_least_640_480 && PHYSFSX_exists(logo_hires_pcx, 1)) ? logo_hires_pcx : "logo.pcx", 1, 1);
  241.         show_title_screen((resolution_at_least_640_480 && PHYSFSX_exists(descent_hires_pcx, 1)) ? descent_hires_pcx : "descent.pcx", 1, 1);
  242. #elif defined(DXX_BUILD_DESCENT_II)
  243.         int played=MOVIE_NOT_PLAYED;    //default is not played
  244.         int song_playing = 0;
  245.  
  246. #define MOVIE_REQUIRED 1        //(!is_D2_OEM && !is_SHAREWARE && !is_MAC_SHARE)        // causes segfault
  247.  
  248.         const auto hiresmode = HIRESMODE;
  249.         {       //show bundler screens
  250.                 played=MOVIE_NOT_PLAYED;        //default is not played
  251.  
  252.                 played = PlayMovie(NULL, "pre_i.mve",0);
  253.  
  254.                 if (!played) {
  255.                         char filename[12];
  256.                         strcpy(filename, hiresmode ? "pre_i1b.pcx" : "pre_i1.pcx");
  257.  
  258.                         while (PHYSFSX_exists(filename,0))
  259.                         {
  260.                                 show_title_screen( filename, 1, 0 );
  261.                                 filename[5]++;
  262.                         }
  263.                 }
  264.         }
  265.  
  266.         played = PlayMovie("intro.tex", "intro.mve",MOVIE_REQUIRED);
  267.  
  268.         if (played != MOVIE_NOT_PLAYED)
  269.                 intro_played = 1;
  270.         else
  271.         {                                               //didn't get intro movie, try titles
  272.  
  273.                 played = PlayMovie(NULL, "titles.mve",MOVIE_REQUIRED);
  274.  
  275.                 if (played == MOVIE_NOT_PLAYED)
  276.                 {
  277.                         con_puts(CON_DEBUG, "Playing title song...");
  278.                         songs_play_song( SONG_TITLE, 1);
  279.                         song_playing = 1;
  280.                         con_puts(CON_DEBUG, "Showing logo screens...");
  281.  
  282.                         show_first_found_title_screen(
  283.                                 hiresmode ? "iplogo1b.pcx" : "iplogo1.pcx", // OEM
  284.                                 "iplogo1.pcx", // SHAREWARE
  285.                                 "mplogo.pcx" // MAC SHAREWARE
  286.                                 );
  287.                         show_first_found_title_screen(
  288.                                 hiresmode ? "logob.pcx" : "logo.pcx", // OEM
  289.                                 "logo.pcx", // SHAREWARE
  290.                                 "plogo.pcx" // MAC SHAREWARE
  291.                                 );
  292.                 }
  293.         }
  294.  
  295.         {       //show bundler movie or screens
  296.                 played=MOVIE_NOT_PLAYED;        //default is not played
  297.  
  298.                 //check if OEM movie exists, so we don't stop the music if it doesn't
  299.                 if (RAIIPHYSFS_File{PHYSFS_openRead("oem.mve")})
  300.                 {
  301.                         played = PlayMovie(NULL, "oem.mve",0);
  302.                         song_playing = 0;               //movie will kill sound
  303.                 }
  304.  
  305.                 if (!played)
  306.                 {
  307.                         char filename[12];
  308.                         strcpy(filename, hiresmode ? "oem1b.pcx" : "oem1.pcx");
  309.                         while (PHYSFSX_exists(filename,0))
  310.                         {
  311.                                 show_title_screen( filename, 1, 0 );
  312.                                 filename[3]++;
  313.                         }
  314.                 }
  315.         }
  316.  
  317.         if (!song_playing)
  318.         {
  319.                 con_puts(CON_DEBUG, "Playing title song...");
  320.                 songs_play_song( SONG_TITLE, 1);
  321.         }
  322.         con_puts(CON_DEBUG, "Showing logo screen...");
  323.         const auto filename = hiresmode ? "descentb.pcx" : "descent.pcx";
  324.         if (PHYSFSX_exists(filename,1))
  325.                 show_title_screen(filename, 1, 1);
  326. #endif
  327. }
  328.  
  329. void show_order_form()
  330. {
  331.         if (CGameArg.SysNoTitles)
  332.                 return;
  333.  
  334. #if defined(DXX_BUILD_DESCENT_I)
  335.         show_first_found_title_screen(
  336.                 "warning.pcx"// D1 Registered
  337.                 "apple.pcx",    // D1 Mac OEM Demo
  338.                 "order01.pcx"   // D1 Demo
  339.         );
  340. #elif defined(DXX_BUILD_DESCENT_II)
  341. #if !DXX_USE_EDITOR
  342.         key_flush();
  343.         const auto hiresmode = HIRESMODE;
  344.         /*
  345.          * If D2 registered, all checks fail and nothing is shown.
  346.          */
  347.         const char *exit_screen = hiresmode ? "ordrd2ob.pcx" : "ordrd2o.pcx"; // OEM
  348.         if ((PHYSFSX_exists(exit_screen, 1)) ||
  349.                 // SHAREWARE, prefer mac if hires
  350.                 (exit_screen = hiresmode ? "orderd2b.pcx" : "orderd2.pcx", PHYSFSX_exists(exit_screen, 1)) ||
  351.                 // SHAREWARE, have to rescale
  352.                 (exit_screen = hiresmode ? "orderd2.pcx" : "orderd2b.pcx", PHYSFSX_exists(exit_screen, 1)) ||
  353.                 // D1
  354.                 (exit_screen = hiresmode ? "warningb.pcx" : "warning.pcx", PHYSFSX_exists(exit_screen, 1))
  355.                 )
  356.                 show_title_screen(exit_screen,1,0);
  357. #endif
  358. #endif
  359. }
  360. }
  361.  
  362. namespace dcx {
  363. namespace {
  364.  
  365. //-----------------------------------------------------------------------------
  366. struct briefing_screen {
  367.         char    bs_name[13];                //  filename, eg merc01.  Assumes .lbm suffix.
  368.         sbyte   level_num;
  369.         sbyte   message_num;
  370.         short   text_ulx, text_uly;         //  upper left x,y of text window
  371.         short   text_width, text_height;    //  width and height of text window
  372. };
  373.  
  374. #define ENDING_LEVEL_NUM_OEMSHARE 0x7f
  375. #define ENDING_LEVEL_NUM_REGISTER 0x7e
  376.  
  377. }
  378.  
  379. static grs_subcanvas_ptr create_spinning_robot_sub_canvas(grs_canvas &canvas)
  380. {
  381.         return gr_create_sub_canvas(canvas, rescale_x(canvas.cv_bitmap, 138), rescale_y(canvas.cv_bitmap, 55), rescale_x(canvas.cv_bitmap, 166), rescale_y(canvas.cv_bitmap, 138));
  382. }
  383.  
  384. static void get_message_name(const char *&message, std::array<char, 32> &result, const char *const trailer)
  385. {
  386.         auto p = message;
  387.         for (; *p == ' '; ++p)
  388.         {
  389.         }
  390.         const auto e = std::prev(result.end(), sizeof(".bbm"));
  391.         auto i = result.begin();
  392.         char c;
  393.         for (; (c = *p) && c != ' ' && c != '\n'; ++p)
  394.         {
  395.                 *i++ = c;
  396.                 if (i == e)
  397.                         /* Avoid buffer overflow; this was not present in the
  398.                          * original code.
  399.                          */
  400.                         break;
  401.         }
  402.         /* This is inconsistent.  If the copy loop terminated on a newline,
  403.          * then `p` points to the newline and the next loop is skipped.  If
  404.          * the copy loop terminated on a null, the next loop is attempted,
  405.          * but exits immediately.  In both those cases, `p` is unchanged.
  406.          * If the copy loop terminated on any other character, the next loop
  407.          * will advance `p` to point to a null (consistent with the copy
  408.          * loop terminating on a null) or past the newline (inconsistent
  409.          * with the copy loop).
  410.          *
  411.          * This inconsistency was present in the prior version of the code,
  412.          * and is retained in case there exist briefings which rely on this
  413.          * inconsistency.
  414.          */
  415.         if (c != '\n')
  416.                 while ((c = *p) && (++p, c) != '\n')            //      Get and drop eoln
  417.                 {
  418.                 }
  419.         message = p;
  420.         strcpy(i, trailer);
  421. }
  422. }
  423.  
  424. namespace dsx {
  425. namespace {
  426.  
  427. #if defined(DXX_BUILD_DESCENT_II)
  428.  
  429. static std::array<briefing_screen, 60> Briefing_screens{{
  430.         {"brief03.pcx",0,3,8,8,257,177}
  431. }}; // default=0!!!
  432. #endif
  433.  
  434. constexpr briefing_screen D1_Briefing_screens_full[] = {
  435.         { "brief01.pcx",   0,  1,  13, 140, 290,  59 },
  436.         { "brief02.pcx",   0,  2,  27,  34, 257, 177 },
  437.         { "brief03.pcx",   0,  3,  20,  22, 257, 177 },
  438.         { "brief02.pcx",   0,  4,  27,  34, 257, 177 },
  439.         { "moon01.pcx",    1,  5,  10,  10, 300, 170 }, // level 1
  440.         { "moon01.pcx",    2,  6,  10,  10, 300, 170 }, // level 2
  441.         { "moon01.pcx",    3,  7,  10,  10, 300, 170 }, // level 3
  442.         { "venus01.pcx",   4,  8,  15, 15, 300,  200 }, // level 4
  443.         { "venus01.pcx",   5,  9,  15, 15, 300,  200 }, // level 5
  444.         { "brief03.pcx",   6, 10,  20,  22, 257, 177 },
  445.         { "merc01.pcx",    6, 11,  10, 15, 300, 200 },  // level 6
  446.         { "merc01.pcx",    7, 12,  10, 15, 300, 200 },  // level 7
  447.         { "brief03.pcx",   8, 13,  20,  22, 257, 177 },
  448.         { "mars01.pcx",    8, 14,  10, 100, 300,  200 }, // level 8
  449.         { "mars01.pcx",    9, 15,  10, 100, 300,  200 }, // level 9
  450.         { "brief03.pcx",  10, 16,  20,  22, 257, 177 },
  451.         { "mars01.pcx",   10, 17,  10, 100, 300,  200 }, // level 10
  452.         { "jup01.pcx",    11, 18,  10, 40, 300,  200 }, // level 11
  453.         { "jup01.pcx",    12, 19,  10, 40, 300,  200 }, // level 12
  454.         { "brief03.pcx",  13, 20,  20,  22, 257, 177 },
  455.         { "jup01.pcx",    13, 21,  10, 40, 300,  200 }, // level 13
  456.         { "jup01.pcx",    14, 22,  10, 40, 300,  200 }, // level 14
  457.         { "saturn01.pcx", 15, 23,  10, 40, 300,  200 }, // level 15
  458.         { "brief03.pcx",  16, 24,  20,  22, 257, 177 },
  459.         { "saturn01.pcx", 16, 25,  10, 40, 300,  200 }, // level 16
  460.         { "brief03.pcx",  17, 26,  20,  22, 257, 177 },
  461.         { "saturn01.pcx", 17, 27,  10, 40, 300,  200 }, // level 17
  462.         { "uranus01.pcx", 18, 28,  100, 100, 300,  200 }, // level 18
  463.         { "uranus01.pcx", 19, 29,  100, 100, 300,  200 }, // level 19
  464.         { "uranus01.pcx", 20, 30,  100, 100, 300,  200 }, // level 20
  465.         { "uranus01.pcx", 21, 31,  100, 100, 300,  200 }, // level 21
  466.         { "neptun01.pcx", 22, 32,  10, 20, 300,  200 }, // level 22
  467.         { "neptun01.pcx", 23, 33,  10, 20, 300,  200 }, // level 23
  468.         { "neptun01.pcx", 24, 34,  10, 20, 300,  200 }, // level 24
  469.         { "pluto01.pcx",  25, 35,  10, 20, 300,  200 }, // level 25
  470.         { "pluto01.pcx",  26, 36,  10, 20, 300,  200 }, // level 26
  471.         { "pluto01.pcx",  27, 37,  10, 20, 300,  200 }, // level 27
  472.         { "aster01.pcx",  -1, 38,  10, 90, 300,  200 }, // secret level -1
  473.         { "aster01.pcx",  -2, 39,  10, 90, 300,  200 }, // secret level -2
  474.         { "aster01.pcx",  -3, 40,  10, 90, 300,  200 }, // secret level -3
  475.         { "end01.pcx",   ENDING_LEVEL_NUM_OEMSHARE,  1,  23, 40, 320, 200 },   //  OEM and shareware end
  476.         { "end02.pcx",   ENDING_LEVEL_NUM_REGISTER,  1,  5, 5, 300, 200 },    // registered end
  477.         { "end01.pcx",   ENDING_LEVEL_NUM_REGISTER,  2,  23, 40, 320, 200 },  // registered end
  478.         { "end03.pcx",   ENDING_LEVEL_NUM_REGISTER,  3,  5, 5, 300, 200 },    // registered end
  479. };
  480.  
  481. constexpr briefing_screen D1_Briefing_screens_share[] = {
  482.         { "brief01.pcx",   0,  1,  13, 140, 290,  59 },
  483.         { "brief02.pcx",   0,  2,  27,  34, 257, 177 },
  484.         { "brief03.pcx",   0,  3,  20,  22, 257, 177 },
  485.         { "brief02.pcx",   0,  4,  27,  34, 257, 177 },
  486.         { "moon01.pcx",    1,  5,  10,  10, 300, 170 }, // level 1
  487.         { "moon01.pcx",    2,  6,  10,  10, 300, 170 }, // level 2
  488.         { "moon01.pcx",    3,  7,  10,  10, 300, 170 }, // level 3
  489.         { "venus01.pcx",   4,  8,  15, 15, 300,  200 }, // level 4
  490.         { "venus01.pcx",   5,  9,  15, 15, 300,  200 }, // level 5
  491.         { "brief03.pcx",   6, 10,  20,  22, 257, 177 },
  492.         { "merc01.pcx",    6, 10,  10, 15, 300, 200 }, // level 6
  493.         { "merc01.pcx",    7, 11,  10, 15, 300, 200 }, // level 7
  494.         { "end01.pcx",   ENDING_LEVEL_NUM_OEMSHARE,  1,  23, 40, 320, 200 }, // shareware end
  495. };
  496.  
  497. struct msgstream
  498. {
  499.         int x;
  500.         int y;
  501.         color_t color;
  502.         char ch;
  503. };
  504.  
  505. constexpr const briefing_screen *get_d1_briefing_screens(const unsigned descent_hog_size)
  506. {
  507.         if (descent_hog_size == D1_SHAREWARE_MISSION_HOGSIZE || descent_hog_size == D1_SHAREWARE_10_MISSION_HOGSIZE)
  508.                 return D1_Briefing_screens_share;
  509.         return D1_Briefing_screens_full;
  510. }
  511.  
  512. #if defined(DXX_BUILD_DESCENT_I)
  513. using briefing_screen_deleter = std::default_delete<briefing_screen>;
  514. #elif defined(DXX_BUILD_DESCENT_II)
  515. class briefing_screen_deleter : std::default_delete<briefing_screen>
  516. {
  517.         typedef std::default_delete<briefing_screen> base_deleter;
  518. public:
  519.         briefing_screen_deleter() = default;
  520.         briefing_screen_deleter(base_deleter &&b) : base_deleter(std::move(b)) {}
  521.         void operator()(briefing_screen *const p) const
  522.         {
  523.                 if (p >= &Briefing_screens.front() && p <= &Briefing_screens.back())
  524.                         return;
  525.                 this->base_deleter::operator()(p);
  526.         }
  527. };
  528. #endif
  529.  
  530. struct briefing : ignore_window_pointer_t
  531. {
  532.         unsigned streamcount;
  533.         short   level_num;
  534.         short   cur_screen;
  535.         std::unique_ptr<briefing_screen, briefing_screen_deleter> screen;
  536.         grs_main_bitmap background;
  537. #if defined(DXX_BUILD_DESCENT_II)
  538.         int             got_z;
  539.         RAIIdigi_sound          hum_channel, printing_channel;
  540.         MVESTREAM_ptr_t pMovie;
  541. #endif
  542.         std::unique_ptr<char[]> text;
  543.         const char      *message;
  544.         int             text_x, text_y;
  545.         std::array<msgstream, 2048> messagestream;
  546.         short   tab_stop;
  547.         ubyte   flashing_cursor;
  548.         ubyte   new_page;
  549.         int             new_screen;
  550. #if defined(DXX_BUILD_DESCENT_II)
  551.         ubyte   dumb_adjust;
  552.         ubyte   line_adjustment;
  553.         char    robot_playing;
  554.         short   chattering;
  555. #endif
  556.         fix64           start_time;
  557.         fix64           delay_count;
  558.         int             robot_num;
  559.         grs_subcanvas_ptr       robot_canv;
  560.         vms_angvec      robot_angles;
  561.         std::array<char, 32> bitmap_name;
  562.         grs_main_bitmap  guy_bitmap;
  563.         sbyte   door_dir, door_div_count, animating_bitmap_type;
  564.         sbyte   prev_ch;
  565.         std::array<char, 16> background_name;
  566. };
  567.  
  568. }
  569.  
  570. static void briefing_init(briefing *br, short level_num)
  571. {
  572.         br->level_num = level_num;
  573.         if (EMULATING_D1 && (br->level_num == 1))
  574.                 br->level_num = 0;      // for start of game stuff
  575.  
  576.         br->cur_screen = 0;
  577.         br->background_name.back() = 0;
  578.         strncpy(br->background_name.data(), DEFAULT_BRIEFING_BKG, br->background_name.size() - 1);
  579. #if defined(DXX_BUILD_DESCENT_II)
  580.         br->robot_playing = 0;
  581. #endif
  582.         br->robot_num = 0;
  583.         br->robot_angles = {};
  584.         br->bitmap_name[0] = '\0';
  585.         br->door_dir = 1;
  586.         br->door_div_count = 0;
  587.         br->animating_bitmap_type = 0;
  588. }
  589.  
  590. //-----------------------------------------------------------------------------
  591. //      Load Descent briefing text.
  592. static int load_screen_text(const d_fname &filename, std::unique_ptr<char[]> &buf)
  593. {
  594.         int len, have_binary = 0;
  595.         auto e = end(filename);
  596.         auto ext = std::find(begin(filename), e, '.');
  597.         if (ext == e)
  598.                 return (0);
  599.         if (!d_stricmp(&*ext, ".txb"))
  600.                 have_binary = 1;
  601.        
  602.         auto tfile = PHYSFSX_openReadBuffered(filename);
  603.         if (!tfile)
  604.                 return (0);
  605.  
  606.         len = PHYSFS_fileLength(tfile);
  607.         buf = std::make_unique<char[]>(len + 1);
  608.         PHYSFS_read(tfile, buf.get(), 1, len);
  609. #if defined(DXX_BUILD_DESCENT_I)
  610.         const auto endbuf = &buf[len];
  611. #elif defined(DXX_BUILD_DESCENT_II)
  612.         const auto endbuf = std::remove(&buf[0], &buf[len], 13);
  613. #endif
  614.         *endbuf = 0;
  615.  
  616.         if (have_binary)
  617.                 decode_text(buf.get(), len);
  618.  
  619.         return (1);
  620. }
  621.  
  622. #if defined(DXX_BUILD_DESCENT_II)
  623. static void set_briefing_fontcolor(struct briefing &br);
  624. static int get_new_message_num(const char *&message)
  625. {
  626.         char *p;
  627.         const auto num = strtoul(message, &p, 10);
  628.         message = p + 1;
  629.         return num;
  630. }
  631. #endif
  632.  
  633. // Return a pointer to the start of text for screen #screen_num.
  634. static const char * get_briefing_message(const briefing *br, int screen_num)
  635. {
  636.         const char      *tptr = br->text.get();
  637.         int     cur_screen=0;
  638.         int     ch;
  639.  
  640.         Assert(screen_num >= 0);
  641.  
  642.         while ( (*tptr != 0 ) && (screen_num != cur_screen)) {
  643.                 ch = *tptr++;
  644.                 if (ch == '$') {
  645.                         ch = *tptr++;
  646.                         if (ch == 'S')
  647.                                 cur_screen = get_message_num(tptr);
  648.                 }
  649.         }
  650.  
  651.         if (screen_num!=cur_screen)
  652.                 return (NULL);
  653.  
  654.         return tptr;
  655. }
  656.  
  657. static void init_char_pos(briefing *br, int x, int y)
  658. {
  659.         br->text_x = x;
  660.         br->text_y = y;
  661. }
  662.  
  663. // Make sure the text stays on the screen
  664. // Return 1 if new page required
  665. // 0 otherwise
  666. static int check_text_pos(briefing *br)
  667. {
  668.         if (br->text_x > br->screen->text_ulx + br->screen->text_width)
  669.         {
  670.                 br->text_x = br->screen->text_ulx;
  671.                 br->text_y += br->screen->text_uly;
  672.         }
  673.  
  674.         if (br->text_y > br->screen->text_uly + br->screen->text_height)
  675.         {
  676.                 br->new_page = 1;
  677.                 return 1;
  678.         }
  679.  
  680.         return 0;
  681. }
  682.  
  683. static void put_char_delay(const grs_font &cv_font, briefing *const br, const char ch)
  684. {
  685.         char str[2];
  686.         int     w;
  687.  
  688.         str[0] = ch; str[1] = '\0';
  689.         if (br->delay_count && (timer_query() < br->start_time + br->delay_count))
  690.         {
  691.                 br->message--;          // Go back to same character
  692.                 return;
  693.         }
  694.  
  695.         if (br->streamcount >= br->messagestream.size())
  696.                 return;
  697.         br->messagestream[br->streamcount].x = br->text_x;
  698.         br->messagestream[br->streamcount].y = br->text_y;
  699.         br->messagestream[br->streamcount].color = *Current_color;
  700.         br->messagestream[br->streamcount].ch = ch;
  701.         br->streamcount++;
  702.  
  703.         br->prev_ch = ch;
  704.         gr_get_string_size(cv_font, str, &w, nullptr, nullptr);
  705.         br->text_x += w;
  706.  
  707. #if defined(DXX_BUILD_DESCENT_II)
  708.         if (!EMULATING_D1 && !br->chattering) {
  709.                 br->printing_channel.reset(digi_start_sound(digi_xlat_sound(SOUND_BRIEFING_PRINTING), F1_0, 0xFFFF/2, 1, -1, -1, sound_object_none));
  710.                 br->chattering=1;
  711.         }
  712. #endif
  713.  
  714.         br->start_time = timer_query();
  715. }
  716.  
  717. static void init_spinning_robot(grs_canvas &canvas, briefing &br);
  718. static int load_briefing_screen(grs_canvas &, briefing *br, const char *fname);
  719.  
  720. // Process a character for the briefing,
  721. // including special characters preceded by a '$'.
  722. // Return 1 when page is finished, 0 otherwise
  723. static int briefing_process_char(grs_canvas &canvas, briefing *const br)
  724. {
  725.         auto &game_font = *GAME_FONT;
  726.         char ch = *br->message++;
  727.         if (ch == '$') {
  728.                 ch = *br->message++;
  729. #if defined(DXX_BUILD_DESCENT_II)
  730.                 if (ch=='D') {
  731.                         br->cur_screen = DefineBriefingBox(canvas.cv_bitmap, br->message);
  732.                         br->screen.reset(&Briefing_screens[br->cur_screen]);
  733.                         init_char_pos(br, br->screen->text_ulx, br->screen->text_uly);
  734.                         br->line_adjustment=0;
  735.                         br->prev_ch = 10;                                   // read to eoln
  736.                 } else if (ch=='U') {
  737.                         br->cur_screen = get_message_num(br->message);
  738.                         br->screen.reset(&Briefing_screens[br->cur_screen]);
  739.                         init_char_pos(br, br->screen->text_ulx, br->screen->text_uly);
  740.                         br->prev_ch = 10;                                   // read to eoln
  741.                 } else
  742. #endif
  743.                 if (ch == 'C') {
  744.                         auto cc = get_message_num(br->message) - 1;
  745.                         if (cc < 0)
  746.                                 cc = 0;
  747.                         else if (cc > Briefing_text_colors.size() - 1)
  748.                                 cc = Briefing_text_colors.size() - 1;
  749.                         Current_color = &Briefing_text_colors[cc];
  750.                         br->prev_ch = 10;
  751.                 } else if (ch == 'F') {     // toggle flashing cursor
  752.                         br->flashing_cursor = !br->flashing_cursor;
  753.                         br->prev_ch = 10;
  754.                         while (*br->message++ != 10)
  755.                                 ;
  756.                 } else if (ch == 'T') {
  757.                         br->tab_stop = get_message_num(br->message);
  758.                         br->prev_ch = 10;                                                       //      read to eoln
  759.                 } else if (ch == 'R') {
  760.                         br->robot_canv.reset();
  761. #if defined(DXX_BUILD_DESCENT_II)
  762.                         if (br->robot_playing) {
  763.                                 DeInitRobotMovie(br->pMovie);
  764.                                 br->robot_playing=0;
  765.                         }
  766. #endif
  767.  
  768.                         if (EMULATING_D1) {
  769.                                 init_spinning_robot(canvas, *br);
  770.                                 br->robot_num = get_message_num(br->message);
  771. #if defined(DXX_BUILD_DESCENT_II)
  772.                                 while (*br->message++ != 10)
  773.                                         ;
  774. #endif
  775.                         } else {
  776. #if defined(DXX_BUILD_DESCENT_II)
  777.                                 char spinRobotName[]="RBA.MVE", kludge;  // matt don't change this!
  778.  
  779.                                 kludge=*br->message++;
  780.                                 spinRobotName[2]=kludge; // ugly but proud
  781.  
  782.                                 br->robot_playing=InitRobotMovie(spinRobotName, br->pMovie);
  783.  
  784.                                 // gr_remap_bitmap_good( &grd_curcanv->cv_bitmap, pal, -1, -1 );
  785.  
  786.                                 if (br->robot_playing) {
  787.                                         RotateRobot(br->pMovie);
  788.                                         set_briefing_fontcolor(*br);
  789.                                 }
  790. #endif
  791.                         }
  792.                         br->prev_ch = 10;                           // read to eoln
  793.                 }
  794.                 else if ((ch == 'N' && (br->animating_bitmap_type = 0, true)) ||
  795.                                 (ch == 'O' && (br->animating_bitmap_type = 1, true)))
  796.                 {
  797.                         br->robot_canv.reset();
  798.                         br->prev_ch = 10;
  799.                         get_message_name(br->message, br->bitmap_name, "#0");
  800.                 } else if (ch=='A') {
  801. #if defined(DXX_BUILD_DESCENT_II)
  802.                         br->line_adjustment=1-br->line_adjustment;
  803. #endif
  804.                 } else if (ch=='Z') {
  805. #if defined(DXX_BUILD_DESCENT_II)
  806.                         char fname[15];
  807.                         int i;
  808.  
  809.                         br->got_z=1;
  810.                         br->dumb_adjust=1;
  811.                         i=0;
  812.                         while ((fname[i]=*br->message) != '\n') {
  813.                                 i++;
  814.                                 br->message++;
  815.                         }
  816.                         fname[i]=0;
  817.                         if (*br->message != 10)
  818.                                 while (*br->message++ != 10)    //  Get and drop eoln
  819.                                         ;
  820.  
  821.                         {
  822.                                 char fname2[15];
  823.  
  824.                                 i=0;
  825.                                 while (fname[i]!='.') {
  826.                                         fname2[i] = fname[i];
  827.                                         i++;
  828.                                 }
  829.                                 fname2[i++]='b';
  830.                                 fname2[i++]='.';
  831.                                 fname2[i++]='p';
  832.                                 fname2[i++]='c';
  833.                                 fname2[i++]='x';
  834.                                 fname2[i++]=0;
  835.  
  836.                                 if ((HIRESMODE && PHYSFSX_exists(fname2,1)) || !PHYSFSX_exists(fname,1))
  837.                                         strcpy(fname,fname2);
  838.                                 load_briefing_screen(*grd_curcanv, br, fname);
  839.                         }
  840.  
  841. #endif
  842.                 } else if (ch == 'B') {
  843.                         std::array<char, 32> bitmap_name;
  844.                         palette_array_t         temp_palette;
  845.                         int             iff_error;
  846.                         br->robot_canv.reset();
  847.                         get_message_name(br->message, bitmap_name, ".bbm");
  848.                         br->guy_bitmap.reset();
  849.                         iff_error = iff_read_bitmap(&bitmap_name[0], br->guy_bitmap, &temp_palette);
  850. #if defined(DXX_BUILD_DESCENT_II)
  851.                         gr_remap_bitmap_good( br->guy_bitmap, temp_palette, -1, -1 );
  852. #endif
  853.                         Assert(iff_error == IFF_NO_ERROR);
  854.                         (void)iff_error;
  855.  
  856.                         br->prev_ch = 10;
  857.                 } else if (ch == 'S') {
  858. #if defined(DXX_BUILD_DESCENT_II)
  859.                         br->chattering = 0;
  860.                         br->printing_channel.reset();
  861. #endif
  862.  
  863.                         br->new_screen = 1;
  864.                         return 1;
  865.                 } else if (ch == 'P') {         //      New page.
  866. #if defined(DXX_BUILD_DESCENT_II)
  867.                         if (!br->got_z) {
  868.                                 Int3(); // Hey ryan!!!! You gotta load a screen before you start
  869.                                 // printing to it! You know, $Z !!!
  870.                                 load_briefing_screen(*grd_curcanv, br, HIRESMODE ? "end01b.pcx" : "end01.pcx");
  871.                         }
  872.  
  873.                         br->chattering = 0;
  874.                         br->printing_channel.reset();
  875. #endif
  876.  
  877.                         br->new_page = 1;
  878.  
  879.                         while (*br->message != 10) {
  880.                                 br->message++;  //      drop carriage return after special escape sequence
  881.                         }
  882.                         br->message++;
  883.                         br->prev_ch = 10;
  884.  
  885.                         return 1;
  886.                 }
  887. #if defined(DXX_BUILD_DESCENT_II)
  888.                 else if (ch == ':') {
  889.                         br->prev_ch = 10;
  890.                         auto p = br->message;
  891.                         /* Legacy clients do not understand $: and will instead show
  892.                          * the remainder of the line.  However, if the next two
  893.                          * characters after $: are $F, legacy clients will treat
  894.                          * the $F as above, toggle the flashing_cursor flag, then
  895.                          * discard the rest of the line.  This special case allows
  896.                          * briefing authors to hide the directive from legacy
  897.                          * clients, but get the same state of flashing_cursor for
  898.                          * both legacy and aware clients.  Briefing authors will
  899.                          * likely need an additional $F nearby to balance this
  900.                          * toggle, but no additional code here is needed to support
  901.                          * that.
  902.                          *
  903.                          * The trailing colon is cosmetic, so that the compatibility
  904.                          * $F is not directly adjacent to the directive.
  905.                          */
  906.                         if (!strncmp(p, "$F:", 3))
  907.                         {
  908.                                 br->flashing_cursor = !br->flashing_cursor;
  909.                                 p += 3;
  910.                         }
  911.                         auto &rotate_robot_label = "Rebirth.rotate.robot ";
  912.                         constexpr auto rotate_robot_len = sizeof(rotate_robot_label) - 1;
  913.                         if (!strncmp(p, rotate_robot_label, rotate_robot_len))
  914.                         {
  915.                                 char *p2;
  916.                                 const auto id = strtoul(p + rotate_robot_len, &p2, 10);
  917.                                 if (*p2 == '\n')
  918.                                 {
  919.                                         p = p2;
  920.                                         br->robot_canv.reset();
  921.                                         if (br->robot_playing)
  922.                                         {
  923.                                                 br->robot_playing = 0;
  924.                                                 DeInitRobotMovie(br->pMovie);
  925.                                         }
  926.                                         init_spinning_robot(canvas, *br);
  927.                                         /* This modifies the appearance of the frame, which
  928.                                          * is unfortunate.  However, without it, all robots
  929.                                          * come out blue shifted.
  930.                                          */
  931.                                         gr_use_palette_table("groupa.256");
  932.                                         br->robot_num = id;
  933.                                 }
  934.                         }
  935.                         else
  936.                         {
  937.                                 const char *p2 = p;
  938.                                 /* Suppress non-printing characters.  No need to support
  939.                                  * encodings.
  940.                                  */
  941.                                 for (char c; (c = *p2) >= ' ' && c <= '~'; ++p2)
  942.                                 {
  943.                                 }
  944.                                 con_printf(CON_VERBOSE, "warning: unknown briefing directive \"%.*s\"", DXX_ptrdiff_cast_int(p2 - p), p);
  945.                         }
  946.                         for (char c; (c = *p) && (++p, c) != '\n';)
  947.                         {
  948.                                 /* Discard through newline.  On break, *p is '\0' or
  949.                                  * p[-1] is '\n'.
  950.                                  */
  951.                         }
  952.                         br->message = p;
  953.                 }
  954. #endif
  955.                 else if (ch == '$' || ch == ';') // Print a $/;
  956.                         put_char_delay(game_font, br, ch);
  957.         } else if (ch == '\t') {                //      Tab
  958.                 const auto &&fspacx = FSPACX();
  959.                 if (br->text_x - br->screen->text_ulx < fspacx(br->tab_stop))
  960.                         br->text_x = br->screen->text_ulx + fspacx(br->tab_stop);
  961.         } else if ((ch == ';') && (br->prev_ch == 10)) {
  962.                 while (*br->message++ != 10)
  963.                         ;
  964.                 br->prev_ch = 10;
  965.         } else if (ch == '\\') {
  966.                 br->prev_ch = ch;
  967.         } else if (ch == 10) {
  968.                 if (br->prev_ch != '\\') {
  969.                         br->prev_ch = ch;
  970. #if defined(DXX_BUILD_DESCENT_II)
  971.                         if (br->dumb_adjust)
  972.                                 br->dumb_adjust--;
  973.                         else
  974. #endif
  975.                                 br->text_y += FSPACY(5)+FSPACY(5)*3/5;
  976.                         br->text_x = br->screen->text_ulx;
  977.                         if (br->text_y > br->screen->text_uly + br->screen->text_height) {
  978. #if defined(DXX_BUILD_DESCENT_I)
  979.                                 const auto descent_hog_size = PHYSFSX_fsize("descent.hog");
  980.                                 load_briefing_screen(*grd_curcanv, br, get_d1_briefing_screens(descent_hog_size)[br->cur_screen].bs_name);
  981. #elif defined(DXX_BUILD_DESCENT_II)
  982.                                 load_briefing_screen(*grd_curcanv, br, Briefing_screens[br->cur_screen].bs_name);
  983. #endif
  984.                                 br->text_x = br->screen->text_ulx;
  985.                                 br->text_y = br->screen->text_uly;
  986.                         }
  987.                 } else {
  988.                         if (ch == 13)           //Can this happen? Above says ch==10
  989.                                 Int3();
  990.                         br->prev_ch = ch;
  991.                 }
  992.         } else {
  993. #if defined(DXX_BUILD_DESCENT_II)
  994.                 if (!br->got_z) {
  995.                         LevelError("briefing wrote to screen without using $Z to load a screen; loading default.");
  996.                         //Int3(); // Hey ryan!!!! You gotta load a screen before you start
  997.                         // printing to it! You know, $Z !!!
  998.                         load_briefing_screen(*grd_curcanv, br, HIRESMODE ? "end01b.pcx" : "end01.pcx");
  999.                 }
  1000. #endif
  1001.                 put_char_delay(game_font, br, ch);
  1002.         }
  1003.  
  1004.         return 0;
  1005. }
  1006.  
  1007. #if defined(DXX_BUILD_DESCENT_I)
  1008. static void set_briefing_fontcolor()
  1009. #elif defined(DXX_BUILD_DESCENT_II)
  1010. static void set_briefing_fontcolor(briefing &br)
  1011. #endif
  1012. {
  1013.         struct rgb
  1014.         {
  1015.                 int r, g, b;
  1016.         };
  1017.         std::array<rgb, 3> colors;
  1018.         if (EMULATING_D1) {
  1019.                 //green
  1020.                 colors[0] = {0, 54, 0};
  1021.                 //white
  1022.                 colors[1] = {42, 38, 32};
  1023.                 //Begin D1X addition
  1024.                 //red
  1025.                 colors[2] = {63, 0, 0};
  1026.         }
  1027.         else
  1028.         {
  1029.                 colors[0] = {0, 40, 0};
  1030.                 colors[1] = {40, 33, 35};
  1031.                 colors[2] = {8, 31, 54};
  1032.         }
  1033.  
  1034. #if defined(DXX_BUILD_DESCENT_II)
  1035.         if (br.robot_playing)
  1036.         {
  1037.                 colors[0] = {0, 31, 0};
  1038.         }
  1039. #endif
  1040.         Briefing_text_colors[0] = gr_find_closest_color_current(colors[0].r, colors[0].g, colors[0].b);
  1041.         Briefing_text_colors[1] = gr_find_closest_color_current(colors[1].r, colors[1].g, colors[1].b);
  1042.         Briefing_text_colors[2] = gr_find_closest_color_current(colors[2].r, colors[2].g, colors[2].b);
  1043.  
  1044.         //blue
  1045.         Briefing_text_colors[3] = gr_find_closest_color_current( 0, 0, 54);
  1046.         //gray
  1047.         Briefing_text_colors[4] = gr_find_closest_color_current( 14, 14, 14);
  1048.         //yellow
  1049.         Briefing_text_colors[5] = gr_find_closest_color_current( 54, 54, 0);
  1050.         //purple
  1051.         Briefing_text_colors[6] = gr_find_closest_color_current( 0, 54, 54);
  1052.         //End D1X addition
  1053.  
  1054.         Erase_color = gr_find_closest_color_current(0, 0, 0);
  1055. }
  1056. }
  1057.  
  1058. static void redraw_messagestream(grs_canvas &canvas, const grs_font &cv_font, const msgstream &stream, unsigned &lastcolor)
  1059. {
  1060.         char msgbuf[2] = {stream.ch, 0};
  1061.         if (lastcolor != stream.color)
  1062.         {
  1063.                 lastcolor = stream.color;
  1064.                 gr_set_fontcolor(canvas, stream.color, -1);
  1065.         }
  1066.         gr_string(canvas, cv_font, stream.x + 1, stream.y, msgbuf);
  1067. }
  1068.  
  1069. namespace dsx {
  1070. static void flash_cursor(grs_canvas &canvas, const grs_font &cv_font, briefing *const br, const int cursor_flag)
  1071. {
  1072.         if (cursor_flag == 0)
  1073.                 return;
  1074.         gr_set_fontcolor(canvas, (timer_query() % (F1_0 / 2)) > F1_0 / 4 ? *Current_color : Erase_color, -1);
  1075.         gr_string(canvas, cv_font, br->text_x, br->text_y, "_");
  1076. }
  1077.  
  1078. #define EXIT_DOOR_MAX   14
  1079. #define OTHER_THING_MAX 10      // Adam: This is the number of frames in your new animating thing.
  1080. #define DOOR_DIV_INIT   6
  1081.  
  1082. //-----------------------------------------------------------------------------
  1083. static void show_animated_bitmap(grs_canvas &canvas, briefing *br)
  1084. {
  1085.         grs_bitmap      *bitmap_ptr;
  1086. #if DXX_USE_OGL
  1087.         float scale = 1.0;
  1088.  
  1089.         if ((static_cast<float>(SWIDTH)/320) < (static_cast<float>(SHEIGHT)/200))
  1090.                 scale = (static_cast<float>(SWIDTH)/320);
  1091.         else
  1092.                 scale = (static_cast<float>(SHEIGHT)/200);
  1093. #endif
  1094.  
  1095.         // Only plot every nth frame.
  1096.         if (br->door_div_count) {
  1097.                 if (br->bitmap_name[0] != 0) {
  1098.                         bitmap_index bi;
  1099.                         bi = piggy_find_bitmap(&br->bitmap_name[0]);
  1100.                         bitmap_ptr = &GameBitmaps[bi.index];
  1101.                         PIGGY_PAGE_IN( bi );
  1102. #if DXX_USE_OGL
  1103.                         ogl_ubitmapm_cs(canvas, rescale_x(canvas.cv_bitmap, 220), rescale_y(canvas.cv_bitmap, 45), bitmap_ptr->bm_w * scale, bitmap_ptr->bm_h * scale, *bitmap_ptr, 255, F1_0);
  1104. #else
  1105.                         gr_bitmapm(canvas, rescale_x(canvas.cv_bitmap, 220), rescale_y(canvas.cv_bitmap, 45), *bitmap_ptr);
  1106. #endif
  1107.                 }
  1108.                 br->door_div_count--;
  1109.                 return;
  1110.         }
  1111.  
  1112.         br->door_div_count = DOOR_DIV_INIT;
  1113.  
  1114.         if (br->bitmap_name[0] != 0) {
  1115.                 char            *pound_signp;
  1116.                 int             num, dig1, dig2;
  1117.                 bitmap_index bi;
  1118.                 grs_subcanvas_ptr bitmap_canv;
  1119.  
  1120.                 switch (br->animating_bitmap_type) {
  1121.                         case 0:
  1122.                                 bitmap_canv = gr_create_sub_canvas(canvas, rescale_x(canvas.cv_bitmap, 220), rescale_y(canvas.cv_bitmap, 45), 64, 64);
  1123.                                 break;
  1124.                         case 1:
  1125.                                 bitmap_canv = gr_create_sub_canvas(canvas, rescale_x(canvas.cv_bitmap, 220), rescale_y(canvas.cv_bitmap, 45), 94, 94);
  1126.                                 break; // Adam: Change here for your new animating bitmap thing. 94, 94 are bitmap size.
  1127.                         default:        Int3(); // Impossible, illegal value for br->animating_bitmap_type
  1128.                 }
  1129.  
  1130.                 auto &subcanvas = *bitmap_canv.get();
  1131.  
  1132.                 pound_signp = strchr(&br->bitmap_name[0], '#');
  1133.                 Assert(pound_signp != NULL);
  1134.  
  1135.                 dig1 = *(pound_signp+1);
  1136.                 dig2 = *(pound_signp+2);
  1137.                 if (dig2 == 0)
  1138.                         num = dig1-'0';
  1139.                 else
  1140.                         num = (dig1-'0')*10 + (dig2-'0');
  1141.  
  1142.                 switch (br->animating_bitmap_type) {
  1143.                         case 0:
  1144.                                 num += br->door_dir;
  1145.                                 if (num > EXIT_DOOR_MAX) {
  1146.                                         num = EXIT_DOOR_MAX;
  1147.                                         br->door_dir = -1;
  1148.                                 } else if (num < 0) {
  1149.                                         num = 0;
  1150.                                         br->door_dir = 1;
  1151.                                 }
  1152.                                 break;
  1153.                         case 1:
  1154.                                 num++;
  1155.                                 if (num > OTHER_THING_MAX)
  1156.                                         num = 0;
  1157.                                 break;
  1158.                 }
  1159.  
  1160.                 Assert(num < 100);
  1161.                 if (num >= 10) {
  1162.                         *(pound_signp+1) = (num / 10) + '0';
  1163.                         *(pound_signp+2) = (num % 10) + '0';
  1164.                         *(pound_signp+3) = 0;
  1165.                 } else {
  1166.                         *(pound_signp+1) = (num % 10) + '0';
  1167.                         *(pound_signp+2) = 0;
  1168.                 }
  1169.  
  1170.                 bi = piggy_find_bitmap(&br->bitmap_name[0]);
  1171.                 bitmap_ptr = &GameBitmaps[bi.index];
  1172.                 PIGGY_PAGE_IN( bi );
  1173. #if DXX_USE_OGL
  1174.                 ogl_ubitmapm_cs(subcanvas, 0, 0, bitmap_ptr->bm_w*scale, bitmap_ptr->bm_h*scale, *bitmap_ptr, 255, F1_0);
  1175. #else
  1176.                 gr_bitmapm(subcanvas, 0, 0, *bitmap_ptr);
  1177. #endif
  1178.  
  1179.                 switch (br->animating_bitmap_type) {
  1180.                         case 0:
  1181.                                 if (num == EXIT_DOOR_MAX) {
  1182.                                         br->door_dir = -1;
  1183.                                         br->door_div_count = 64;
  1184.                                 } else if (num == 0) {
  1185.                                         br->door_dir = 1;
  1186.                                         br->door_div_count = 64;
  1187.                                 }
  1188.                                 break;
  1189.                         case 1:
  1190.                                 break;
  1191.                 }
  1192.         }
  1193. }
  1194.  
  1195. }
  1196.  
  1197. //-----------------------------------------------------------------------------
  1198. static void show_briefing_bitmap(grs_canvas &canvas, grs_bitmap *bmp)
  1199. {
  1200.         const bool hiresmode = HIRESMODE;
  1201.         const auto w = static_cast<float>(SWIDTH) / (hiresmode ? 640 : 320);
  1202.         const auto h = static_cast<float>(SHEIGHT) / (hiresmode ? 480 : 200);
  1203.         const float scale = (w < h) ? w : h;
  1204.  
  1205.         auto bitmap_canv = gr_create_sub_canvas(canvas, rescale_x(canvas.cv_bitmap, 220), rescale_y(canvas.cv_bitmap, 55), bmp->bm_w*scale, bmp->bm_h*scale);
  1206.         show_fullscr(*bitmap_canv, *bmp);
  1207. }
  1208.  
  1209. //-----------------------------------------------------------------------------
  1210. namespace dsx {
  1211. static void init_spinning_robot(grs_canvas &canvas, briefing &br) //(int x,int y,int w,int h)
  1212. {
  1213.         br.robot_canv = create_spinning_robot_sub_canvas(canvas);
  1214. }
  1215.  
  1216. static void show_spinning_robot_frame(briefing *br, int robot_num)
  1217. {
  1218.         auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
  1219.         if (robot_num != -1) {
  1220.                 br->robot_angles.p = br->robot_angles.b = 0;
  1221.                 br->robot_angles.h += 150;
  1222.  
  1223.                 Assert(Robot_info[robot_num].model_num != -1);
  1224.                 draw_model_picture(*br->robot_canv.get(), Robot_info[robot_num].model_num, br->robot_angles);
  1225.         }
  1226. }
  1227.  
  1228. //-----------------------------------------------------------------------------
  1229. #define KEY_DELAY_DEFAULT       ((F1_0*20)/1000)
  1230.  
  1231. static void init_new_page(briefing *br)
  1232. {
  1233.         br->new_page = 0;
  1234.         br->robot_num = -1;
  1235.  
  1236.         load_briefing_screen(*grd_curcanv, br, br->background_name.data());
  1237.         br->text_x = br->screen->text_ulx;
  1238.         br->text_y = br->screen->text_uly;
  1239.  
  1240.         br->streamcount=0;
  1241.         br->guy_bitmap.reset();
  1242.  
  1243. #if defined(DXX_BUILD_DESCENT_II)
  1244.         if (br->robot_playing)
  1245.         {
  1246.                 DeInitRobotMovie(br->pMovie);
  1247.                 br->robot_playing=0;
  1248.         }
  1249. #endif
  1250.  
  1251.         br->start_time = 0;
  1252.         br->delay_count = KEY_DELAY_DEFAULT;
  1253. }
  1254.  
  1255. #if defined(DXX_BUILD_DESCENT_II)
  1256. static int DefineBriefingBox(const grs_bitmap &cv_bitmap, const char *&buf)
  1257. {
  1258.         int i=0;
  1259.         char name[20];
  1260.  
  1261.         const auto n = get_new_message_num (buf);
  1262.  
  1263.         assert(n < Briefing_screens.size());
  1264.  
  1265.         while (*buf != ' ')
  1266.         {
  1267.                 name[i++]= *buf;
  1268.                 ++buf;
  1269.         }
  1270.  
  1271.         name[i]='\0';   // slap a delimiter on this guy
  1272.  
  1273.         strcpy (Briefing_screens[n].bs_name,name);
  1274.         Briefing_screens[n].level_num=get_new_message_num (buf);
  1275.         Briefing_screens[n].message_num=get_new_message_num (buf);
  1276.         Briefing_screens[n].text_ulx=get_new_message_num (buf);
  1277.         Briefing_screens[n].text_uly=get_new_message_num (buf);
  1278.         Briefing_screens[n].text_width=get_new_message_num (buf);
  1279.         Briefing_screens[n].text_height = get_message_num (buf);  // NOTICE!!!
  1280.  
  1281.         Briefing_screens[n].text_ulx = rescale_x(cv_bitmap, Briefing_screens[n].text_ulx);
  1282.         Briefing_screens[n].text_uly = rescale_y(cv_bitmap, Briefing_screens[n].text_uly);
  1283.         Briefing_screens[n].text_width = rescale_x(cv_bitmap, Briefing_screens[n].text_width);
  1284.         Briefing_screens[n].text_height = rescale_y(cv_bitmap, Briefing_screens[n].text_height);
  1285.  
  1286.         return (n);
  1287. }
  1288. #endif
  1289.  
  1290. static void free_briefing_screen(briefing *br);
  1291.  
  1292. //      -----------------------------------------------------------------------------
  1293. //      loads a briefing screen
  1294. static int load_briefing_screen(grs_canvas &canvas, briefing *const br, const char *const fname)
  1295. {
  1296. #if defined(DXX_BUILD_DESCENT_I)
  1297.         const auto descent_hog_size = PHYSFSX_fsize("descent.hog");
  1298.         char forigin[PATH_MAX];
  1299.         decltype(br->background_name) fname2a;
  1300.  
  1301.         free_briefing_screen(br);
  1302.  
  1303.         snprintf(fname2a.data(), fname2a.size(), "%s", fname);
  1304.         snprintf(forigin, sizeof(forigin), "%s", PHYSFS_getRealDir(fname));
  1305.         d_strlwr(forigin);
  1306.  
  1307.         // check if we have a hires version of this image (not included in PC-version by default)
  1308.         // Also if this hires image comes via external AddOn pack, only apply if requested image would be loaded from descent.hog - not a seperate mission which might want to show something else.
  1309.         if (SWIDTH >= 640 && SHEIGHT >= 480 && (strstr(forigin,"descent.hog") != NULL))
  1310.         {
  1311.                 const auto fb = fname2a.begin();
  1312.                 auto ptr = fb;
  1313.                 for (; const char c = *ptr; ++ptr)
  1314.                 {
  1315.                         if (c == '.')
  1316.                         {
  1317.                                 *ptr = 0;
  1318.                                 break;
  1319.                         }
  1320.                 }
  1321.                 auto &hires_pcx = "h.pcx";
  1322.                 const size_t len_fname2 = std::distance(fb, ptr);
  1323.                 if (len_fname2 + sizeof(hires_pcx) < fname2a.size())
  1324.                 {
  1325.                         strcpy(ptr, hires_pcx);
  1326.                         if (!PHYSFSX_exists(fname2a.data(), 1))
  1327.                                 snprintf(fname2a.data(), fname2a.size(), "%s", fname);
  1328.                 }
  1329.         }
  1330.         br->background_name = fname2a;
  1331.         const auto fname2 = fname2a.data();
  1332.  
  1333.         pcx_result pcx_error;
  1334.         if ((!d_stricmp(fname2, "brief02.pcx") || !d_stricmp(fname2, "brief02h.pcx")) && cheats.baldguy &&
  1335.                 bald_guy_load("btexture.xxx", br->background, gr_palette) == pcx_result::SUCCESS)
  1336.         {
  1337.         }
  1338.         else if ((pcx_error = pcx_read_bitmap(fname2, br->background, gr_palette)) != pcx_result::SUCCESS)
  1339.         {
  1340.                 con_printf(CON_URGENT, "%s:%u: error: loading briefing screen <%s> failed: PCX load error: %s (%u)", __FILE__, __LINE__, fname2, pcx_errormsg(pcx_error), static_cast<unsigned>(pcx_error));
  1341.         }
  1342.  
  1343.         // Hack: Make sure black parts of robot are shown black
  1344.         if (MacPig && gr_palette[0].r == 63 &&
  1345.                 (!d_stricmp(fname2, "brief03.pcx") || !d_stricmp(fname2, "end01.pcx") ||
  1346.                 !d_stricmp(fname2, "brief03h.pcx") || !d_stricmp(fname2, "end01h.pcx")
  1347.                 ))
  1348.         {
  1349.                 swap_0_255(br->background);
  1350.                 gr_palette[0].r = gr_palette[0].g = gr_palette[0].b = 0;
  1351.                 gr_palette[255].r = gr_palette[255].g = gr_palette[255].b = 63;
  1352.         }
  1353.         show_fullscr(canvas, br->background);
  1354.         gr_palette_load(gr_palette);
  1355.  
  1356.         set_briefing_fontcolor();
  1357.  
  1358.         br->screen = std::make_unique<briefing_screen>(get_d1_briefing_screens(descent_hog_size)[br->cur_screen]);
  1359.         br->screen->text_ulx = rescale_x(canvas.cv_bitmap, br->screen->text_ulx);
  1360.         br->screen->text_uly = rescale_y(canvas.cv_bitmap, br->screen->text_uly);
  1361.         br->screen->text_width = rescale_x(canvas.cv_bitmap, br->screen->text_width);
  1362.         br->screen->text_height = rescale_y(canvas.cv_bitmap, br->screen->text_height);
  1363.         init_char_pos(br, br->screen->text_ulx, br->screen->text_uly);
  1364. #elif defined(DXX_BUILD_DESCENT_II)
  1365.         free_briefing_screen(br);
  1366.         const auto bndata = br->background_name.data();
  1367.         if (fname != bndata)
  1368.         {
  1369.                 br->background_name.back() = 0;
  1370.                 strncpy(bndata, fname, br->background_name.size() - 1);
  1371.         }
  1372.  
  1373.         pcx_result pcx_error;
  1374.         if ((pcx_error = pcx_read_bitmap(fname, br->background, gr_palette)) != pcx_result::SUCCESS)
  1375.         {
  1376.                 con_printf(CON_URGENT, "%s:%u: error: loading briefing screen <%s> failed: PCX load error: %s (%u)", __FILE__, __LINE__, fname, pcx_errormsg(pcx_error), static_cast<unsigned>(pcx_error));
  1377.                 return 0;
  1378.         }
  1379.         show_fullscr(canvas, br->background);
  1380.         if (EMULATING_D1 && !d_stricmp(fname, "brief03.pcx")) // HACK, FIXME: D1 missions should use their own palette (PALETTE.256), but texture replacements not complete
  1381.                 gr_use_palette_table("groupa.256");
  1382.  
  1383.         gr_palette_load(gr_palette);
  1384.  
  1385.         set_briefing_fontcolor(*br);
  1386.  
  1387.         if (EMULATING_D1)
  1388.         {
  1389.                 br->got_z = 1;
  1390.                 br->screen = std::make_unique<briefing_screen>(Briefing_screens[br->cur_screen]);
  1391.                 br->screen->text_ulx = rescale_x(canvas.cv_bitmap, br->screen->text_ulx);
  1392.                 br->screen->text_uly = rescale_y(canvas.cv_bitmap, br->screen->text_uly);
  1393.                 br->screen->text_width = rescale_x(canvas.cv_bitmap, br->screen->text_width);
  1394.                 br->screen->text_height = rescale_y(canvas.cv_bitmap, br->screen->text_height);
  1395.                 init_char_pos(br, br->screen->text_ulx, br->screen->text_uly);
  1396.         }
  1397.  
  1398. #endif
  1399.         return 1;
  1400. }
  1401.  
  1402. static void free_briefing_screen(briefing *br)
  1403. {
  1404.         br->background.reset();
  1405. #if defined(DXX_BUILD_DESCENT_II)
  1406.         if (br->robot_playing)
  1407.         {
  1408.                 DeInitRobotMovie(br->pMovie);
  1409.                 br->robot_playing=0;
  1410.         }
  1411. #endif
  1412.         br->robot_canv.reset();
  1413. #if defined(DXX_BUILD_DESCENT_II)
  1414.         br->printing_channel.reset();
  1415. #endif
  1416.         if (EMULATING_D1)
  1417.                 br->screen.reset();
  1418. }
  1419.  
  1420. static int new_briefing_screen(briefing *br, int first)
  1421. {
  1422.         br->new_screen = 0;
  1423.         const auto descent_hog_size = PHYSFSX_fsize("descent.hog");
  1424.         const auto num_d1_briefing_screens = (
  1425.                 (descent_hog_size == D1_SHAREWARE_MISSION_HOGSIZE || descent_hog_size == D1_SHAREWARE_10_MISSION_HOGSIZE)
  1426.                 ? std::size(D1_Briefing_screens_share)
  1427.                 : std::size(D1_Briefing_screens_full)
  1428.         );
  1429. #if defined(DXX_BUILD_DESCENT_I)
  1430.  
  1431.         if (!first)
  1432.                 br->cur_screen++;
  1433.  
  1434.         auto &&d1_briefing_screens = get_d1_briefing_screens(descent_hog_size);
  1435.         while (br->cur_screen < num_d1_briefing_screens && d1_briefing_screens[br->cur_screen].level_num != br->level_num)
  1436.         {
  1437.                 br->cur_screen++;
  1438.                 if (br->cur_screen == num_d1_briefing_screens && br->level_num == 0)
  1439.                 {
  1440.                         // Showed the pre-game briefing, now show level 1 briefing
  1441.                         br->level_num++;
  1442.                         br->cur_screen = 0;
  1443.                 }
  1444.         }
  1445.  
  1446.         if (br->cur_screen == num_d1_briefing_screens)
  1447.                 return 0;               // finished
  1448.  
  1449.         if (!load_briefing_screen(*grd_curcanv, br, d1_briefing_screens[br->cur_screen].bs_name))
  1450.                 return 0;
  1451.  
  1452.         br->message = get_briefing_message(br, d1_briefing_screens[br->cur_screen].message_num);
  1453. #elif defined(DXX_BUILD_DESCENT_II)
  1454.         br->got_z = 0;
  1455.  
  1456.         if (EMULATING_D1)
  1457.         {
  1458.                 auto &&d1_briefing_screens = get_d1_briefing_screens(descent_hog_size);
  1459.                 if (!first)
  1460.                         br->cur_screen++;
  1461.                 else
  1462.                         for (int i = 0; i < num_d1_briefing_screens; i++)
  1463.                                 Briefing_screens[i] = d1_briefing_screens[i];
  1464.  
  1465.                 while (br->cur_screen < num_d1_briefing_screens && Briefing_screens[br->cur_screen].level_num != br->level_num)
  1466.                 {
  1467.                         br->cur_screen++;
  1468.                         if (br->cur_screen == num_d1_briefing_screens && br->level_num == 0)
  1469.                         {
  1470.                                 // Showed the pre-game briefing, now show level 1 briefing
  1471.                                 br->level_num++;
  1472.                                 br->cur_screen = 0;
  1473.                         }
  1474.                 }
  1475.  
  1476.                 if (br->cur_screen == num_d1_briefing_screens)
  1477.                         return 0;               // finished
  1478.  
  1479.                 if (!load_briefing_screen(*grd_curcanv, br, Briefing_screens[br->cur_screen].bs_name))
  1480.                         return 0;
  1481.         }
  1482.         else if (first)
  1483.         {
  1484.                 br->cur_screen = br->level_num;
  1485.                 br->screen.reset(&Briefing_screens[0]);
  1486.                 init_char_pos(br, br->screen->text_ulx, br->screen->text_uly-(8*(1+HIRESMODE)));
  1487.         }
  1488.         else
  1489.                 return 0;       // finished
  1490.  
  1491.         br->message = get_briefing_message(br, EMULATING_D1 ? Briefing_screens[br->cur_screen].message_num : br->cur_screen);
  1492.         br->printing_channel.reset();
  1493.         br->dumb_adjust = 0;
  1494.         br->line_adjustment = 1;
  1495.         br->chattering = 0;
  1496.         br->robot_playing=0;
  1497. #endif
  1498.  
  1499.         if (br->message==NULL)
  1500.                 return 0;
  1501.  
  1502.         Current_color = &Briefing_text_colors.front();
  1503.         br->streamcount = 0;
  1504.         br->tab_stop = 0;
  1505.         br->flashing_cursor = 0;
  1506.         br->new_page = 0;
  1507.         br->start_time = 0;
  1508.         br->delay_count = KEY_DELAY_DEFAULT;
  1509.         br->robot_num = -1;
  1510.         br->bitmap_name[0] = 0;
  1511.         br->guy_bitmap.reset();
  1512.         br->prev_ch = -1;
  1513.  
  1514. #if defined(DXX_BUILD_DESCENT_II)
  1515.         if (songs_is_playing() == -1 && !br->hum_channel)
  1516.                 br->hum_channel.reset(digi_start_sound(digi_xlat_sound(SOUND_BRIEFING_HUM), F1_0/2, 0xFFFF/2, 1, -1, -1, sound_object_none));
  1517. #endif
  1518.  
  1519.         return 1;
  1520. }
  1521.  
  1522.  
  1523. //-----------------------------------------------------------------------------
  1524. static window_event_result briefing_handler(window *, const d_event &event, briefing *br)
  1525. {
  1526.         window_event_result result;
  1527.  
  1528.         switch (event.type)
  1529.         {
  1530.                 case EVENT_WINDOW_ACTIVATED:
  1531.                 case EVENT_WINDOW_DEACTIVATED:
  1532.                         key_flush();
  1533.                         break;
  1534.  
  1535.                 case EVENT_MOUSE_BUTTON_DOWN:
  1536.                         if (event_mouse_get_button(event) == 0)
  1537.                         {
  1538.                                 if (br->new_screen)
  1539.                                 {
  1540.                                         if (!new_briefing_screen(br, 0))
  1541.                                         {
  1542.                                                 return window_event_result::close;
  1543.                                         }
  1544.                                 }
  1545.                                 else if (br->new_page)
  1546.                                         init_new_page(br);
  1547.                                 else
  1548.                                         br->delay_count = 0;
  1549.                                 return window_event_result::handled;
  1550.                         }
  1551.                         break;
  1552.  
  1553. #if DXX_MAX_JOYSTICKS // Pierre-Marie Baty -- missing preprocessor condition
  1554.                 case EVENT_JOYSTICK_BUTTON_DOWN:
  1555.                         // using joy_translate_menu_key doesn't work here for unclear
  1556.                         // reasons, so we build a reasonable facsimile right here
  1557.                         if (event_joystick_get_button(event) == 1)
  1558.                                 return window_event_result::close;
  1559.                         if (br->new_screen)
  1560.                         {
  1561.                                 if (!new_briefing_screen(br, 0))
  1562.                                 {
  1563.                                         return window_event_result::close;
  1564.                                 }
  1565.                         }
  1566.                         else if (br->new_page)
  1567.                                 init_new_page(br);
  1568.                         else
  1569.                                 br->delay_count = 0;
  1570.                         return window_event_result::handled;
  1571. #endif
  1572.  
  1573.                 case EVENT_KEY_COMMAND:
  1574.                 {
  1575.                         int key = event_key_get(event);
  1576.  
  1577.                         switch (key)
  1578.                         {
  1579. #if defined(DXX_BUILD_DESCENT_I)
  1580.                                 case KEY_ALTED + KEY_B: // B - ALTED... BALT... BALD... get it?
  1581.                                         cheats.baldguy = !cheats.baldguy;
  1582.                                         break;
  1583. #endif
  1584.                                 case KEY_ESC:
  1585.                                         return window_event_result::close;
  1586.                                 case KEY_SPACEBAR:
  1587.                                 case KEY_ENTER:
  1588.                                         br->delay_count = 0;
  1589.                                         DXX_BOOST_FALLTHROUGH;
  1590.                                 default:
  1591.                                         if ((result = call_default_handler(event)) != window_event_result::ignored)
  1592.                                                 return result;
  1593.                                         else if (br->new_screen)
  1594.                                         {
  1595.                                                 if (!new_briefing_screen(br, 0))
  1596.                                                 {
  1597.                                                         return window_event_result::close;
  1598.                                                 }
  1599.                                         }
  1600.                                         else if (br->new_page)
  1601.                                                 init_new_page(br);
  1602.                                         break;
  1603.                         }
  1604.                         break;
  1605.                 }
  1606.  
  1607.                 case EVENT_WINDOW_DRAW:
  1608.                 {
  1609.                         gr_set_default_canvas();
  1610.                         auto &canvas = *grd_curcanv;
  1611.  
  1612.                         timer_delay2(50);
  1613.  
  1614.                         if (!(br->new_screen || br->new_page))
  1615.                                 while (!briefing_process_char(canvas, br) && !br->delay_count)
  1616.                                 {
  1617.                                         check_text_pos(br);
  1618.                                         if (br->new_page)
  1619.                                                 break;
  1620.                                 }
  1621.                         check_text_pos(br);
  1622.  
  1623.                         if (br->background.bm_data)
  1624.                                 show_fullscr(canvas, br->background);
  1625.  
  1626.                         if (br->guy_bitmap.bm_data)
  1627.                                 show_briefing_bitmap(canvas, &br->guy_bitmap);
  1628.                         if (br->bitmap_name[0] != 0)
  1629.                                 show_animated_bitmap(canvas, br);
  1630. #if defined(DXX_BUILD_DESCENT_II)
  1631.                         if (br->robot_playing)
  1632.                                 RotateRobot(br->pMovie);
  1633. #endif
  1634.                         if (br->robot_num != -1)
  1635.                                 show_spinning_robot_frame(br, br->robot_num);
  1636.  
  1637.                         auto &game_font = *GAME_FONT;
  1638.  
  1639.                         gr_set_fontcolor(canvas, *Current_color, -1);
  1640.                         {
  1641.                                 unsigned lastcolor = ~0u;
  1642.                                 range_for (const auto b, partial_const_range(br->messagestream, br->streamcount))
  1643.                                         redraw_messagestream(canvas, game_font, b, lastcolor);
  1644.                         }
  1645.  
  1646.                         if (br->new_page || br->new_screen)
  1647.                                 flash_cursor(canvas, game_font, br, br->flashing_cursor);
  1648.                         else if (br->flashing_cursor)
  1649.                                 gr_string(canvas, game_font, br->text_x, br->text_y, "_");
  1650.                         break;
  1651.                 }
  1652.                 case EVENT_WINDOW_CLOSE:
  1653.                         free_briefing_screen(br);
  1654. #if defined(DXX_BUILD_DESCENT_II)
  1655.                         br->hum_channel.reset();
  1656. #endif
  1657.                         break;
  1658.  
  1659.                 default:
  1660.                         break;
  1661.         }
  1662.         return window_event_result::ignored;
  1663. }
  1664.  
  1665. void do_briefing_screens(const d_fname &filename, int level_num)
  1666. {
  1667.         if (!*static_cast<const char *>(filename))
  1668.                 return;
  1669.  
  1670.         auto br = std::make_unique<briefing>();
  1671.         briefing_init(br.get(), level_num);
  1672.  
  1673.         if (!load_screen_text(filename, br->text))
  1674.         {
  1675.                 return;
  1676.         }
  1677.  
  1678.         const auto wind = window_create(grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT, briefing_handler, br.get());
  1679.         if (!wind)
  1680.         {
  1681.                 return;
  1682.         }
  1683.  
  1684. #if defined(DXX_BUILD_DESCENT_II)
  1685.         if (!(EMULATING_D1 || is_SHAREWARE || is_MAC_SHARE || is_D2_OEM || !PLAYING_BUILTIN_MISSION))
  1686.                 songs_stop_all();
  1687.         else
  1688. #endif
  1689.         {
  1690.                 if ((songs_is_playing() != SONG_BRIEFING) && (songs_is_playing() != SONG_ENDGAME))
  1691.                         songs_play_song( SONG_BRIEFING, 1 );
  1692.         }
  1693.  
  1694. #if defined(DXX_BUILD_DESCENT_I)
  1695.         set_screen_mode( SCREEN_MENU );
  1696. #elif defined(DXX_BUILD_DESCENT_II)
  1697.         // set screen correctly for robot movies
  1698.         set_screen_mode( SCREEN_MOVIE );
  1699. #endif
  1700.  
  1701.         gr_set_default_canvas();
  1702.  
  1703.         if (!new_briefing_screen(br.get(), 1))
  1704.         {
  1705.                 window_close(wind);
  1706.                 return;
  1707.         }
  1708.  
  1709.         // Stay where we are in the stack frame until briefing done
  1710.         // Too complicated otherwise
  1711.         event_process_all();
  1712. }
  1713.  
  1714. void do_end_briefing_screens(const d_fname &filename)
  1715. {
  1716.         int level_num_screen = Current_level_num, showorder = 0;
  1717.  
  1718.         if (!*static_cast<const char *>(filename))
  1719.                 return; // no filename, no ending
  1720.  
  1721.         if (EMULATING_D1)
  1722.         {
  1723.                 unsigned song;
  1724.                 if (d_stricmp(filename, BIMD1_ENDING_FILE_OEM) == 0)
  1725.                 {
  1726.                         song = SONG_ENDGAME;
  1727.                         level_num_screen = ENDING_LEVEL_NUM_OEMSHARE;
  1728. #if defined(DXX_BUILD_DESCENT_I)
  1729.                         showorder = 1;
  1730. #endif
  1731.                 }
  1732.                 else if (d_stricmp(filename, BIMD1_ENDING_FILE_SHARE) == 0)
  1733.                 {
  1734.                         song = SONG_BRIEFING;
  1735.                         level_num_screen = ENDING_LEVEL_NUM_OEMSHARE;
  1736. #if defined(DXX_BUILD_DESCENT_I)
  1737.                         showorder = 1;
  1738. #endif
  1739.                 }
  1740.                 else
  1741.                 {
  1742.                         song = SONG_ENDGAME;
  1743.                         level_num_screen = ENDING_LEVEL_NUM_REGISTER;
  1744.                 }
  1745.                 songs_play_song(song, 1);
  1746.         }
  1747. #if defined(DXX_BUILD_DESCENT_II)
  1748.         else if (PLAYING_BUILTIN_MISSION)
  1749.         {
  1750.                 unsigned song;
  1751.                 if ((d_stricmp(filename, BIMD2_ENDING_FILE_OEM) == 0 && (song = SONG_TITLE, true)) ||
  1752.                         (d_stricmp(filename, BIMD2_ENDING_FILE_SHARE) == 0 && (song = SONG_ENDGAME, true)))
  1753.                 {
  1754.                         songs_play_song(song, 1);
  1755.                         level_num_screen = 1;
  1756.                         showorder = 1;
  1757.                 }
  1758.         }
  1759.         else
  1760.         {
  1761.                 songs_play_song( SONG_ENDGAME, 1 );
  1762.                 level_num_screen = Last_level + 1;
  1763.         }
  1764. #endif
  1765.  
  1766.         do_briefing_screens(filename, level_num_screen);
  1767.         if (showorder)
  1768.                 show_order_form();
  1769. }
  1770.  
  1771. }
  1772.