view dmessage.c @ 3:5a977ccbc7a9 default tip

Empty changelog
author darius
date Sat, 06 Dec 1997 05:41:29 +0000
parents
children
line wrap: on
line source

/* $Id: dmessage.c,v 1.1.1.1 1997/12/06 05:41:29 darius Exp $ */

/*
 * dmessage.c
 *
 * for the client of a socket based protocol.
 * code for message window scrollback added by Bill Dyess 12/7/93
 */
#include "copyright.h"

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#ifdef __STDC__
#include <stdlib.h>
#endif
#include "Wlib.h"
#include "defs.h"
#include "struct.h"
#include "data.h"
#include "proto.h"
#ifdef SOUND
#include "Slib.h"
#endif

#define MESSAGESIZE	20
#define YOFF		0

struct messageData {
    char   *message;
    unsigned char flags, from, to;
};

struct messageNode {
    struct messageData *line;
    struct messageNode *next, *prev;
};

/* Prototypes */
int instr P((char *string1, char *string2));
void CheckFeatures P((char *m));
void print_message 
P((char *message, unsigned int flags, unsigned int from,
   unsigned int to, struct messageData * data,
   W_Window winmask, int backwards));
    void evalFlags P((int type, char *flagList));

    void    initMessageWindows()
{
    int     i;
    char   *s;

    messWin[WALL].window = W_MakeScrollingWindow("review_all", WINSIDE + BORDER,
						 YOFF + WINSIDE + 3 * BORDER + 2 * MESSAGESIZE, 80, 10, 0, "xterm", BORDER);
    messWin[WTEAM].window = W_MakeScrollingWindow("review_team", WINSIDE + BORDER,
      YOFF + WINSIDE + 4 * BORDER + 2 * MESSAGESIZE + 10 * W_Textheight + 8,
						  80, 5, 0, "xterm", BORDER);
    messWin[WINDIV].window = W_MakeScrollingWindow("review_your", WINSIDE + BORDER,
     YOFF + WINSIDE + 5 * BORDER + 2 * MESSAGESIZE + 15 * W_Textheight + 16,
						 80, 4, 0, "xterm", BORDER);
    messWin[WKILL].window = W_MakeScrollingWindow("review_kill", WINSIDE + BORDER,
     YOFF + WINSIDE + 6 * BORDER + 2 * MESSAGESIZE + 19 * W_Textheight + 24,
						  80, 6, 0, "xterm", BORDER);
    messWin[WPHASER].window = W_MakeScrollingWindow("review_phaser", WINSIDE +
    BORDER, YOFF + WINSIDE + 7 * BORDER + 2 * MESSAGESIZE + 24 * W_Textheight
					   + 32, 80, 3, 0, "xterm", BORDER);
    messWin[WREVIEW].window = W_MakeScrollingWindow("review", WINSIDE + BORDER,
			      YOFF + WINSIDE + 3 * BORDER + 2 * MESSAGESIZE,
						80, 20, 0, "xterm", BORDER);

    /*
       initialize the 'allow' flags.  Any window can display any message.
       [BDyess]
    */
    s = stringDefault("review_all.allow", "MA");
    evalFlags(WALL, s);
    s = stringDefault("review_team.allow", "T");
    evalFlags(WTEAM, s);
    s = stringDefault("review_your.allow", "I");
    evalFlags(WINDIV, s);
    s = stringDefault("review_kill.allow", "K");
    evalFlags(WKILL, s);
    s = stringDefault("review_phaser.allow", "P");
    evalFlags(WPHASER, s);
    s = stringDefault("review.allow", "MATIKP");
    evalFlags(WREVIEW, s);

    for (i = 0; i < WNUM; i++) {
	messWin[i].head = messWin[i].curHead = NULL;
    }
}

void
evalFlags(type, flagList)
    int     type;
    char   *flagList;
{
    char *orig=flagList;

    messWin[type].flags = 0;
    while (*flagList) {
	switch (toupper(*flagList)) {
	case 'A':
	    messWin[type].flags |= WA_ALL;
	    break;
	case 'T':
	    messWin[type].flags |= WA_TEAM;
	    break;
	case 'I':
	    messWin[type].flags |= WA_INDIV;
	    break;
	case 'K':
	    messWin[type].flags |= WA_KILL;
	    break;
	case 'P':
	    messWin[type].flags |= WA_PHASER;
	    break;
	case 'M':
	    messWin[type].flags |= WA_MACRO;
	    break;
	default:
	    printf("Invalid allow flag '%c' in allow list, ignoring\n",
		   *flagList);
	    break;
	}
	flagList++;
    }
    free(orig);
}

