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