1 #include <stdlib.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <time.h>
5 #include "text.h"
6 #include "achievements.h"
7 #include "actors.h"
8 #include "asc.h"
9 #include "books.h"
10 #include "buddy.h"
11 #include "chat.h"
12 #include "console.h"
13 #include "consolewin.h"
14 #include "elconfig.h"
15 #include "errors.h"
16 #include "filter.h"
17 #include "gl_init.h"
18 #include "hud_misc_window.h"
19 #include "highlight.h"
20 #include "init.h"
21 #include "items.h"
22 #include "knowledge.h"
23 #include "lights.h"
24 #include "main.h"
25 #include "misc.h"
26 #include "multiplayer.h"
27 #include "password_manager.h"
28 #include "paste.h"
29 #include "pm_log.h"
30 #include "translate.h"
31 #include "astrology.h"
32 #include "url.h"
33 #include "counters.h"
34 #include "io/elpathwrapper.h"
35 #include "spells.h"
36 #include "serverpopup.h"
37 #include "sky.h"
38 #include "sound.h"
39 #include "trade_log.h"
40 #include "actor_scripts.h"
41 #include "emotes.h"
42 
43 int emote_filter=0;
44 int summoning_filter=0;
45 
46 text_message display_text_buffer[DISPLAY_TEXT_BUFFER_SIZE];
47 int last_message = -1;
48 Uint8 current_filter = FILTER_ALL;
49 
50 text_message input_text_line;
51 
52 char last_pm_from[32];
53 
54 static Uint32 last_server_message_time;
55 
56 int show_timestamp = 0;
57 
58 int dark_channeltext = 0;
59 
60 /* Impliment the glow perk hud indicator state. */
61 static int glow_perk_check_state = 0;
62 static int glow_perk_active = 0;
63 static int glow_perk_unavailable = 1;
64 static Uint32 glow_perk_timer = 0;
65 
check_glow_perk(void)66 void check_glow_perk(void) { glow_perk_check_state = 3; }
glow_perk_is_active(void)67 int glow_perk_is_active(void) { return glow_perk_active; };
68 
69 /*
70  * Called each frame by the hud indicator code if the glow perk indcator is enabled.
71 */
glow_perk_is_unavailable(void)72 int glow_perk_is_unavailable(void)
73 {
74 	/*
75 	 * The first #glow will be sent when a new login happens.  When the
76 	 * message text arrives, another #glow will be sent after a timeout.
77 	 * By togginging twice, we get the starting state and preserve it.
78 	*/
79 	if (glow_perk_check_state == 3)
80 	{
81 		send_input_text_line("#glow", 5);
82 		glow_perk_unavailable = 1;
83 		glow_perk_check_state--;
84 	}
85 	else if (glow_perk_timer && ((SDL_GetTicks()-glow_perk_timer) > 1500))
86 	{
87 		send_input_text_line("#glow", 5);
88 		glow_perk_timer = 0;
89 	}
90 	return glow_perk_unavailable;
91 };
92 
93 /*
94  * Called when we receive #glow command text.
95 */
set_glow_status(int value)96 static int set_glow_status(int value)
97 {
98 	glow_perk_active = value;
99 	if (glow_perk_check_state)
100 	{
101 		if (--glow_perk_check_state)
102 			glow_perk_timer = SDL_GetTicks();
103 		else
104 			glow_perk_unavailable = 0;
105 		return 1;
106 	}
107 	glow_perk_unavailable = 0;
108 	return 0;
109 }
110 /* End glow perk hud indicator state. */
111 
112 static int is_special_day = 0;
today_is_special_day(void)113 int today_is_special_day(void) { return is_special_day; };
set_today_is_special_day(void)114 void set_today_is_special_day(void) { is_special_day = 1; };
clear_today_is_special_day(void)115 void clear_today_is_special_day(void) { is_special_day = 0; };
116 
117 /* functions to count, return and clear the number of PM or MODPM seen */
118 static int seen_pm_count = 0;
inc_seen_pm_count(void)119 static void inc_seen_pm_count(void) { seen_pm_count++; }
get_seen_pm_count(void)120 int get_seen_pm_count(void) { return seen_pm_count; }
clear_seen_pm_count(void)121 void clear_seen_pm_count(void) { seen_pm_count = 0; }
122 
123 int log_chat = LOG_SERVER;
124 
125 FILE	*chat_log=NULL;
126 FILE	*srv_log=NULL;
127 
128 #ifndef NEW_SOUND
129 int afk_snd_warning = 0;
130 #endif
131 
alloc_text_message_data(text_message * msg,int size)132 void alloc_text_message_data (text_message *msg, int size)
133 {
134 	msg->data = size > 0 ? calloc (size, 1) : NULL;
135 	msg->size = size;
136 	msg->len = 0;
137 }
138 
resize_text_message_data(text_message * msg,int len)139 void resize_text_message_data (text_message *msg, int len)
140 {
141 	if (msg->size <= len)
142 	{
143 		int nsize = msg->size ? 2 * msg->size : len+1;
144 		while (nsize < len)
145 			nsize += nsize;
146 		msg->data = realloc (msg->data, nsize);
147 		msg->size = nsize;
148         }
149 }
150 
set_text_message_data(text_message * msg,const char * data)151 void set_text_message_data (text_message *msg, const char* data)
152 {
153 	if (data == NULL || data[0] == '\0')
154 	{
155 		clear_text_message_data (msg);
156 	}
157 	else if (msg->size > 0)
158 	{
159 		safe_strncpy (msg->data, data, msg->size);
160 		msg->len = strlen (msg->data);
161         }
162 }
163 
init_text_buffers()164 void init_text_buffers ()
165 {
166 	int i;
167 
168 	for (i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
169 		init_text_message (display_text_buffer + i, 0);
170 
171 	init_text_message (&input_text_line, MAX_TEXT_MESSAGE_LENGTH + 1);
172 	input_text_line.chan_idx = CHAT_ALL;
173 	set_text_message_color (&input_text_line, 1.0f, 1.0f, 1.0f);
174 }
175 
cleanup_text_buffers(void)176 void cleanup_text_buffers(void)
177 {
178 	int i;
179 
180 	free_text_message_data (&input_text_line);
181 	for(i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
182 		free_text_message_data (display_text_buffer + i);
183 }
184 
open_chat_log()185 void open_chat_log(){
186 	char starttime[200], sttime[200];
187 	struct tm *l_time; time_t c_time;
188 
189 	char chat_log_file[100];
190 	char srv_log_file[100];
191 
192 	time(&c_time);
193 	l_time = localtime(&c_time);
194 
195 	if (get_rotate_chat_log())
196 	{
197 		char logsuffix[7];
198 		strftime(logsuffix, sizeof(logsuffix), "%Y%m", l_time);
199 		safe_snprintf (chat_log_file, sizeof (chat_log_file),  "chat_log_%s.txt", logsuffix);
200 		safe_snprintf (srv_log_file, sizeof (srv_log_file), "srv_log_%s.txt", logsuffix);
201 	}
202 	else
203 	{
204 		safe_strncpy(chat_log_file, "chat_log.txt", sizeof(chat_log_file));
205 		safe_strncpy(srv_log_file, "srv_log.txt", sizeof(srv_log_file));
206 	}
207 
208 	chat_log = open_file_config (chat_log_file, "a");
209 	if (log_chat == LOG_SERVER || log_chat == LOG_SERVER_SEPERATE)
210 		srv_log = open_file_config (srv_log_file, "a");
211 	if (chat_log == NULL)
212 	{
213 		LOG_TO_CONSOLE(c_red3, "Unable to open log file to write. We will NOT be recording anything.");
214 		log_chat = LOG_NONE;
215 		return;
216 	}
217 	else if ((log_chat == LOG_SERVER || log_chat == LOG_SERVER_SEPERATE) && srv_log == NULL)
218 	{
219 		LOG_TO_CONSOLE(c_red3, "Unable to open server log file to write. We will fall back to recording everything in chat_log.txt.");
220 		log_chat = LOG_CHAT;
221 		return;
222 	}
223 	strftime(sttime, sizeof(sttime), "\n\nLog started at %Y-%m-%d %H:%M:%S localtime", l_time);
224 	safe_snprintf(starttime, sizeof(starttime), "%s (%s)\n\n", sttime, tzname[l_time->tm_isdst>0]);
225 	fwrite (starttime, strlen(starttime), 1, chat_log);
226 }
227 
228 
timestamp_chat_log()229 void timestamp_chat_log(){
230 	char starttime[200], sttime[200];
231 	struct tm *l_time; time_t c_time;
232 
233 	if(log_chat == LOG_NONE) {
234 		return; //we're not logging anything
235 	}
236 
237 	if (chat_log == NULL){
238 		open_chat_log();
239 	} else {
240 		time(&c_time);
241 		l_time = localtime(&c_time);
242 		strftime(sttime, sizeof(sttime), "Hourly time-stamp: log continued at %Y-%m-%d %H:%M:%S localtime", l_time);
243 		safe_snprintf(starttime, sizeof(starttime), "%s (%s)\n", sttime, tzname[l_time->tm_isdst>0]);
244 		fwrite (starttime, strlen(starttime), 1, chat_log);
245 	}
246 }
247 
248 
write_to_log(Uint8 channel,const Uint8 * const data,int len)249 void write_to_log (Uint8 channel, const Uint8* const data, int len)
250 {
251 	int i, j;
252 	Uint8 ch;
253 	char str[1024];
254 	struct tm *l_time; time_t c_time;
255 	FILE *fout;
256 
257 #ifdef NEW_SOUND
258 	// Check if this string matches text we play a sound for
259 	check_sound_alerts(data, len, channel);
260 #endif // NEW_SOUND
261 
262 	if(log_chat == LOG_NONE || (channel == CHAT_SERVER && log_chat == LOG_CHAT))
263 		// We're not logging at all, or this is a server message and
264 		// we're not logging those
265 		return;
266 
267 	if (chat_log == NULL){
268 		open_chat_log();
269 		if(chat_log == NULL){
270 			return;
271 		}
272 	}
273 
274 	// The file we'll write to
275 	fout = (channel == CHAT_SERVER && log_chat >= 3) ? srv_log : chat_log;
276 
277 	if(!show_timestamp)
278 	{
279 		// Start filling the buffer with the time stamp
280 		time (&c_time);
281 		l_time = localtime (&c_time);
282 		j = strftime (str, sizeof(str), "[%H:%M:%S] ", l_time);
283 	}
284 	else
285 	{
286 		//we already have a time stamp
287 		j=0;
288 	}
289 
290 	i = 0;
291 	while (i < len)
292 	{
293 		for ( ; i < len && j < sizeof (str) - 1; i++)
294 		{
295 			ch = data[i];
296 
297 			// remove colorization and soft wrapping characters when
298 			// writing to the chat log
299 			if (!is_color (ch) && ch != '\r')
300 				str[j++] = ch;
301 		}
302 		if (i >= len) str[j++]='\n';
303 
304 		fwrite (str, j, 1, fout);
305 
306 		// start again at the beginning of the buffer
307 		j = 0;
308 	}
309 
310 	// Flush the file, so the content is written even when EL crashes.
311 	fflush (fout);
312 }
313 
send_input_text_line(char * line,int line_len)314 void send_input_text_line (char *line, int line_len)
315 {
316 	char str[256];
317 	int i,j;
318 	int len;
319 	Uint8 ch;
320 
321 	change_to_channel_tab(line);
322 
323 	if ( caps_filter && line_len > 4 && my_isupper (line, -1) )
324 		my_tolower (line);
325 
326 	i=0;
327 	j=1;
328 	if (line[0] != '/' && line[0] != char_slash_str[0])	//we don't have a PM
329 	{
330 		str[0] = RAW_TEXT;
331 	}
332 	else
333 	{
334 		str[0] = SEND_PM;
335 		i++;	// skip the leading /
336 	}
337 
338 	for ( ; i < line_len && j < sizeof (str) - 1; i++)	// copy it, but ignore the enter
339 	{
340 		ch = line[i];
341 		if (ch != '\n' && ch != '\r')
342 		{
343 			str[j] = ch;
344 			j++;
345 		}
346 	}
347 	str[j] = 0;	// always a NULL at the end
348 
349 	len = strlen (&str[1]);
350 	if (my_tcp_send (my_socket, (Uint8*)str, len+1) < len+1)
351 	{
352 		//we got a nasty error, log it
353 	}
354 
355 	return;
356 }
357 
match_emote(emote_dict * command,actor * act,int send)358 int match_emote(emote_dict *command, actor *act, int send)
359 {
360 	hash_entry *match;
361 
362 	// Try to match the input against an emote command and actor type
363 	match=hash_get(emote_cmds,(void*)command->command);
364 
365 	if(match){
366 		//printf("Emote <%s> sent (%p)\n",((emote_dict*)match->item)->command,((emote_dict*)match->item)->emote);
367 		//SEND emote to server
368 		if (send) send_emote(((emote_dict*)match->item)->emote->id);
369 
370 		return 1;
371 	}
372 	return 0;
373 }
374 
375 
parse_text_for_emote_commands(const char * text,int len)376 int parse_text_for_emote_commands(const char *text, int len)
377 {
378 	int i=0, j = 0, wf=0,ef=0, itsme=0;
379 	char name[20];	// Yeah, this should be done correctly
380 	emote_dict emote_text;
381 	actor *act;
382 
383 
384 	//printf("parsing local for emotes\n");
385 	//extract name
386 	while(text[i]&&i<20){
387 		if (is_color(text[i])) {i++; continue;}
388 		name[j]=text[i];
389 		if(text[i]==' ' || text[i]==':') {
390 			name[j]=0;
391 			if(text[i]==':') i++;
392 			break;
393 		}
394 		i++;j++;
395 	}
396 
397 	if(j>=20||name[j]) return 0; 		//out of bound or not terminated
398 
399 	//check if we are saying text
400 	LOCK_ACTORS_LISTS();
401 
402 	act = get_actor_ptr_from_id(yourself);
403 	if (!act){
404 		UNLOCK_ACTORS_LISTS();
405 		LOG_ERROR("Unable to find actor who just said local text?? name: %s", name);
406 		return 1;		// Eek! We don't have an actor match... o.O
407 	}
408 
409 	if (!(!strncasecmp(act->actor_name, name, strlen(name)) &&
410 			(act->actor_name[strlen(name)] == ' ' ||
411 			act->actor_name[strlen(name)] == '\0'))){
412 		//we are not saying this text, return
413 		//UNLOCK_ACTORS_LISTS();
414 		//return 0;
415 			itsme=0;
416 	} else itsme=1;
417 
418 	j=0;
419 	do {
420 		if (is_color(text[i])) continue;
421 		if ((text[i]==' ' || text[i]==0)) {
422 			if (j&&j<=MAX_EMOTE_LEN) {
423 				wf++;
424 				emote_text.command[j]=0;
425 				ef+=match_emote(&emote_text,act,itsme);
426 			} else wf+= (j) ? 1:0;
427 			j=0;
428 		} else {
429 			if (j<MAX_EMOTE_LEN)
430 				emote_text.command[j]=text[i];
431 			j++;
432 		}
433 	} while(text[i++]);
434 	//printf("ef=%i, wf=%i, filter=>%i\n",ef,wf,emote_filter);
435 	UNLOCK_ACTORS_LISTS();
436 
437 	return  ((ef==wf) ? (emote_filter):(0));
438 
439 }
440 
filter_or_ignore_text(char * text_to_add,int len,int size,Uint8 channel)441 int filter_or_ignore_text (char *text_to_add, int len, int size, Uint8 channel)
442 {
443 	int l, idx;
444 
445 	if (len <= 0) return 0;	// no point
446 
447 	//check for auto receiving #help
448 	for (idx = 0; idx < len; idx++)
449 	{
450 		if (!is_color (text_to_add[idx])) break;
451 	}
452 	l = len - idx;
453 	if (l >= strlen(help_request_str) && text_to_add[idx] == '#' && (strncasecmp (&text_to_add[idx], help_request_str, strlen(help_request_str)) == 0 || strncasecmp (&text_to_add[idx], "#mod chat", 9) == 0))
454 	{
455 		auto_open_encyclopedia = 0;
456 	}
457 
458 	/*
459 	DANGER, WILL ROBINSON!
460 
461 	The below code should not exist in it's present form.  I'd change it,
462 	but I'd need access to the server.  Simply checking text output (which
463 	is used for all sorts of things) for the phrase "Game Date" is very
464 	dangerous.  Example: what if, in the future, we allow spaces in
465 	character names?  Someone chooses the name "Game Date" and walks around
466 	saying "hi".  Everyone's clients in the area interpret this as being a
467 	Game Date command.
468 
469 	I've made the below code not *as* dangerous. Had a user been able to
470 	fake out the below code, previously, it would have caused a buffer overflow
471 	in their client if they didn't write in only numbers after it.  Now, they
472 	won't crash; it'll just be misparsed.
473 
474 	General practice recommendation: don't mix server commands with user
475 	input.
476 
477 	 - Karen
478 	*/
479 	/*
480 	ed (ttlanhil): made it check if it's a server colour. still not perfect
481 	(this should have been done server-side instead of parsing the date), but safer
482 	*/
483 	if (from_color_char (text_to_add[0]) == c_green1 && !strncasecmp(text_to_add+1, "Game Date", 9))
484 	{
485 		//we assume that the server will still send little-endian dd/mm/yyyy... we could make it safer by parsing the format too, but it's simpler to assume
486 		const char * const month_names[] = { "Aluwia", "Seedar", "Akbar", "Zartia", "Elandra", "Viasia", "Fruitfall", "Mortia", "Carnelar", "Nimlos", "Chimar", "Vespia" };
487 		const char * const day_names[] = { "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th", "30th" };
488 		char new_str[100];
489 		const char *ptr=text_to_add;
490 		short unsigned int day=1, month=1, year=0;
491 		int offset = 0;
492 
493 		while(!isdigit(ptr[offset]))
494 		{
495 			offset++;
496 			if (offset >= sizeof(new_str))
497 			{
498 				LOG_ERROR("error (1) parsing date string: %s",text_to_add);
499 				//something evil this way comes...
500 				return 0;
501 			}
502 		}
503 		ptr += offset;
504 
505 		if (sscanf (ptr,"%hu%*[-/]%hu%*[-/]%hu",&day,&month,&year) < 3
506 		    || day <= 0 || month <= 0
507 		    || day > 30 || month > 12 || year > 9999)
508 		{
509 			LOG_ERROR("error (2) parsing date string: %s",text_to_add);
510 			//something evil this way comes...
511 		}
512 		else
513 		{
514 			// only display initial or "#date" user requested date
515 			if (!set_date(ptr))
516 			{
517 				safe_snprintf(new_str, sizeof(new_str), date_format, day_names[day-1], month_names[month-1], year);
518 				LOG_TO_CONSOLE(c_green1, new_str);
519 			}
520 
521 			//Calculate fraction Big Lunar month (2 conjunction months) less game clock time
522 			//Represented in Degrees.
523 			skybox_time_d = (SDL_GetTicks()%( 1296000 * 1000 ));
524 			skybox_time_d *= 360.0/( 1296000.0 * 1000.0);
525 			skybox_time_d = -skybox_time_d;
526 			skybox_time_d += 360.0 * (((month%2)*30 + day-1)*360 + game_minute)/21600.0;
527 			skybox_update_positions();
528 			return 0;
529 		}
530 	}
531 
532 	if (from_color_char (text_to_add[0]) == c_green1 && !strncasecmp(text_to_add+1, "Game Time", 9))
533 	{
534 		real_game_second = atoi(&text_to_add[18]);
535 		set_real_game_second_valid();
536 		next_second_time = cur_time + 1000;
537         new_second();
538 	}
539 
540 	// Check for local messages to be translated into actor movements (contains [somthing])
541 	if (channel == CHAT_LOCAL)
542 	{
543 		if(parse_text_for_emote_commands(text_to_add, len)) return 0;
544 	}
545 
546 	if (channel == CHAT_SERVER) {
547 		if (!strncasecmp(text_to_add+1, "You started to harvest ", 23)) {
548 			strncpy(harvest_name, text_to_add+1+23, len-1-23-1);
549 			harvest_name[len-1-23-1] = '\0';
550 			set_now_harvesting();
551 		}
552 		else if ((!strncasecmp(text_to_add+1, "You stopped harvesting.", 23)) ||
553 			(!strncasecmp(text_to_add+1, "You can't harvest while fighting (duh)!", 39)) ||
554 			(!strncasecmp(text_to_add+1, "You can't do that while trading!", 32)) ||
555 			(!strncasecmp(text_to_add+1, "You can't harvest here", 22)) ||
556 			(!strncasecmp(text_to_add+1, "You lack the knowledge of ", 26)) ||
557 			((!strncasecmp(text_to_add+1, "You need to wear ", 17) && strstr(text_to_add, "order to harvest") != NULL)) ||
558 			((!strncasecmp(text_to_add+1, "You need to have a ", 19) && strstr(text_to_add, "order to harvest") != NULL)))
559 		{
560 			clear_now_harvesting();
561 		}
562 		else if (!strncasecmp(text_to_add+1, "Great, you changed your password!", 33))
563 		{
564 			if (!passmngr_confirm_pw_change())
565 				LOG_TO_CONSOLE(c_red1, passmngr_error_str);
566 		}
567 		else if (!strncasecmp(text_to_add+1, "Glow on!", 8))
568 		{
569 			if (set_glow_status(1))
570 				return 0;
571 		}
572 		else if (!strncasecmp(text_to_add+1, "Glow off!", 9))
573 		{
574 			if (set_glow_status(0))
575 				return 0;
576 		}
577 		else if (!strncasecmp(text_to_add+1, "You need the I glow in the dark perk in order to use this.", 58))
578 		{
579 			glow_perk_unavailable = 1;
580 			if (glow_perk_check_state)
581 			{
582 				glow_perk_check_state = 0;
583 				return 0;
584 			}
585 		}
586 		else if (is_death_message(text_to_add+1)) {
587 			// nothing to be done here cause all is done in the test function
588 		}
589 		else if (!strncasecmp(text_to_add+1, "You found ", 10) && strstr(text_to_add+1, " coins.")) {
590 			decrement_harvest_counter(atoi(text_to_add+11));
591 		}
592 		else if (!strncasecmp(text_to_add+1, "Send Item UIDs ", 15)) {
593 			if (text_to_add[1+15] == '0')
594 				item_uid_enabled = 0;
595 			else if (text_to_add[1+15] == '1')
596 				item_uid_enabled = 1;
597 			printf("item_uid_enabled=%d\n", item_uid_enabled);
598 		}
599 		else if ((copy_next_LOCATE_ME > 0) && !strncasecmp(text_to_add+1, "You are in ", 11)) {
600 			char buffer[4096];
601 			switch (copy_next_LOCATE_ME)
602 			{
603 				case 1:
604 					copy_to_clipboard(text_to_add+1);
605 					break;
606 				case 2:
607 					snprintf(buffer, sizeof(buffer), "@My Position: %s", text_to_add + 12);
608 					send_input_text_line(buffer, strlen(buffer));
609 					break;
610 			}
611 			copy_next_LOCATE_ME = 0;
612 			return 0;
613 		}
614 		else if (!strncasecmp(text_to_add+1, "You see: ", 9)) {
615 			achievements_player_name(text_to_add+10, len-10);
616 		}
617 		else if ((!strncasecmp(text_to_add+1, "You just got food poisoned!", 27)) ||
618 			(!strncasecmp(text_to_add+1, "Oh well, no invisibility, but we got poisoned.", 46)))
619 		{
620 			increment_poison_incidence();
621 		}
622 		else if (strstr(text_to_add+1, "aborted the trade.")) {
623 			trade_aborted(text_to_add+1);
624 		}
625 		else if (strstr(text_to_add+1, "Trade session failed")) {
626 			trade_aborted(text_to_add+1);
627 		}
628 		else if (strstr(text_to_add+1, "You have been saved!")) {
629 			last_save_time = time(NULL);
630 		}
631 		else if (strstr(text_to_add+1, "Day ends:") || strstr(text_to_add+1, "This day was removed by ")) {
632 			clear_today_is_special_day();
633 		}
634 		else if (strstr(text_to_add+1, "Today is a special day:")) {
635 			set_today_is_special_day();
636 		}
637 		else if (strstr(text_to_add+1, "You'd need a pair of binoculars to read the book from here - get closer!")) {
638 			if (!book_window_is_open())
639 				return 0;
640 		}
641 		else if (!strncasecmp(text_to_add+1, "You are researching ", 20)) {
642 			if (get_true_knowledge_info(text_to_add+1))
643 				return 0;
644 		}
645 		else if (!strncasecmp(text_to_add+1, "Your buddy list is now empty.", 29)) {
646 			clear_buddy();
647 		}
648 
649 
650 		else {
651 			static Uint32 last_time[] = { 0, 0 };
652 			static int done_one[] = { 0, 0 };
653 			int match_index = -1;
654 			if (!strncasecmp(text_to_add+1, "You are too far away! Get closer!", 33))
655 				match_index = 0;
656 			else if (!strncasecmp(text_to_add+1, "Can't do, your target is already fighting with someone else,", 60))
657 				match_index = 1;
658 			if (match_index > -1)
659 			{
660 				Uint32 new_time = SDL_GetTicks();
661 				clear_now_harvesting();
662 				if(your_actor != NULL)
663 					add_highlight(your_actor->x_tile_pos,your_actor->y_tile_pos, HIGHLIGHT_SOFT_FAIL);
664 				/* suppress further messages within for 5 seconds of last */
665 				if (done_one[match_index] && ((new_time - last_time[match_index]) < 5000))
666 					return 0;
667 				done_one[match_index] = 1;
668 				last_time[match_index] = new_time;
669 			}
670 		}
671 
672 	} else if (channel == CHAT_LOCAL) {
673 		if (now_harvesting() && !strncasecmp(text_to_add+1, get_username(), strlen(get_username()))) {
674 			char *ptr = text_to_add+1+strlen(get_username());
675 			if (!strncasecmp(ptr, " found a ", 9)) {
676 				ptr += 9;
677 				if (!strncasecmp(ptr, "bag of gold, getting ", 21)) {
678 					decrement_harvest_counter(atoi(ptr+21));
679 				} else if (!strstr(ptr, " could not carry ")) {
680 					decrement_harvest_counter(1);
681 				}
682 			}
683 		} else if (!strncasecmp(text_to_add+1, "(*) ", 4)) {
684 			increment_summon_counter(text_to_add+1+4);
685 			if (summoning_filter) return 0;
686 		}
687 	}
688 	/* check for misc counter strings */
689 	catch_counters_text(text_to_add+1);
690 
691 	/* put #mpm in a popup box, on top of all else */
692 	if ((channel == CHAT_MODPM) && (!strncasecmp(text_to_add+1, "[Mod PM from", 12))) {
693 		display_server_popup_win((const unsigned char*)text_to_add);
694 	}
695 
696 	// look for astrology messages
697 	if((channel == CHAT_SERVER) && is_astrology_message (text_to_add))
698 	{
699 		return 0;
700 	}
701 
702 	//Make sure we don't check our own messages.
703 	if( !(channel == CHAT_PERSONAL && len >= strlen(pm_from_str) && strncasecmp (text_to_add+1, pm_from_str, strlen(pm_from_str)) != 0) &&
704 		!(channel == CHAT_MODPM && len >= strlen(mod_pm_from_str) && strncasecmp (text_to_add+1, mod_pm_from_str, strlen(mod_pm_from_str)) != 0)
705 	) {
706 
707 		//check if ignored - pre_check_if_ignored() checks for Mod PM's etc to not ignore (or it  would be asking for trouble)
708 		if (pre_check_if_ignored (text_to_add, len, channel))
709 		{
710 			return 0;
711 		}
712 		//All right, we do not ignore the person
713 		if (channel == CHAT_PERSONAL || channel == CHAT_MODPM)
714 			inc_seen_pm_count();
715 		if (afk)
716 		{
717 			if (channel == CHAT_PERSONAL || channel == CHAT_MODPM)
718 			{
719 				// player sent us a PM
720 				add_message_to_pm_log (text_to_add, len, channel);
721 				if (afk_snd_warning) {
722 					do_afk_sound();
723 				}
724 			}
725 			else if (channel == CHAT_LOCAL && from_color_char (text_to_add[0]) == c_grey1 && is_talking_about_me (&text_to_add[1], len-1, 0))
726 			{
727 				// player mentions our name in local chat
728 				if (afk_local) {
729 					add_message_to_pm_log (&text_to_add[1], len - 1, channel);
730 					if (afk_snd_warning) {
731 						do_afk_sound();
732 					}
733 				} else {
734 					send_afk_message (&text_to_add[1], len - 1, channel);
735 				}
736 			}
737 			else if (channel == CHAT_SERVER)
738 			{
739 				// check if this was a trade attempt
740 				int i;
741 				for (i = 1; i < len; i++) {
742 					if (text_to_add[i] == ' ' || text_to_add[i] == ':' || is_color (text_to_add[i])) {
743 						break;
744 					}
745 				}
746 				if (i < len-15 && strncasecmp (&text_to_add[i], " wants to trade", 15) == 0) {
747 					send_afk_message (&text_to_add[1], len - 1, channel);
748 					if (afk_snd_warning) {
749 						do_afk_sound();
750 					}
751 				}
752 			}
753 		}
754 	} else {	//We sent this PM or MODPM. Can we expect a reply?
755 		int len = 0;
756 		char name[MAX_USERNAME_LENGTH];
757 		for(;text_to_add[len+8] != ':' && len < MAX_USERNAME_LENGTH - 1; ++len);
758 		safe_strncpy(name, text_to_add+8, len+1);
759 		if(check_if_ignored(name)){
760 			char msg[65];
761 			safe_snprintf(msg, sizeof(msg), warn_currently_ignoring, name);
762 			LOG_TO_CONSOLE(c_red2, msg);
763 		}
764 	}
765 
766 	// parse for URLs
767 	find_all_url (text_to_add, len);
768 
769 	// look for buddy-wants-to-add-you messages
770 	if(channel == CHAT_SERVER && from_color_char (text_to_add[0]) == c_green1)
771 	{
772 		for (l = 1; l < len; l++)
773 		{
774 			if (text_to_add[l] == ' ') break;
775 		}
776 		if (len - l >= strlen(msg_accept_buddy_str) && strncmp (&text_to_add[l], msg_accept_buddy_str, strlen(msg_accept_buddy_str)) == 0 && l <=32)
777 		{
778 			char name[32];
779 			int i;
780 			int cur_char;
781 			/*entropy says: I really fail to understand the logic of that safe_snprintf. And gcc can't understand it either
782 			  because the name is corrupted. so we implement it the old fashioned way.
783 			  Grum responds: actually it's the MingW compiler on windows that doesn't understand it, because it doesn't
784 			  terminate the string when the buffer threatens to overflow. It works fine with gcc on Unix, and using
785 			  sane_safe_snprintf should also fix it on windows.
786 			safe_snprintf (name, l, "%s", &text_to_add[1]);
787 			*/
788 			for (i = 0; i < sizeof (name); i++)
789 			{
790 				cur_char = text_to_add[i+1];
791 				name[i] = cur_char;
792 				if (cur_char == ' ')
793 				{
794 					name[i] = '\0';
795 					break;
796 				}
797 			}
798 			add_buddy_confirmation (name);
799 		}
800 	}
801 
802 	// filter any naughty words out
803 	return filter_text (text_to_add, len, size);
804 }
805 
put_text_in_buffer(Uint8 channel,const Uint8 * text_to_add,int len)806 void put_text_in_buffer (Uint8 channel, const Uint8 *text_to_add, int len)
807 {
808 	put_colored_text_in_buffer (c_grey1, channel, text_to_add, len);
809 }
810 
811 //-- Logan Dugenoux [5/26/2004]
812 // Checks chat string, if it begins with an actor name,
813 // and the actor is displayed, put said sentence into an overtext bubble
814 #define ALLOWED_CHAR_IN_NAME(_x_)		(isalnum(_x_)||(_x_=='_'))
check_chat_text_to_overtext(const Uint8 * text_to_add,int len,Uint8 channel)815 void check_chat_text_to_overtext (const Uint8 *text_to_add, int len, Uint8 channel)
816 {
817 	if (!view_chat_text_as_overtext || channel != CHAT_LOCAL)
818 		return;		// disabled
819 
820 	if (from_color_char (text_to_add[0]) == c_grey1)
821 	{
822 		char playerName[128];
823 		char textbuffer[1024];
824 		int i;
825 		int j;
826 
827 		j = 0;
828 		i = 1;
829 		while (text_to_add[i] < 128 && i < len)
830 		{
831 			if (text_to_add[i] != '[')
832 			{
833 				playerName[j] = (char)text_to_add[i];
834 				j++;
835 				if (j >= sizeof (playerName))
836 					return;//over buffer
837 			}
838 			i++;
839 		}
840 
841 		if (i < len)
842 		{
843 			playerName[j] = '\0';
844 			while ( j > 0 && !ALLOWED_CHAR_IN_NAME (playerName[j]) )
845 				playerName[j--] = '\0';
846 			j = 0;
847 			while (i < len)
848 			{
849 				if ( j >= sizeof (textbuffer) )
850 					return;//over buffer
851 				textbuffer[j] = (char) text_to_add[i];
852 				i++; j++;
853 			}
854 			textbuffer[j] = '\0';
855 			for (i = 0; i < max_actors; i++)
856 			{
857 				char actorName[128];
858 				j = 0;
859 				// Strip clan info
860 				while ( ALLOWED_CHAR_IN_NAME (actors_list[i]->actor_name[j]) )
861 				{
862 					actorName[j] = actors_list[i]->actor_name[j];
863 					j++;
864 					if ( j >= sizeof (actorName) )
865 						return;	// over buffer
866 				}
867 				actorName[j] = '\0';
868 				if (strcmp (actorName, playerName) == 0)
869 				{
870 					add_displayed_text_to_actor (actors_list[i], textbuffer);
871 					break;
872 				}
873 			}
874 		}
875 	}
876 }
877 
put_char_in_buffer(text_message * buf,Uint8 ch,int pos)878 int put_char_in_buffer (text_message *buf, Uint8 ch, int pos)
879 {
880 	int i, nlen;
881 
882 	if (pos < 0 || pos > buf->len || pos >= buf->size) {
883 		return 0;
884 	}
885 
886 	// First shift everything after pos to the right
887 	nlen = buf->len + 1;
888 	if (nlen >= buf->size)
889 		nlen = buf->size - 1;
890 	buf->data[nlen] = '\0';
891 	for (i = nlen - 1; i > pos; i--)
892 		buf->data[i] = buf->data[i-1];
893 
894 	// insert the new character, and update the length
895 	buf->data[pos] = ch;
896 	buf->len = nlen;
897 
898 	return 1;
899 }
900 
put_string_in_buffer(text_message * buf,const Uint8 * str,int pos)901 int put_string_in_buffer (text_message *buf, const Uint8 *str, int pos)
902 {
903 	int nr_free, nr_paste, ib, jb, nb;
904 	Uint8 ch;
905 
906 	if (pos < 0 || pos > buf->len) return 0;
907 	if (str == NULL) return 0;
908 
909 	// find out how many characters to paste
910 	nr_free = buf->size - pos - 1;
911 	nr_paste = 0;
912 	for (ib = 0; str[ib] && nr_paste < nr_free; ib++)
913 	{
914 		ch = str[ib];
915 		if (is_printable (ch) || ch == '\n')
916 			nr_paste++;
917 	}
918 
919 	if (nr_paste == 0) {
920 		return 0;
921 	}
922 
923 	// now move the characters right of the cursor (if any)
924 	nb = buf->len - pos;
925 	if (nb > nr_free - nr_paste)
926 		nb = nr_free - nr_paste;
927 	if (nb > 0)
928 	{
929 		for (ib = nb-1; ib >= 0; ib--)
930 			buf->data[pos+ib+nr_paste] = buf->data[pos+ib];
931 	}
932 	buf->data[pos+nb+nr_paste] = '\0';
933 	buf->len = pos+nb+nr_paste;
934 
935 	// insert the pasted text
936 	jb = 0;
937 	for (ib = 0; str[ib]; ib++)
938 	{
939 		ch = str[ib];
940 		if (ch == '\n')
941 			ch = ' ';
942 		if (is_printable (ch))
943 		{
944 			buf->data[pos+jb] = ch;
945 			if (++jb >= nr_paste) break;
946 		}
947 	}
948 
949 	return nr_paste;
950 }
951 
put_colored_text_in_buffer(Uint8 color,Uint8 channel,const Uint8 * text_to_add,int len)952 void put_colored_text_in_buffer (Uint8 color, Uint8 channel, const Uint8 *text_to_add, int len)
953 {
954 	text_message *msg;
955 	int minlen, text_color;
956 	Uint32 cnr = 0, ibreak = -1, jbreak = -1;
957 	char time_stamp[12];
958 	struct tm *l_time; time_t c_time;
959 
960 	check_chat_text_to_overtext (text_to_add, len, channel);
961 
962 	// check for auto-length
963 	if (len < 0)
964 		len = strlen ((char*)text_to_add);
965 
966 	// set the time when we got this message
967 	last_server_message_time = cur_time;
968 
969 	// if the buffer is full, delete some old lines and move the remainder to the front
970 	if (++last_message >= DISPLAY_TEXT_BUFFER_SIZE)
971 	{
972 		const size_t num_move = DISPLAY_TEXT_BUFFER_SIZE - DISPLAY_TEXT_BUFFER_DEL;
973 		size_t i;
974 		for (i=0; i<DISPLAY_TEXT_BUFFER_DEL; i++)
975 		{
976 			msg = &(display_text_buffer[i]);
977 			if (msg->data)
978 			{
979 				msg->deleted = 1;
980 				update_text_windows(msg);
981 				free_text_message_data (msg);
982 			}
983 		}
984 		memmove(display_text_buffer, &display_text_buffer[DISPLAY_TEXT_BUFFER_DEL], sizeof(text_message)*num_move);
985 		last_message -= DISPLAY_TEXT_BUFFER_DEL;
986 		for (i = num_move; i < DISPLAY_TEXT_BUFFER_SIZE; i++)
987 			init_text_message (display_text_buffer + i, 0);
988 	}
989 
990 	msg = &(display_text_buffer[last_message]);
991 
992 	// Try to make a guess at the number of wrapping newlines required,
993 	// but allow al least for a null byte and up to 8 extra newlines and
994 	// colour codes
995 	minlen = len + 18 + (len/60);
996 	if (show_timestamp)
997 	{
998 		minlen += 12;
999 		time (&c_time);
1000 		l_time = localtime (&c_time);
1001 		strftime (time_stamp, sizeof(time_stamp), "[%H:%M:%S] ", l_time);
1002 	}
1003 	cnr = get_active_channel (channel);
1004 	if (cnr != 0)
1005 		// allow some space for the channel number
1006 		minlen += 20;
1007 	if (msg->data == NULL)
1008 		alloc_text_message_data (msg, minlen);
1009 	else
1010 		resize_text_message_data (msg, minlen);
1011 
1012 	if (cnr != 0)
1013 	{
1014 		for (ibreak = 0; ibreak < len; ibreak++)
1015 		{
1016 			if (text_to_add[ibreak] == ']') break;
1017 		}
1018 	}
1019 
1020 	if (channel == CHAT_LOCAL)
1021 	{
1022 		for (jbreak = 0; jbreak < len; jbreak++)
1023 		{
1024 			if (text_to_add[jbreak] == ':' && text_to_add[jbreak+1] == ' ') break;
1025 		}
1026 	}
1027 
1028 	if (dark_channeltext==1)
1029 		text_color = c_grey2;
1030 	else if (dark_channeltext==2)
1031 		text_color = c_grey4;
1032 	else
1033 		text_color = c_red1; // unexpected
1034 
1035 	if (ibreak >= len)
1036 	{
1037 		// not a channel, or something's messed up
1038 		if(!is_color (text_to_add[0]))
1039 		{
1040 			// force the color
1041 			if (show_timestamp)
1042 			{
1043 				safe_snprintf (msg->data, msg->size, "%c%s%.*s", to_color_char (color), time_stamp, len, text_to_add);
1044 			}
1045 			else
1046 			{
1047 				safe_snprintf (msg->data, msg->size, "%c%.*s", to_color_char (color), len, text_to_add);
1048 			}
1049 		}
1050 		else
1051 		{
1052 			// color set by server
1053 			if (show_timestamp)
1054 			{
1055 				if(dark_channeltext && channel==CHAT_LOCAL && from_color_char(text_to_add[0])==c_grey1 && jbreak < (len-3))
1056 				{
1057 					safe_snprintf (msg->data, msg->size, "%c%s%.*s%.*s", to_color_char (text_color), time_stamp, jbreak+1, &text_to_add[1], len-jbreak-3, &text_to_add[jbreak+3]);
1058 				}
1059 				else
1060 				{
1061 					safe_snprintf (msg->data, msg->size, "%c%s%.*s", text_to_add[0], time_stamp, len-1, &text_to_add[1]);
1062 				}
1063 			}
1064 			else
1065 			{
1066 				if(dark_channeltext && channel==CHAT_LOCAL && from_color_char(text_to_add[0])==c_grey1 && jbreak < (len-3))
1067 				{
1068 					safe_snprintf (msg->data, msg->size, "%c%.*s%.*s", to_color_char (text_color), jbreak+1, &text_to_add[1], len-jbreak-3, &text_to_add[jbreak+3]);
1069 				}
1070 				else
1071 				{
1072 					safe_snprintf (msg->data, msg->size, "%.*s", len, text_to_add);
1073 				}
1074 			}
1075 		}
1076 	}
1077 	else
1078 	{
1079 		char nr_str[16];
1080 		int has_additional_color = is_color(text_to_add[ibreak+3]);
1081 		if (cnr >= 1000000000)
1082 			safe_snprintf (nr_str, sizeof (nr_str), "guild");
1083 		else
1084 			safe_snprintf (nr_str, sizeof (nr_str), "%u", cnr);
1085 
1086 		if(!is_color (text_to_add[0]))
1087 		{
1088 			// force the color
1089 			if (show_timestamp)
1090 			{
1091 				if (dark_channeltext)
1092 				{
1093 					safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s%c%.*s", to_color_char (color), time_stamp, ibreak, text_to_add, nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1094 				}
1095 				else
1096 				{
1097 					safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s", to_color_char (color), time_stamp, ibreak, text_to_add, nr_str, len-ibreak, &text_to_add[ibreak]);
1098 				}
1099 			}
1100 			else
1101 			{
1102 				if (dark_channeltext)
1103 				{
1104 					safe_snprintf (msg->data, msg->size, "%c%.*s @ %s%.*s%c%.*s", to_color_char (color), ibreak, text_to_add, nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1105 				}
1106 				else
1107 				{
1108 					safe_snprintf (msg->data, msg->size, "%c%.*s @ %s%.*s", to_color_char (color), ibreak, text_to_add, nr_str, len-ibreak, &text_to_add[ibreak]);
1109 				}
1110 			}
1111 		}
1112 		else
1113 		{
1114 			// color set by server
1115 			if (show_timestamp)
1116 			{
1117 				if (dark_channeltext)
1118 				{
1119 					safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s%c%.*s", text_to_add[0], time_stamp, ibreak-1, &text_to_add[1], nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1120 				}
1121 				else
1122 				{
1123 					safe_snprintf (msg->data, msg->size, "%c%s%.*s @ %s%.*s", text_to_add[0], time_stamp, ibreak-1, &text_to_add[1], nr_str, len-ibreak, &text_to_add[ibreak]);
1124 				}
1125 			}
1126 			else
1127 			{
1128 				if (dark_channeltext)
1129 				{
1130 					safe_snprintf (msg->data, msg->size, "%.*s @ %s%.*s%c%.*s", ibreak, text_to_add, nr_str, 3, &text_to_add[ibreak], to_color_char (text_color), len-ibreak-3-has_additional_color, &text_to_add[ibreak+3+has_additional_color]);
1131 				}
1132 				else
1133 				{
1134 					safe_snprintf (msg->data, msg->size, "%.*s @ %s%.*s", ibreak, text_to_add, nr_str, len-ibreak, &text_to_add[ibreak]);
1135 				}
1136 			}
1137 		}
1138 	}
1139 
1140 	msg->len = strlen (msg->data);
1141 	msg->chan_idx = channel;
1142 	msg->channel = cnr;
1143 
1144 	// set invalid wrap data to force rewrapping
1145 	msg->wrap_lines = 0;
1146 	msg->wrap_zoom = 0.0f;
1147 	msg->wrap_width = 0;
1148 
1149 	msg->deleted = 0;
1150 	recolour_message(msg);
1151 	update_text_windows(msg);
1152 
1153 	// log the message
1154 	write_to_log (channel, (unsigned char*)msg->data, msg->len);
1155 
1156 	return;
1157 }
1158 
1159 // find the last lines, according to the current time
find_last_lines_time(int * msg,int * offset,Uint8 filter,int width)1160 int find_last_lines_time (int *msg, int *offset, Uint8 filter, int width)
1161 {
1162 	// adjust the lines_no according to the time elapsed since the last message
1163 	if ( (cur_time - last_server_message_time) / 1000 > 3)
1164 	{
1165 		dec_lines_to_show();
1166 		last_server_message_time = cur_time;
1167 	}
1168 	if (get_lines_to_show() <= 0) return 0;
1169 
1170 	find_line_nr(get_total_nr_lines(), get_total_nr_lines() - get_lines_to_show(),
1171 		filter, msg, offset, CHAT_FONT, 1.0, width);
1172 	return 1;
1173 }
1174 
find_line_nr(int nr_lines,int line,Uint8 filter,int * msg,int * offset,font_cat font,float zoom,int width)1175 void find_line_nr(int nr_lines, int line, Uint8 filter, int *msg, int *offset,
1176 	font_cat font, float zoom, int width)
1177 {
1178 	int line_count = 0, lines_no = nr_lines - line;
1179 	int imsg, ichar;
1180 	char *data;
1181 
1182 	imsg = last_message;
1183 	if ( imsg<0 ) {
1184 		/* No data in buffer */
1185 		*msg = *offset = 0;
1186 		return;
1187 	}
1188 	do
1189 	{
1190 		int msgchan = display_text_buffer[imsg].chan_idx;
1191 
1192 		switch (msgchan) {
1193 			case CHAT_LOCAL:    if (!local_chat_separate)    msgchan = CHAT_ALL; break;
1194 			case CHAT_PERSONAL: if (!personal_chat_separate) msgchan = CHAT_ALL; break;
1195 			case CHAT_GM:       if (!guild_chat_separate)    msgchan = CHAT_ALL; break;
1196 			case CHAT_SERVER:   if (!server_chat_separate)   msgchan = CHAT_ALL; break;
1197 			case CHAT_MOD:      if (!mod_chat_separate)      msgchan = CHAT_ALL; break;
1198 			case CHAT_MODPM:                                 msgchan = CHAT_ALL; break;
1199 		}
1200 
1201 		if (msgchan == filter || msgchan == CHAT_ALL || filter == FILTER_ALL)
1202 		{
1203 			data = display_text_buffer[imsg].data;
1204 			if (data == NULL)
1205 				// Hmmm... we messed up. This should not be
1206 				// happening.
1207 				break;
1208 
1209 			rewrap_message(&display_text_buffer[imsg], font, zoom, width, NULL);
1210 
1211 			for (ichar = display_text_buffer[imsg].len - 1; ichar >= 0; ichar--)
1212 			{
1213 				if (data[ichar] == '\n' || data[ichar] == '\r')
1214 				{
1215 					line_count++;
1216 					if (line_count >= lines_no)
1217 					{
1218 						*msg = imsg;
1219 						*offset = ichar+1;
1220 						return;
1221 					}
1222 				}
1223 			}
1224 
1225 			line_count++;
1226 			if (line_count >= lines_no)
1227 			{
1228 				*msg = imsg;
1229 				*offset = 0;
1230 				return;
1231 			}
1232 		}
1233 
1234 		--imsg;
1235 
1236 	} while (imsg >= 0 && imsg != last_message);
1237 
1238 	*msg = 0;
1239 	*offset = 0;
1240 }
1241 
clear_display_text_buffer()1242 void clear_display_text_buffer ()
1243 {
1244 	int i;
1245 	for (i = 0; i < DISPLAY_TEXT_BUFFER_SIZE; ++i)
1246 	{
1247 		if (!display_text_buffer[i].deleted && display_text_buffer[i].data != NULL && display_text_buffer[i].data[0] != '\0'){
1248 			free_text_message_data (display_text_buffer + i);
1249 		}
1250 		display_text_buffer[i].deleted= 1;
1251 	}
1252 
1253 	last_message = -1;
1254 	last_server_message_time = cur_time;
1255 
1256 	clear_console();
1257 	if(use_windowed_chat == 2){
1258 		clear_chat_wins();
1259 	}
1260 }
1261 
rewrap_message(text_message * msg,font_cat cat,float text_zoom,int width,int * cursor)1262 int rewrap_message(text_message* msg, font_cat cat, float text_zoom, int width, int *cursor)
1263 {
1264 	int nlines;
1265 
1266 	if (msg == NULL || msg->data == NULL || msg->deleted)
1267 		return 0;
1268 
1269 	if (msg->wrap_width != width || msg->wrap_zoom != text_zoom)
1270 	{
1271 		int max_line_width = 0;
1272  		nlines = reset_soft_breaks((unsigned char*)msg->data, msg->len, msg->size,
1273 			cat, text_zoom, width, cursor, &max_line_width);
1274 		msg->len = strlen(msg->data);
1275 		msg->wrap_lines = nlines;
1276 		msg->wrap_width = width;
1277 		msg->wrap_zoom = text_zoom;
1278 		msg->max_line_width = max_line_width;
1279 	} else {
1280 		nlines = msg->wrap_lines;
1281 	}
1282 
1283 	return nlines;
1284 }
1285