void
messageWinEvent(evt)
    W_Event *evt;
/* handles the mouse and keyboard events that happen in one of the message
   windows.  For key events, if the event is within the team or all window then
   it will automatically start the message for you, with destination to the
   player's team or ALL, respectively.  If it's a button event, it will scroll
   the message window back, forward, or to start (left, right, and middle
   button respectively).  It beeps if there is no messages to scroll (ie at
   the top or bottom already). [BDyess] 12/07/93 */
{
    struct messageNode **headpntr, *runner, *newcpntr;
    struct messageData *j;
    int     windowHeight;
    register int i;
    W_Window window = evt->Window;
    int     key = evt->key;
    int scroll=0;

    if (!W_IsMapped(window))
	return;

    if (evt->type == W_EV_KEY) {
	if (key == ('e' + 128)) {	/* erase window [BDyess] */
	    W_ClearWindow(window);
	} else if(key == ('p'+128)) {
	    scroll = -1;
	} else if(key == ('n'+128)) {
	    scroll = 1;
	} else if (window == messWin[WALL].window) {
	    smessage_ahead('A', key);
	} else if (window == messWin[WTEAM].window) {
	    smessage_ahead('T', key);
	} else {
	    smessage(key);
	}
	if(!scroll)
	    return;
    }
    headpntr = NULL;
    for (i = 0; i < WNUM; i++) {
	if (window == messWin[i].window) {
	    headpntr = &messWin[i].curHead;
	    break;
	}
    }

    runner = *headpntr;
    if (runner == NULL) {
	if(scrollBeep)
	    W_Beep();
	return;
    }
    windowHeight = W_WindowHeight(window);

    if(scroll==-1) {
	for (i = 0; (i < windowHeight) && runner; i++, runner = runner->next)
            ;
	if (runner == NULL) {
	    if (scrollBeep)
		W_Beep();
	    return;
	}
	j=runner->line;
	print_message(j->message, j->flags, j->from, j->to, NULL, window, 1);
	*headpntr = (*headpntr)->next;
	return;
    } else if(scroll==1) {
	if(runner->prev == NULL) {
	    if (scrollBeep)
		W_Beep();
	    return;
	}
	j=runner->prev->line;
	print_message(j->message, j->flags, j->from, j->to, NULL, window, 0);
	*headpntr = (*headpntr)->prev;
	return;
    }
    switch (key) {
    case W_LBUTTON:
	for (i = 0; (i < windowHeight-1) && runner->next; i++, runner = runner->next)
	    ;
	newcpntr=runner;
	
	if (runner->next == NULL) {
	    if (scrollBeep)
		W_Beep();
	    break;
	}
	runner=runner->next;
	for (i = 0; (i < windowHeight-1) && runner; i++, runner = runner->next) {
	    j=runner->line;
	    print_message(j->message, j->flags, j->from, j->to, NULL, window, 1);
	}
	if(i<windowHeight-1) {
	    for(;i<windowHeight-1;i++)
		W_WriteText(window,0,-1,0,"",0,0);
	}
	*headpntr=newcpntr;
	break;
    case W_RBUTTON:
	if (runner->prev == NULL) {
	    if (scrollBeep)
		W_Beep();
	    break;
	}
	for (i = 0; (i < windowHeight-1) && runner->prev; i++, runner = runner->prev) {
	    j=runner->prev->line;
	    print_message(j->message, j->flags, j->from, j->to, NULL, window, 0);
	}
	*headpntr = runner;
	break;
    case W_MBUTTON:
	if (runner->prev == NULL) {
	    if (scrollBeep)
		W_Beep();
	    break;
	}
	W_ClearWindow(window);
	while (runner->prev)
	    runner = runner->prev;
	*headpntr = runner;
	for (i = 0; i < windowHeight && runner->next; i++, runner = runner->next);
	while (runner) {
	    j = runner->line;
	    print_message(j->message, j->flags, j->from, j->to, NULL, window, 0);
	    runner = runner->prev;
	}
	break;
    }
}

