Subversion Repositories Games.Descent

Rev

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

  1. /*
  2.  * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
  3.  * It is copyright by its individual contributors, as recorded in the
  4.  * project's Git history.  See COPYING.txt at the top level for license
  5.  * terms and a link to the Git history.
  6.  */
  7. /*
  8.  *
  9.  * Routines for displaying HUD messages...
  10.  *
  11.  */
  12.  
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16.  
  17. #include "hudmsg.h"
  18. #include "pstypes.h"
  19. #include "u_mem.h"
  20. #include "strutil.h"
  21. #include "console.h"
  22. #include "object.h"
  23. #include "inferno.h"
  24. #include "game.h"
  25. #include "screens.h"
  26. #include "gauges.h"
  27. #include "physics.h"
  28. #include "dxxerror.h"
  29. #include "menu.h"           // For the font.
  30. #include "collide.h"
  31. #include "newdemo.h"
  32. #include "player.h"
  33. #include "gamefont.h"
  34. #include "screens.h"
  35. #include "text.h"
  36. #include "laser.h"
  37. #include "args.h"
  38. #include "playsave.h"
  39. #include "countarray.h"
  40.  
  41. namespace {
  42. constexpr std::integral_constant<unsigned, 150> HUD_MESSAGE_LENGTH{};
  43.  
  44. struct hudmsg
  45. {
  46.         fix time;
  47.         ntstring<HUD_MESSAGE_LENGTH> message;
  48.         template <typename M>
  49.                 hudmsg(const fix& t, M &&m) :
  50.                 time(t)
  51.         {
  52.                 message.copy_if(m);
  53.         }
  54. };
  55.  
  56. struct hudmsg_array_t : public count_array_t<hudmsg, HUD_MAX_NUM_STOR> {};
  57. }
  58.  
  59. static hudmsg_array_t HUD_messages;
  60.  
  61.  
  62. int HUD_toolong = 0;
  63. static int HUD_color = -1;
  64. static int HUD_init_message_literal_worth_showing(int class_flag, const char *message);
  65.  
  66. void HUD_clear_messages()
  67. {
  68.         HUD_messages.clear();
  69.         HUD_toolong = 0;
  70.         HUD_color = -1;
  71. }
  72.  
  73. namespace dsx {
  74. // ----------------------------------------------------------------------------
  75. //      Writes a message on the HUD and checks its timer.
  76. void HUD_render_message_frame(grs_canvas &canvas)
  77. {
  78.         int y;
  79.  
  80.         HUD_toolong = 0;
  81.  
  82.         if (HUD_messages.empty())
  83.                 return;
  84.  
  85.         auto expired = [](hudmsg &h) -> int {
  86.                 if (h.time <= FrameTime)
  87.                         return 1;
  88.                 h.time -= FrameTime;
  89.                 return 0;
  90.         };
  91.         HUD_messages.erase_if(expired);
  92.  
  93.         // display last $HUD_MAX_NUM_DISP messages on the list
  94.         if (!HUD_messages.empty())
  95.         {
  96.                 if (HUD_color == -1)
  97.                         HUD_color = BM_XRGB(0,28,0);
  98.                 gr_set_fontcolor(canvas, HUD_color, -1);
  99.                 y = FSPACY(1);
  100.  
  101.                 auto &game_font = *GAME_FONT;
  102.                 const auto &&line_spacing = LINE_SPACING(game_font, game_font);
  103. #if defined(DXX_BUILD_DESCENT_II)
  104.                 if (PlayerCfg.GuidedInBigWindow &&
  105.                         LevelUniqueObjectState.Guided_missile.get_player_active_guided_missile(LevelUniqueObjectState.get_objects().vmptr, Player_num) != nullptr)
  106.                         y += line_spacing;
  107. #endif
  108.  
  109.                 hudmsg_array_t::iterator i, e = HUD_messages.end();
  110.                 if (HUD_messages.size() < HUD_MAX_NUM_DISP)
  111.                         i = HUD_messages.begin();
  112.                 else
  113.                         i = e - HUD_MAX_NUM_DISP;
  114.                 if (strlen(i->message) > 38)
  115.                         HUD_toolong = 1;
  116.                 for (; i != e; ++i )    {
  117.                         gr_string(canvas, game_font, 0x8000, y, &i->message[0]);
  118.                         y += line_spacing;
  119.                 }
  120.         }
  121. }
  122. }
  123.  
  124. static int is_worth_showing(int class_flag)
  125. {
  126.         if (PlayerCfg.NoRedundancy && (class_flag & HM_REDUNDANT))
  127.                 return 0;
  128.  
  129.         if (PlayerCfg.MultiMessages && (Game_mode & GM_MULTI) && !(class_flag & HM_MULTI))
  130.                 return 0;
  131.         return 1;
  132. }
  133.  
  134. // Call to flash a message on the HUD.  Returns true if message drawn.
  135. // (message might not be drawn if previous message was same)
  136. int HUD_init_message_va(int class_flag, const char * format, va_list args)
  137. {
  138.         if (!is_worth_showing(class_flag))
  139.                 return 0;
  140.  
  141. #ifndef macintosh
  142.         char message[HUD_MESSAGE_LENGTH+1] = "";
  143. #else
  144.         char message[1024] = "";
  145. #endif
  146.  
  147. #ifndef macintosh
  148.         vsnprintf(message, sizeof(char)*HUD_MESSAGE_LENGTH, format, args);
  149. #else
  150.         vsprintf(message, format, args);
  151. #endif
  152.         int r = HUD_init_message_literal_worth_showing(class_flag, message);
  153.         if (r)
  154.                 con_puts(CON_HUD, message);
  155.         return r;
  156. }
  157.  
  158.  
  159. static int HUD_init_message_literal_worth_showing(int class_flag, const char *message)
  160. {
  161.         // check if message is already in list and bail out if so
  162.         if (!HUD_messages.empty())
  163.         {
  164.                 hudmsg_array_t::iterator i, e = HUD_messages.end();
  165.                 // if "normal" message, only check if it's the same at the most recent one, if marked as "may duplicate" check whole list
  166.                 if (class_flag & HM_MAYDUPL)
  167.                         i = HUD_messages.begin();
  168.                 else
  169.                         i = e - 1;
  170.                 for (; i != e; ++i)
  171.                 {
  172.                         if (!d_stricmp(message, i->message))
  173.                         {
  174.                                 i->time = F1_0*2; // keep redundant message in list
  175.                                 if (std::distance(i, e) < HUD_MAX_NUM_DISP) // if redundant message on display, update them all
  176.                                 {
  177.                                         if (HUD_messages.size() < HUD_MAX_NUM_DISP)
  178.                                                 i = HUD_messages.begin();
  179.                                         else
  180.                                                 i = HUD_messages.end() - HUD_MAX_NUM_DISP;
  181.                                         for (unsigned j = 1; i != e; ++i, j++)
  182.                                                 i->time = F1_0*(j*2);
  183.                                 }
  184.                                 return 0;
  185.                         }
  186.                 }
  187.         }
  188.  
  189.         if (HUD_messages.size() >= HUD_MAX_NUM_STOR)
  190.         {
  191.                 std::move(HUD_messages.begin() + 1, HUD_messages.end(), HUD_messages.begin());
  192.                 HUD_messages.pop_back();
  193.         }
  194.         fix t;
  195.         if (HUD_messages.size() + 1 < HUD_MAX_NUM_DISP)
  196.                 t = F1_0*3; // one message - display 3 secs
  197.         else
  198.         {
  199.                 hudmsg_array_t::iterator e = HUD_messages.end(), i = e - HUD_MAX_NUM_DISP;
  200.                 for (unsigned j = 1; ++i != e; j++) // multiple messages - display 2 seconds each
  201.                         i->time = F1_0*(j*2);
  202.                 t = F1_0 * ((HUD_MAX_NUM_DISP + 1) * 2);
  203.         }
  204.         HUD_messages.emplace_back(t, message);
  205.  
  206.         if (HUD_color == -1)
  207.                 HUD_color = BM_XRGB(0,28,0);
  208.  
  209.         if (Newdemo_state == ND_STATE_RECORDING )
  210.                 newdemo_record_hud_message( message );
  211.  
  212.         return 1;
  213. }
  214.  
  215. int (HUD_init_message)(int class_flag, const char * format, ... )
  216. {
  217.         int ret;
  218.         va_list args;
  219.  
  220.         va_start(args, format);
  221.         ret = HUD_init_message_va(class_flag, format, args);
  222.         va_end(args);
  223.  
  224.         return ret;
  225. }
  226.  
  227. int HUD_init_message_literal(int class_flag, const char *str)
  228. {
  229.         if (!is_worth_showing(class_flag))
  230.                 return 0;
  231.         int r = HUD_init_message_literal_worth_showing(class_flag, str);
  232.         if (r)
  233.                 con_puts(CON_HUD, str);
  234.         return r;
  235. }
  236.  
  237. void player_dead_message(grs_canvas &canvas)
  238. {
  239.         if (Player_dead_state == player_dead_state::exploded)
  240.         {
  241.                 if (get_local_player().lives == 1)
  242.                 {
  243.                         int x, y, w, h;
  244.                         auto &huge_font = *HUGE_FONT;
  245.                         gr_get_string_size(huge_font, TXT_GAME_OVER, &w, &h, nullptr);
  246.                         const int gw = w;
  247.                         const int gh = h;
  248.                         w += 20;
  249.                         h += 8;
  250.                         x = (canvas.cv_bitmap.bm_w - w ) / 2;
  251.                         y = (canvas.cv_bitmap.bm_h - h ) / 2;
  252.                
  253.                         gr_settransblend(canvas, 14, gr_blend::normal);
  254.                         const uint8_t color = BM_XRGB(0, 0, 0);
  255.                         gr_rect(canvas, x, y, x + w, y + h, color);
  256.                         gr_settransblend(canvas, GR_FADE_OFF, gr_blend::normal);
  257.                
  258.                         gr_string(canvas, huge_font, 0x8000, (canvas.cv_bitmap.bm_h - h) / 2 + h / 8, TXT_GAME_OVER, gw, gh);
  259.                 }
  260.        
  261.                 if (HUD_color == -1)
  262.                         HUD_color = BM_XRGB(0,28,0);
  263.                 gr_set_fontcolor(canvas, HUD_color, -1);
  264.                 auto &game_font = *GAME_FONT;
  265.                 gr_string(canvas, game_font, 0x8000, canvas.cv_bitmap.bm_h - LINE_SPACING(game_font, game_font), PlayerCfg.RespawnMode == RespawnPress::Any ? TXT_PRESS_ANY_KEY : "Press fire key or button to continue...");
  266.         }
  267. }
  268.