Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
}