void
rsvp_borg_call(message, from)
    char   *message;
    int     from;
{
#if 0
    int     len, pos;
    len = strlen(message);
    pos = len - 5;
    if (strncmp(message + pos, "     ", 5) == 0)
#else
    char   *chk;

    if (strlen(message) < 15)
	return;
    for (chk = message + 10; *chk && *chk == ' '; chk++)
	 /* empty body */ ;
    if (*chk)
	return;
#endif
    {
	char    buf[120];

	sprintf(buf, "Paradise Client %s (%s)", CLIENTVERS,
#ifdef __osf__
		"DEC OSF/1"
#else
#ifdef NeXT
		"NeXT"
#else
#ifdef ultrix
		"DEC Ultrix"
#else
#if defined(sparc) && defined(SVR4)
		"Solaris"
#else
#ifdef sparc
		"SUN4/Sparc"
#else
#ifdef sun
		"SUN3"
#else
#ifdef sequent
		"sequent"
#else
#ifdef hpux
		"hpux"
#else
#ifdef AIX
		"AIX"
#else
#ifdef vax
		"vax"
#else
#ifdef sgi
		"sgi/IRIX"
#else
#ifdef apollo
		"apollo"
#else
#ifdef RS6K
		"RS6K"
#else
#ifdef linux
		"Linux"
#else
#ifdef __NetBSD__
		"NetBSD"
#else
#ifdef __FreeBSD__
		"FreeBSD"
#else
#ifdef AMIGA
		"Amiga"
#else
		"generic"
#endif				/* Amiga */
#endif				/* linux */
#endif				/* RS6K */
#endif				/* apollo */
#endif				/* sgi */
#endif				/* vax */
#endif				/* AIX */
#endif				/* hpux */
#endif				/* sequent */
#endif				/* sun */
#endif				/* sparc */
#endif				/* solaris */
#endif				/* ultrix */
#endif				/* NeXT */
#endif				/* __osf__ */
#endif				/* FreeBSD */
#endif				/* NetBSD */
	    );
	if (from == 255) {
	    sendMessage(buf, MGOD, 0);
	} else
	    sendMessage(buf, MINDIV, from);
    }
}

void
logit(message)
    char   *message;
/* logs the given message if the 'logmess' variable is set to one.  It send the
   message to stdout by default, or to a file if one is defined. [BDyess] */
{
    if (!logmess)
	return;
    if (logfilehandle && logFile) {
	fprintf(logfilehandle, "%s\n", message);
	fflush(logfilehandle);
    } else {
	printf("%s\n", message);
    }
}

void
writeMessage(data, message, color, len, winmask, type, backwards)
    struct messageData *data;
    char   *message;
    W_Color color;
    int     len;
    W_Window winmask;
    int     type;
    int     backwards;
{
    struct messageNode *newhead;
    struct messageWin *j;

    if (data != NULL) {
#ifdef SPEECH /* was AMIGA... ya never know ;-) */
	if (((S_SpeakYour && type & WA_INDIV) ||
	     (S_SpeakTeam && type & WA_TEAM) ||
	     (S_SpeakAll && type & WA_ALL) ||
	     (S_SpeakKill && type & WA_KILL)) &&
	    ((strncmp(message,"GOD->",5) != 0) || S_SpeakGod) &&
	    ((data->from != me->p_no) || S_SpeakSelf)) {
	    S_SpeakString(message, len);
	}
#endif				/* SPEECH */
#ifdef SOUND
	if ((type & WA_INDIV) && data->from != me->p_no) {
	    S_PlaySound(S_WHISTLE);
	}
#endif
	for (j = &messWin[0]; j < &messWin[WNUM]; j++) {
	    if (!(j->flags & type))
		continue;
	    newhead = (struct messageNode *) malloc(sizeof(struct messageNode));
	    newhead->next = j->head;
	    if (newhead->next)
		newhead->next->prev = newhead;
	    newhead->prev = NULL;
	    newhead->line = data;
	    if (j->head == j->curHead) {
		/* we aren't currently scrolled back [BDyess] */
		W_WriteText(j->window, 0, 0, color, message, len, 0);
		j->curHead = newhead;
	    }
	    j->head = newhead;
	}
    } else {			/* we're drawing a new page */
	for (j = &messWin[0]; j < &messWin[WNUM]; j++) {
	    if (!(j->flags & type))
		continue;
	    if (j->window != winmask)
		continue;
	    W_WriteText(j->window, 0, (backwards ? -1 : 0), color, message, len, 0);
	}
    }
}

