1 /**
2 * \file message.c
3 * \brief Message handling
4 *
5 * Copyright (c) 2007 Elly, Andi Sidwell
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 */
17
18 #include "z-virt.h"
19 #include "z-color.h"
20 #include "z-util.h"
21 #include "message.h"
22 #include "game-event.h"
23 #include "option.h"
24 #include "init.h"
25 #include "player.h"
26
27 typedef struct _message_t
28 {
29 char *str;
30 struct _message_t *newer;
31 struct _message_t *older;
32 u16b type;
33 u16b count;
34 } message_t;
35
36 typedef struct _msgcolor_t
37 {
38 u16b type;
39 byte color;
40 struct _msgcolor_t *next;
41 } msgcolor_t;
42
43 typedef struct _msgqueue_t
44 {
45 message_t *head;
46 message_t *tail;
47 msgcolor_t *colors;
48 u32b count;
49 u32b max;
50 } msgqueue_t;
51
52 static msgqueue_t *messages = NULL;
53
54 /**
55 * ------------------------------------------------------------------------
56 * Functions operating on the entire list
57 * ------------------------------------------------------------------------ */
58 /**
59 * Initialise the messages package. Should be called before using any other
60 * functions in the package.
61 */
messages_init(void)62 void messages_init(void)
63 {
64 messages = mem_zalloc(sizeof(msgqueue_t));
65 messages->max = 2048;
66 }
67
68 /**
69 * Free the message package.
70 */
messages_free(void)71 void messages_free(void)
72 {
73 msgcolor_t *c = messages->colors;
74 msgcolor_t *nextc;
75 message_t *m = messages->head;
76 message_t *nextm;
77
78 while (m) {
79 nextm = m->older;
80 mem_free(m->str);
81 mem_free(m);
82 m = nextm;
83 }
84
85 while (c) {
86 nextc = c->next;
87 mem_free(c);
88 c = nextc;
89 }
90
91 mem_free(messages);
92 }
93
94 /**
95 * Return the current number of messages stored.
96 */
messages_num(void)97 u16b messages_num(void)
98 {
99 return messages->count;
100 }
101
102 /**
103 * ------------------------------------------------------------------------
104 * Functions for individual messages
105 * ------------------------------------------------------------------------ */
106 /**
107 * Save a new message into the memory buffer, with text `str` and type `type`.
108 * The type should be one of the MSG_ constants defined in message.h.
109 *
110 * The new message may not be saved if it is identical to the one saved before
111 * it, in which case the "count" of the message will be increased instead.
112 * This count can be fetched using the message_count() function.
113 */
message_add(const char * str,u16b type)114 void message_add(const char *str, u16b type)
115 {
116 message_t *m;
117
118 if (messages->head &&
119 messages->head->type == type &&
120 !strcmp(messages->head->str, str) &&
121 messages->head->count != (u16b)-1) {
122 messages->head->count++;
123 return;
124 }
125
126 m = mem_zalloc(sizeof(message_t));
127 m->str = string_make(str);
128 m->type = type;
129 m->count = 1;
130 m->older = messages->head;
131
132 if (messages->head)
133 messages->head->newer = m;
134
135 messages->head = m;
136 messages->count++;
137
138 if (!messages->tail)
139 messages->tail = m;
140
141 if (messages->count > messages->max) {
142 message_t *old_tail = messages->tail;
143
144 messages->tail = old_tail->newer;
145 messages->tail->older = NULL;
146 mem_free(old_tail->str);
147 mem_free(old_tail);
148 messages->count--;
149 }
150 }
151
152 /**
153 * Returns the message of age `age`.
154 */
message_get(u16b age)155 static message_t *message_get(u16b age)
156 {
157 message_t *m = messages->head;
158
159 while (m && age) {
160 age--;
161 m = m->older;
162 }
163
164 return m;
165 }
166
167
168 /**
169 * Returns the text of the message of age `age`. The age of the most recently
170 * saved message is 0, the one before that is of age 1, etc.
171 *
172 * Returns the empty string if the no messages of the age specified are
173 * available.
174 */
message_str(u16b age)175 const char *message_str(u16b age)
176 {
177 message_t *m = message_get(age);
178 return (m ? m->str : "");
179 }
180
181 /**
182 * Returns the number of times the message of age `age` was saved. The age of
183 * the most recently saved message is 0, the one before that is of age 1, etc.
184 *
185 * In other words, if message_add() was called five times, one after the other,
186 * with the message "The orc sets your hair on fire.", then the text will only
187 * have one age (age = 0), but will have a count of 5.
188 */
message_count(u16b age)189 u16b message_count(u16b age)
190 {
191 message_t *m = message_get(age);
192 return (m ? m->count : 0);
193 }
194
195 /**
196 * Returns the type of the message of age `age`. The age of the most recently
197 * saved message is 0, the one before that is of age 1, etc.
198 *
199 * The type is one of the MSG_ constants, defined in message.h.
200 */
message_type(u16b age)201 u16b message_type(u16b age)
202 {
203 message_t *m = message_get(age);
204 return (m ? m->type : 0);
205 }
206
207 /**
208 * Returns the display colour of the message memorised `age` messages ago.
209 * (i.e. age = 0 represents the last memorised message, age = 1 is the one
210 * before that, etc).
211 */
message_color(u16b age)212 byte message_color(u16b age)
213 {
214 message_t *m = message_get(age);
215 return (m ? message_type_color(m->type) : COLOUR_WHITE);
216 }
217
218
219 /**
220 * ------------------------------------------------------------------------
221 * Message-color functions
222 * ------------------------------------------------------------------------ */
223 /**
224 * Defines the color `color` for the message type `type`.
225 */
message_color_define(u16b type,byte color)226 void message_color_define(u16b type, byte color)
227 {
228 msgcolor_t *mc;
229
230 if (!messages->colors)
231 {
232 messages->colors = mem_zalloc(sizeof(msgcolor_t));
233 messages->colors->type = type;
234 messages->colors->color = color;
235 return;
236 }
237
238 mc = messages->colors;
239 while (1)
240 {
241 if (mc->type == type)
242 {
243 mc->color = color;
244 break;
245 }
246 if (! mc->next) {
247 mc->next = mem_zalloc(sizeof(msgcolor_t));
248 mc->next->type = type;
249 mc->next->color = color;
250 break;
251 }
252 mc = mc->next;
253 }
254 }
255
256 /**
257 * Returns the colour for the message type `type`.
258 */
message_type_color(u16b type)259 byte message_type_color(u16b type)
260 {
261 msgcolor_t *mc;
262 byte color = COLOUR_WHITE;
263
264 if (messages)
265 {
266 mc = messages->colors;
267
268 while (mc && mc->type != type)
269 mc = mc->next;
270
271 if (mc && (mc->color != COLOUR_DARK))
272 color = mc->color;
273 }
274
275 return color;
276 }
277
278 /**
279 * Return the MSG_ flag that matches the given string. This does not handle
280 * SOUND_MAX.
281 *
282 * \param name is a string that contains the name of a flag or a number.
283 * \return The MSG_ flag that matches the given name.
284 */
message_lookup_by_name(const char * name)285 int message_lookup_by_name(const char *name)
286 {
287 static const char *message_names[] = {
288 #define MSG(x, s) #x,
289 #include "list-message.h"
290 #undef MSG
291 };
292 size_t i;
293 unsigned int number;
294
295 if (sscanf(name, "%u", &number) == 1)
296 return (number < MSG_MAX) ? (int)number : -1;
297
298 for (i = 0; i < N_ELEMENTS(message_names); i++) {
299 if (my_stricmp(name, message_names[i]) == 0)
300 return (int)i;
301 }
302
303 return -1;
304 }
305
306 /**
307 * Return the MSG_ flag that matches the given sound event name.
308 *
309 * \param name is the sound name from sound.cfg.
310 * \return The MSG_ flag for the corresponding sound.
311 */
message_lookup_by_sound_name(const char * name)312 int message_lookup_by_sound_name(const char *name)
313 {
314 static const char *sound_names[] = {
315 #define MSG(x, s) s,
316 #include "list-message.h"
317 #undef MSG
318 };
319 size_t i;
320
321 /* Exclude MSG_MAX since it has NULL for the sound's name. */
322 for (i = 0; i < N_ELEMENTS(sound_names) - 1; i++) {
323 if (my_stricmp(name, sound_names[i]) == 0)
324 return (int)i;
325 }
326
327 return MSG_GENERIC;
328 }
329
330 /**
331 * Return the sound name for the given message.
332 *
333 * \param message is the MSG_ flag to find.
334 * \return The sound.cfg sound name.
335 */
message_sound_name(int message)336 const char *message_sound_name(int message)
337 {
338 static const char *sound_names[] = {
339 #define MSG(x, s) s,
340 #include "list-message.h"
341 #undef MSG
342 };
343
344 if (message < MSG_GENERIC || message >= MSG_MAX)
345 return NULL;
346
347 return sound_names[message];
348 }
349
350 /**
351 * Make a noise, without a message. Sound modules hook into this event.
352 *
353 * \param type MSG_* constant for the sound type
354 */
sound(int type)355 void sound(int type)
356 {
357 /* No sound */
358 if (!OPT(player, use_sound)) return;
359
360 /* Dispatch */
361 event_signal_message(EVENT_SOUND, type, NULL);
362 }
363
364 /**
365 * Clear everything, display a formatted message, ring the system bell.
366 *
367 * \param fmt Format string
368 */
bell(const char * fmt,...)369 void bell(const char *fmt, ...)
370 {
371 va_list vp;
372
373 char buf[1024];
374
375 /* Begin the Varargs Stuff */
376 va_start(vp, fmt);
377
378 /* Format the args, save the length */
379 (void)vstrnfmt(buf, sizeof(buf), fmt, vp);
380
381 /* End the Varargs Stuff */
382 va_end(vp);
383
384 /* Fail if messages not loaded */
385 if (!messages) return;
386
387 /* Add to message log */
388 message_add(buf, MSG_BELL);
389
390 /* Send bell event */
391 event_signal_message(EVENT_BELL, MSG_BELL, buf);
392 }
393
394 /**
395 * Display a formatted message.
396 *
397 * NB: Never call this function directly with a string read in from a
398 * file, because it may contain format characters and crash the game.
399 * Always use msg("%s", string) in those situations.
400 *
401 * \param fmt Format string
402 */
msg(const char * fmt,...)403 void msg(const char *fmt, ...)
404 {
405 va_list vp;
406
407 char buf[1024];
408
409 /* Begin the Varargs Stuff */
410 va_start(vp, fmt);
411
412 /* Format the args, save the length */
413 (void)vstrnfmt(buf, sizeof(buf), fmt, vp);
414
415 /* End the Varargs Stuff */
416 va_end(vp);
417
418 /* Fail if messages not loaded */
419 if (!messages) return;
420
421 /* Add to message log */
422 message_add(buf, MSG_GENERIC);
423
424 /* Send refresh event */
425 event_signal_message(EVENT_MESSAGE, MSG_GENERIC, buf);
426
427 }
428
429 /**
430 * Display a formatted message with a given type, making a sound
431 * relevant to the message tyoe.
432 *
433 * \param type MSG_ constant
434 * \param fmt Format string
435 */
msgt(unsigned int type,const char * fmt,...)436 void msgt(unsigned int type, const char *fmt, ...)
437 {
438 va_list vp;
439 char buf[1024];
440 va_start(vp, fmt);
441 vstrnfmt(buf, sizeof(buf), fmt, vp);
442 va_end(vp);
443
444 /* Fail if messages not loaded */
445 if (!messages) return;
446
447 /* Add to message log */
448 message_add(buf, type);
449
450 /* Send refresh event */
451 sound(type);
452 event_signal_message(EVENT_MESSAGE, type, buf);
453 }
454
455
456 struct init_module messages_module = {
457 .name = "messages",
458 .init = messages_init,
459 .cleanup = messages_free
460 };
461