void
print_message(message, flags, from, to, data, winmask, backwards)
    char   *message;
    unsigned int flags, from, to;
    struct messageData *data;
    W_Window winmask;
    int backwards;
/* this function determines the color that the given message should be and
   then passes it to writeMessage, where the data structure for the scroll
   back code is created and also where the winmask test is done.  The winmask
   determines what window the message is written to, useful when calling this
   routine from the scrollback routines.  If winmask == NULL it doesn't
   limit printing at all, eg team messages will go to the team window and to
   the total review window.  [BDyess] 12/07/93 */
{
    register int len;
    W_Color color;
    W_Window targwin;

#define    take  MTEAM + MTAKE + MVALID
#define    destroy  MTEAM + MDEST + MVALID
#define    kill  MALL + MKILL + MVALID
#define    killp  MALL + MKILLP + MVALID
#define    killa  MALL + MKILLA + MVALID
#define    bomb  MTEAM + MBOMB + MVALID
#define    team  MTEAM + MVALID
#define    conq  MALL + MCONQ + MVALID

    len = strlen(message);
    if (from == 254) {		/* client passing info around */
	switch (showPhaser) {
	case 0:
	    break;
	case 1:
	    writeMessage(data, message, textColor, len, winmask, WA_KILL | WA_REVIEW, backwards);
	    break;
	case 2:
	    writeMessage(data, message, textColor, len, winmask, WA_REVIEW | WA_PHASER, backwards);
	    break;
	case 3:
	    writeMessage(data, message, textColor, len, winmask, WA_REVIEW, backwards);
	    break;
	}
	return;
    }
    if (from == 255) {
	if (flags == MCONFIG + MINDIV + MVALID) {
/*       printf("Going to checkfeatures\n");*/
	    CheckFeatures(message);
	    return;
	}
	/* From God */
	color = textColor;
    } else {
	/* kludge to fix the occasional incorrect color message */
	if (*message == ' ' && from != me->p_no) {
	    /* XXX fix to handle funky teams */
	    switch (*(message + 1)) {
	    case 'F':
		color = W_Yellow;
		break;
	    case 'R':
		color = W_Red;
		break;
	    case 'K':
		color = W_Green;
		break;
	    case 'O':
		color = W_Cyan;
		break;
	    case 'I':
		color = W_Grey;
		break;
	    default:
		color = playerColor(&(players[from]));
	    }
	} else
	    color = playerColor(&(players[from]));
    }

    /* added/modified to fit with the scrollback feature 1/94 -JR */
    if (!paradise && niftyNewMessages) {
	if (flags == conq) {
	    /* output conquer stuff to stdout in addition to message window */
	    fprintf(stdout, "%s\n", message);
	    if (instr(message, "kill")) {
		fprintf(stdout, "NOTE: The server here does not properly set message flags\n");
		fprintf(stdout, "You should probably pester the server god to update....\n");
	    }
	}
	if ((flags == team) || (flags == take) || (flags == destroy)) {
	    writeMessage(data, message, color, len, winmask, WA_TEAM | WA_REVIEW, backwards);
	    targwin = messWin[WTEAM].window;
	} else if ((flags == kill) || (flags == killp) || (flags == killa) || (flags == bomb)) {
	    writeMessage(data, message, color, len, winmask,
			 WA_KILL | (reportKills ? WA_REVIEW : 0), backwards);
	    targwin = messWin[WKILL].window;
	} else if (flags & MINDIV) {
	    writeMessage(data, message, color, len, winmask, (WA_INDIV | WA_REVIEW), backwards);
	    targwin = messWin[WINDIV].window;
	} else if (flags == (MMACRO | MALL)) {
	    writeMessage(data, message, color, len, winmask, (WA_MACRO | WA_REVIEW), backwards);
	    targwin = messWin[WALL].window;
	} else {
	    /*
	       if we don't know where the message belongs by this time, stick
	       it in the all board...
	    */
	    writeMessage(data, message, color, len, winmask, (WA_ALL | WA_REVIEW), backwards);
	    targwin = messWin[WALL].window;
	}
    } else {

	/*
	   Kludge stuff for report kills...
	*/
	if ((strncmp(message, "GOD->ALL", 8) == 0 &&
	     (instr(message, "was kill") ||
	      instr(message, "killed by"))) ||
	    instr(message, "burned to a crisp by") ||
	    instr(message, "shot down by") ||
	    (*message != ' ' && instr(message, "We are being attacked"))) {
	    writeMessage(data, message, color, len, winmask,
			 WA_KILL | (reportKills ? WA_REVIEW : 0), backwards);
	    return;
	}
	/*
	   note: messages are kept track of even if the associated window is
	   not mapped.  This allows the window to be later mapped and have
	   all the past messages. [BDyess]
	*/
	if (flags & MTEAM) {
	    writeMessage(data, message, color, len, winmask, WA_TEAM | WA_REVIEW, backwards);
	    targwin = messWin[WTEAM].window;
	} else if (flags & MINDIV) {
	    writeMessage(data, message, color, len, winmask, WA_INDIV | WA_REVIEW, backwards);
	    targwin = messWin[WINDIV].window;
	} else if (flags == (MMACRO | MALL)) {
	    writeMessage(data, message, color, len, winmask, WA_MACRO | WA_REVIEW, backwards);
	    targwin = messWin[WALL].window;
	} else {
	    writeMessage(data, message, color, len, winmask, WA_ALL | WA_REVIEW, backwards);
	    targwin = messWin[WALL].window;
	}
    }
    /*
       send warnings to warning or message window, if player doesn't have
       messag es mapped
    */
    if ((use_msgw && (targwin == messWin[WINDIV].window || targwin == messWin[WTEAM].window)) ||
	(!W_IsMapped(targwin) && !W_IsMapped(messWin[WREVIEW].window))) {
	if (!messpend && messagew) {	/* don't collide with messages being
					   written! */
	    W_ClearWindow(messagew);
	    W_WriteText(messagew, 5, 5, color, message, len, W_RegularFont);
	} else
	    warning(message);
    }
}

void
dmessage(message, flags, from, to)
    char   *message;
    unsigned int flags, from, to;
/* prints the given message by going though several subroutines.  Here, it first
   creates the data structure that holds the message info and logs the message,
   then sends it on it's subroutine path to print it to the correct window(s).
   This is a good place to handle any special-functions that occur due to
   incoming messages.  The data structure for the scroll back is like this:
   each window has a head pointer that points to the top of its message
   list.  Each list only contains the messages that go to it's window.  The
   lists have pointers to the message itself, so that only one copy of the
   message exists even if it is displayed on several windows.  Each list also
   has a current head pointer that points to the record that is at the bottom
   of that current window.  If the current head pointer and the head pointer
   are different, then the window must be scrolled back.  In such a case, new
   messages are still received but not printed.  [BDyess] 12/07/93 */
{
    struct messageData *data;
    struct distress dist;
    int len;

#ifdef RC_DISTRESS
    /* aha! A new type distress/macro call came in. parse it appropriately */
    if (F_gen_distress && (flags == (MTEAM | MDISTR | MVALID))) {
	HandleGenDistr(message, from, to, &dist);
	len = makedistress(&dist, message, distmacro[dist.distype].macro);
#ifdef BEEPLITE
	if (UseLite)
	    rcdlite(&dist);
#endif
	if (len <= 0)
	    return;
	flags ^= MDISTR;
    }
#endif
    /*
       add create message data struct.  Used for message scrollback
       capability.  [BDyess]
    */
    data = (struct messageData *) malloc(sizeof(struct messageData));
    data->message = (char *) strdup(message);
    data->flags = flags;
    data->from = from;
    data->to = to;
    /*
       keep track of how many queued messages there are for use with the
       infoIcon [BDyess]
    */
    if (infoIcon) {
	if (to == me->p_no && flags & MINDIV) {	/* personal message */
	    me_messages++;
	} else if (flags & MTEAM) {	/* team message */
	    team_messages++;
	} else {		/* message for all */
	    all_messages++;
	}
	if (iconified)
	    drawIcon();
    }
    logit(message);

    /*
       fix for upgrade bug.  Forced UDP would resend numbers, (thinking them
       speed changes) screwing up upgrading on those twinkish sturgeon
       servers. [BDyess]
    */
    if (strncmp(message, "UPG->", 5) == 0)
	upgrading = 1;
    if (upgrading && !(me->p_flags & PFORBIT))
	upgrading = 0;

    if ((from != me->p_no) || pigSelf)
	rsvp_borg_call(message, from);

    /* beep when a personal message is sent while iconified [BDyess] */
    if (to == me->p_no && (flags & MINDIV) && iconified) {
	W_Beep();
    }
    if (from == 255 &&
	strcmp(message, "Tractor beam aborted warp engagement") == 0) {
	me->p_flags &= ~PFWARPPREP;
    }
    print_message(message, flags, from, to, data, NULL, 0);
}


int
instr(string1, string2)
    char   *string1, *string2;
{
    char   *s;
    int     length;

    length = strlen(string2);
    for (s = string1; *s != 0; s++) {
	if (*s == *string2 && strncmp(s, string2, length) == 0)
	    return (1);
    }
    return (0);
}

void
CheckFeatures(m)
    char   *m;
/* I don't know if this works correctly or not.  It is ripped from BRM and has
   been modified a bit to fit.  Right now it doesn't do anything.  [BDyess] */
{
    char    buf[BUFSIZ];
    char   *pek = &m[10];

    if ((int) strlen(m) < 11)
	return;

    while ((*pek == ' ') && (*pek != '\0'))
	pek++;

    strcpy(buf, "Paradise Client: ");

    if (!strcmp(pek, "NO_VIEW_BOX")) {
	allowViewBox = 0;
	strcat(buf, pek);
    }
#ifdef CONTINUOUS_MOUSE
    if (!strcmp(pek, "NO_CONTINUOUS_MOUSE")) {
	allowContinuousMouse = 0;
	strcat(buf, pek);
    }
#endif				/* CONTINUOUS_MOUSE */
    if (!strcmp(pek, "NO_SHOW_ALL_TRACTORS")) {
	allowShowAllTractorPressor = 0;
	strcat(buf, pek);
    }
    if (!strcmp(pek, "HIDE_PLAYERLIST_ON_ENTRY")) {
	allowPlayerlist = 0;
	strcat(buf, pek);
    }
    if (!strcmp(pek, "NO_NEWMACRO")) {
/*      UseNewMacro = 0;*/
	strcat(buf, pek);
    }
    if (!strcmp(pek, "NO_SMARTMACRO")) {
/*      UseSmartMacro = 0;*/
	strcat(buf, pek);
    }
    if (!strcmp(pek, "WHY_DEAD")) {
	why_dead = 1;
	strcat(buf, pek);
    }
    if (!strcmp(pek, "RC_DISTRESS")) {
/*      gen_distress = 1;*/
/*      distmacro = dist_prefered;*/
	strcat(buf, pek);
    }
    /* what the hell is this? - jmn */
    if (!strncmp(pek, "INFO", 4)) {
	strcat(buf, pek);
    }
    if (strlen(buf) == strlen("Paradise Client: ")) {
	strcat(buf, "UNKNOWN FEATURE: ");
	strcat(buf, pek);
    }
    buf[79] = '\0';

    printf("%s\n", buf);

    W_WriteText(messWin[WREVIEW].window, 0, 0, W_White, buf, strlen(buf), 0);
    W_WriteText(messWin[WALL].window, 0, 0, W_White, buf, strlen(buf), 0);
}

void
sendVersion()
{
    static int version_sent = 0;
    char    buf[80];

    if (!version_sent) {
	version_sent = 1;
	sprintf(buf, "@%s", CLIENTVERS);
	pmessage(buf, me->p_no, MINDIV | MCONFIG);
    }
}