/****************************************************************
* players.c: Common robot code for TinyMUD automata
*
* HISTORY
* 01-May-91 Michael Mauldin (mlm) at Carnegie-Mellon University
* Don't page idle players msgs.
*
* 05-May-90 Michael Mauldin (mlm) at Carnegie-Mellon University
* Seventh sequential release. Add the quote player
* command, Avoid saving duplicate player dialog.
* Mods for TinyHELL.
*
* 02-Apr-90 Michael Mauldin (mlm) at Carnegie-Mellon University
* Sixth experimental release. Add time information to
* who_is queries.
*
* 19-Jan-90 Michael Mauldin (mlm) at Carnegie-Mellon University
* Sixth experimental (expand player data in player file)
*
* 09-Jan-90 Michael Mauldin (mlm) at Carnegie-Mellon University
* Fourth general release (save room contents, trap ignore)
*
* 25-Jan-90 Michael Mauldin (mlm) at Carnegie-Mellon University
* Third interim release (allow numeric IP addresses)
*
* 05-Jan-90 Michael Mauldin (mlm) at Carnegie-Mellon University
* Second General Release.
*
* 31-Dec-89 Michael Mauldin (mlm) at Carnegie-Mellon University
* Created.
****************************************************************/
# include <stdio.h>
# include <ctype.h>
# include <time.h>
# include <sys/types.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
# include <setjmp.h>
# include <netinet/in.h>
# include <netdb.h>
# include <ctype.h>
# include <varargs.h>
# include "robot.h"
# include "vars.h"
/****************************************************************
* find_player: return player name
****************************************************************/
long find_player (name)
char *name;
{ register long i;
char pat[MSGSIZ];
if (!name && !*name) return (-1);
strcpy (pat, lcstr (name));
if (streq (pat, "you") || streq (pat, "yourself"))
{ strcpy (pat, lcstr (myname)); }
for (i=0; i<players; i++)
{ if (strcmp (pat, lcstr (player[i].name)) == 0)
{ return (i); }
}
return (-1);
}
/****************************************************************
* close_player: return player name, or speaker name if "me"
****************************************************************/
long close_player (str, who)
char *str, *who;
{ register long i;
char pat[MSGSIZ];
if (!str && !*str) return (-1);
strcpy (pat, lcstr (str));
if (streq (pat, "you") || streq (pat, "yourself"))
{ strcpy (pat, lcstr (myname)); }
else if (streq (pat, "me") || streq (pat, "myself") || streq (pat, "i"))
{ strcpy (pat, lcstr (who)); }
for (i=0; i<players; i++)
{ if (strcmp (pat, lcstr (player[i].name)) == 0)
{ return (i); }
}
return (-1);
}
/****************************************************************
* add_player: return player name
****************************************************************/
long add_player (name)
char *name;
{ register long i;
if (reserved (name)) return (-1);
if ((i = find_player (name)) >= 0)
{ return (i); }
/* Expand array if need be */
if (players >= maxplayer)
{ PLAYER *oldplayer = player;
maxplayer = maxplayer * 6 / 5 + 50;
player_sp = maxplayer * sizeof (PLAYER);
player = (PLAYER *) ralloc (player_sp);
fprintf (stderr, "Util: expanding player array to %ld entries\n",
maxplayer);
if (player == NULL)
{ crash_robot ("Malloc returns NULL in add_player"); }
for (i=0; i<players; i++) player[i] = oldplayer[i];
free (oldplayer);
}
fprintf (stderr, "Play: adding player %s, number %ld\n", name, players);
player[players].name = makestring (name);
player[players].number = 0;
player[players].present = 0;
player[players].active = 0;
player[players].firstsaw = now;
player[players].lastsaw = 0;
player[players].lastspoke = 0;
player[players].lastheard = 0;
player[players].lastplace = -1;
player[players].lastactive = 0;
player[players].lastgave = 0;
player[players].lastdona = 0;
player[players].dontotal = 0;
player[players].lastkill = 0;
player[players].lastoffend = 0;
player[players].flags = 0;
player[players].lastheralded = 0;
player[players].lastlook = 0;
player[players].user1 = 0;
player[players].user2 = 0;
player[players].desc = NULL;
player[players].carry = NULL;
player[players].dialog = NULL;
player[players].email = NULL;
player[players].msgs = NULL;
return (players++);
}
/****************************************************************
* clear_present
****************************************************************/
clear_present ()
{ register long i;
for (i=0; i<players; i++)
{ player[i].present = 0; }
player[me].active = 1;
player[me].present = 1;
}
/****************************************************************
* clear_active
****************************************************************/
clear_active ()
{ register long i;
for (i=0; i<players; i++)
{ player[i].active = 0; }
if (me >= 0)
{ player[me].active = 1;
player[me].present = 1;
}
}
/****************************************************************
* saw_player: update player database
****************************************************************/
saw_player (name, place, desc)
char *name, *place, *desc;
{ long pl = -1, rm = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
if (place && *place) rm = add_room (place, desc);
clock = now;
player[pl].lastsaw = clock;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].present = 1;
}
/****************************************************************
* arrive_player: update player database
****************************************************************/
arrive_player (name, place, desc)
char *name, *place, *desc;
{ long pl = -1, rm = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
if (place && *place) rm = add_room (place, desc);
clock = now;
player[pl].lastsaw = clock;
player[pl].lastactive = clock;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].present = 1;
player[pl].active = 1;
}
/****************************************************************
* leave_player: update player database
****************************************************************/
leave_player (name, place, desc)
char *name, *place, *desc;
{ long pl = -1, rm = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
if (place && *place) rm = add_room (place, desc);
clock = now;
player[pl].lastsaw = clock;
player[pl].lastactive = clock;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].present = 0;
player[pl].active = 1;
}
/****************************************************************
* idle_player: update player database
****************************************************************/
idle_player (name, idle)
char *name;
long idle;
{ long pl = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
clock = now;
player[pl].lastactive = clock - idle;
player[pl].active = 1;
}
/****************************************************************
* active_player: update player database
****************************************************************/
active_player (name, place, desc)
char *name, *place, *desc;
{ long pl = -1, rm = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
if (place && *place && desc && *desc)
{ if (find_room (place, desc) == hererm) player[pl].present = 1;
rm = add_room (place, desc);
}
clock = now;
player[pl].lastactive = clock;
player[pl].lastsaw = clock;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].active = 1;
}
/****************************************************************
* gave_player: update player database
****************************************************************/
gave_player (name, amount)
char *name;
long amount;
{ long pl = -1, rm = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
rm = add_room (here, desc);
clock = now;
player[pl].lastactive = clock;
player[pl].lastsaw = clock;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].lastgave = clock;
}
/****************************************************************
* donate_player: update player database
****************************************************************/
donate_player (name, amount)
char *name;
long amount;
{ long pl = -1, rm = -1, clock;
if ((pl = add_player (name)) < 0) return (-1);
rm = add_room (here, desc);
clock = now;
player[pl].lastactive = clock;
player[pl].lastsaw = clock;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].lastdona = clock;
player[pl].dontotal += amount;
}
/****************************************************************
* assault_player: update player database
****************************************************************/
assault_player (name)
char *name;
{ long pl = -1, rm = -1;
if ((pl = add_player (name)) < 0) return (-1);
rm = add_room (here, desc);
player[pl].lastactive = now;
player[pl].lastsaw = now;
if (rm >= 0) player[pl].lastplace = rm;
player[pl].lastkill = now;
player[pl].present = 1;
player[pl].active = 1;
strcpy (killer, name);
}
/****************************************************************
* heard_player: update player database
****************************************************************/
heard_player (name, str)
char *name, *str;
{ long pl = -1;
char buf[(MSGSIZ+DIALOGSIZE+2)];
register char *s, *t;
long len, bytes;
if ((pl = add_player (name)) < 0) return (-1);
player[pl].lastheard = now;
player[pl].lastactive = now;
player[pl].active = 1;
if (msgtype < M_PAGE)
{ player[pl].present = 1;
if (hererm >= 0) player[pl].lastplace = hererm;
}
/* Track last few lines of dialog - avoid duplicate sentences */
if (str &&
!is_hearts (lcstr (str)) &&
!stlmatch (str, "something to ") &&
!stlmatch (str, "I once heard ") &&
!sindex (str, "quote ") &&
!sindex (lcstr (str), "give me pennies") &&
!is_tell (lcstr (str)) &&
(!isowner (name) ||
!sindex (lcstr (str), "code") &&
!sindex (lcstr (str), "shutdown") &&
!sindex (lcstr (str), "terse") &&
!sindex (lcstr (str), "debug")) &&
(player[pl].dialog == NULL || !sindex (player[pl].dialog, str)))
{ if (player[pl].dialog == NULL)
{ player[pl].dialog = makefixstring ("", DIALOGSIZE); }
/* Copy message into buf, removing vertical bars (|) */
for (s=str, t=buf, bytes=0; *s && bytes < BIGBUF; s++)
{ if (*s != '|')
{ *t++ = *s; bytes++; }
}
/* Append older messages, separated by vertical bars (|) */
*t++ = '|'; bytes++;
for (s=player[pl].dialog; *s && bytes < BIGBUF; )
{ *t++ = *s++; bytes++; }
*t++ = '\0';
if (bytes > (DIALOGSIZE + MSGSIZ))
{ fprintf (stderr, "Warn: heard_player, bytes moved %ld!\n", bytes); }
buf[DIALOGSIZE-1] = '\0';
if ((bytes = strlen (buf)) >= DIALOGSIZE)
{ crash_robot ("buf too long in heard_player, %ld bytes", bytes); }
strcpy (player[pl].dialog, buf);
}
}
/****************************************************************
* spoke_player: update player database
****************************************************************/
spoke_player (name)
char *name;
{ long pl = -1;
if ((pl = add_player (name)) < 0) return (-1);
player[pl].lastspoke = now;
}
/****************************************************************
* read_players: Read in a TinyMud players file
****************************************************************/
#define OLDEST(X,Y) ((X)?((X)<(Y)?(X):(Y)):(Y))
#define NEWEST(X,Y) ((X)>(Y)?(X):(Y))
read_players (fn)
char *fn;
{ char buf[BIGBUF], name[MSGSIZ], msgtxt[BIGBUF], wname[MSGSIZ];
register char *s;
FILE *mfile;
long npl = 0, timestmp, cur = -1, nread = 0;
PLAYER *pp;
long t_number, t_dontotal, t_firstsaw, t_flags, t_lastactive, t_lastdona;
long t_lastgave, t_lastheard, t_lastkill, t_lastlook, t_lastoffend;
long t_lastplace, t_lastsaw, t_lastspoke, t_user1, t_user2;
/* Open the players file */
if ((mfile = fopen (fn, "r")) == NULL)
{ if (access (fn, 0) < 0)
{ fprintf (stderr, "Util: starting with blank players\n");
realloc_players (200);
return (1);
}
else
{ fatal ("Util: can't read %s.\n", fn); }
}
/* Read player file header */
if (!fgets (buf, BIGBUF, mfile))
{ fclose (mfile);
fprintf (stderr,
"Util: null players file, %s, will be over-written\n", fn);
realloc_players (200);
return (1);
}
/* Check for old style players file */
if (stlmatch (buf, "Gloria Players File"))
{ fclose (mfile);
fatal ("Old style players file, use mn6cvt to convert it\n");
}
/* Check for new style header */
if (!stlmatch (buf, "Maas-Neotek Players file"))
{ fclose (mfile);
fatal ("Fatal, '%s' %s\nBad line: %s\n",
fn, "does not have a valid Maas-Neotek players file header", buf);
}
/* Now read room list */
while (fgets (buf, BIGBUF, mfile))
{ buf[strlen (buf) - 1] = '\0';
if (*buf != '\0' && buf[1] != ':')
{ fprintf (stderr, "Warn: bad player line: %s\n", buf);
continue;
}
/* Handle each line differently */
switch (*buf)
{ case 'K': if (npl > 0)
{ fprintf (stderr, "Warning, new K: line %s\n", buf); }
else
{ npl = atol (buf+2);
if (npl <= 0)
{ fclose (mfile);
fprintf (stderr, "Warning in %s, %s\n: %s\n",
fn, "number of players should be positive", buf);
}
if (npl < 200) npl = 200;
realloc_players (npl);
}
break;
case 'G': if (sscanf (buf, "G:%ld %[^\n]", &heraldtime, herald) != 2)
{ *herald = '\0'; heraldtime = 0; }
break;
case 'T':
case 'H':
case 'P':
case 'I': /* Ignore extra information for now */
break;
case 'W': if (sscanf (buf, "W:%[^\n]", wname) == 1)
{ if (!streq (world, wname))
{ fprintf (stderr, "Wrld: new is %s, old was %s\n",
world, wname);
}
}
break;
case 'M': if (cur >= 0)
{ if (sscanf (buf, "M:%d %[^\n]", ×tmp, msgtxt) == 2)
{ add_msg (cur, timestmp, msgtxt); }
else
{ fprintf (stderr, "Warn: bogus M line: %s", buf); }
}
break;
case 'N':
#ifdef DEBUG_PLAYERS
if ((++nread % 100) == 0)
{ fprintf (stderr, "Read %d entries...\n", nread); }
#endif
if (!valid_user_name (buf+2))
{ cur = -1;
fprintf (stderr, "Warn: bogus player name '%s'\n", buf);
}
else if ((cur = find_player (buf+2)) >= 0)
{ if (cur >= maxplayer)
{ fprintf (stderr, "Woah! cur %d > maxplayer 5d.\n",
cur, maxplayer); }
}
else
{ cur = players++;
#ifdef DEBUG_PLAYERS
if ((players % 500) == 0)
{ fprintf (stderr, "Read %d unique players...\n", players); }
#endif
player[cur].name = makestring (buf+2);
}
break;
case 'F': if (cur >= 0)
{ pp = &player[cur];
if (sscanf (buf,
"F:%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
&t_number, &t_dontotal, &t_firstsaw, &t_flags,
&t_lastactive, &t_lastdona, &t_lastgave,
&t_lastheard, &t_lastkill, &t_lastlook,
&t_lastoffend, &t_lastplace, &t_lastsaw,
&t_lastspoke, &t_user1, &t_user2) == 16)
{
pp->number = OLDEST (pp->number, t_number);
pp->dontotal += t_dontotal;
pp->firstsaw = OLDEST (pp->firstsaw, t_firstsaw);
pp->flags |= t_flags;
pp->lastactive = NEWEST (pp->lastactive, t_lastactive);
pp->lastdona = NEWEST (pp->lastdona, t_lastdona);
pp->lastgave = NEWEST (pp->lastgave, t_lastgave);
pp->lastheard = NEWEST (pp->lastheard, t_lastheard);
pp->lastkill = NEWEST (pp->lastkill, t_lastkill);
pp->lastlook = NEWEST (pp->lastlook, t_lastlook);
pp->lastoffend = NEWEST (pp->lastoffend, t_lastoffend);
pp->lastplace = t_lastplace;
pp->lastsaw = NEWEST (pp->lastsaw, t_lastsaw);
pp->lastspoke = NEWEST (pp->lastspoke, t_lastspoke);
pp->user1 = t_user1;
pp->user2 = t_user2;
}
else
{ fprintf (stderr, "Warn: Bogus F line: %s\n", buf); }
}
break;
case 'S': if (cur >= 0)
{ if (player[cur].dialog)
{ strncpy (player[cur].dialog, buf + 2, DIALOGSIZE);
player[cur].dialog[DIALOGSIZE-1] = '\0';
}
else
{ player[cur].dialog = makefixstring (buf + 2, DIALOGSIZE); }
}
break;
case 'D': if (cur >= 0)
{ freestring (player[cur].desc);
player[cur].desc = makestring (buf + 2);
}
break;
case 'C': if (cur >= 0)
{ if (player[cur].carry)
{ strncpy (player[cur].carry, buf + 2, DIALOGSIZE);
player[cur].carry[DIALOGSIZE-1] = '\0';
}
else
{ player[cur].carry = makefixstring (buf + 2, DIALOGSIZE); }
}
break;
case 'E': if (cur >= 0)
{ freestring (player[cur].email);
player[cur].email = makestring (buf + 2);
}
break;
case '#':
case '\0': break;
default: fprintf (stderr, "Warn: bad player file line: %s\n", buf);
}
}
fclose (mfile);
fprintf (stderr, "Util: read %ld players from %s\n", players, fn);
return (1);
}
/****************************************************************
* realloc_players:
****************************************************************/
realloc_players (n)
long n;
{ PLAYER *oldplayer = player;
long oldmax = maxplayer;
register long i;
maxplayer = n+100;
player_sp = maxplayer * sizeof (PLAYER);
player = (PLAYER *) ralloc (player_sp);
fprintf (stderr, "Util: allocating player array of %ld entries\n",
maxplayer);
if (player == NULL)
{ crash_robot ("malloc returns NULL in realloc_players"); }
/* If expanding, copy over old information */
i = 0;
if (oldplayer)
{ fprintf (stderr, "Copying old array[%d..%d] to new array...\n",
i, oldmax-1);
for (; i<oldmax; i++)
{ player[i] = oldplayer[i]; }
free (oldplayer);
}
/* Now zero out the rest of the array */
fprintf (stderr, "Util: clearing out player array from %d to %d...\n",
i, maxplayer);
for (; i<maxplayer; i++)
{ player[i].number = -1;
player[i].present = 0;
player[i].active = 0;
player[i].firstsaw = 0;
player[i].lastsaw = 0;
player[i].lastspoke = 0;
player[i].lastheard = 0;
player[i].lastplace = 0;
player[i].lastactive = 0;
player[i].lastgave = 0;
player[i].lastdona = 0;
player[i].dontotal = 0;
player[i].lastkill = 0;
player[i].lastoffend = 0;
player[i].flags = 0;
player[i].lastheralded = 0;
player[i].lastlook = 0;
player[i].user1 = 0;
player[i].user2 = 0;
player[i].name = NULL;
player[i].desc = NULL;
player[i].carry = NULL;
player[i].dialog = NULL;
player[i].email = NULL;
player[i].msgs = NULL;
}
}
/****************************************************************
* write_players: Write current state of players
****************************************************************/
write_players (outname)
char *outname;
{ register long i;
register O_EXIT *xp;
long swap = 0, newplayers = 0;
char tmpfile[MSGSIZ];
MSGS *mp;
FILE *outfil;
if (players <= 0) return (0);
sprintf (tmpfile, "%s.NEW", outname);
if (access (outname, 0) == 0)
{ swap++;
if ((outfil = fopen (tmpfile, "w")) == 0)
{ perror (tmpfile); return (0); }
}
else
{ if ((outfil = fopen (outname, "w")) == 0)
{ perror (outname); return (0); }
}
# ifndef NO_WEED_PLAYERS
/* Weed out old players that havent been seen for a while */
for (i=0; i<players; i++)
{ long age;
if (player[i].lastactive > 0)
{ age = now - player[i].lastactive; }
else if (player[i].lastsaw > 0)
{ age = now - player[i].lastsaw; }
else
{ age = now - player[i].firstsaw; }
/* Everyone hangs around for 2 months, plus we remember trusties & jerks */
if (age < 60 * DAYS || PLAYER_GET (i, PL_JERK | PL_REMEMBER))
{ newplayers++; continue; }
/* If there are non-entities (no messages or quotes), get rid of them */
if (player[i].dialog == NULL && player[i].msgs == NULL)
{ PLAYER_SET (i, PL_OLD); continue; }
#ifdef WEED_500
/* Other players hang around for 500 days */
if (age < 500 * DAYS)
{ newplayers++; continue; }
#endif
/* If we havent seen them for 500 days, good riddance */
PLAYER_SET (i, PL_OLD); continue;
}
#else
/* Keep everyone */
for (i=0; i<players; i++)
{ PLAYER_CLR (i, PL_OLD); }
newplayers = players;
# endif
fprintf (outfil, "Maas-Neotek Players file: %s\n\n", VERSION);
fprintf (outfil, "K:%ld players\n", newplayers);
fprintf (outfil, "T:%s", ctime (&now));
fprintf (outfil, "W:%s\n", world);
fprintf (outfil, "H:%s\n", mudhost);
fprintf (outfil, "P:%ld\n", mudport);
fprintf (outfil, "I:%s\n", myname);
if (*herald && heraldtime > 0)
{ fprintf (outfil, "G:%ld %s\n", heraldtime, herald); }
for (i=0; i<players; i++)
{ if (player[i].name && player[i].name[0] && !PLAYER_GET (i, PL_OLD))
{ fprintf (outfil, "\nN:%s\n", player[i].name);
fprintf (outfil,
"F:%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\n",
player[i].number, player[i].dontotal, player[i].firstsaw,
player[i].flags, player[i].lastactive, player[i].lastdona,
player[i].lastgave, player[i].lastheard, player[i].lastkill,
player[i].lastlook, player[i].lastoffend, player[i].lastplace,
player[i].lastsaw, player[i].lastspoke, player[i].user1,
player[i].user2);
if (player[i].desc && player[i].desc[0])
{ fprintf (outfil, "D:%s\n", player[i].desc); }
if (player[i].carry && player[i].carry[0])
{ fprintf (outfil, "C:%s\n", player[i].carry); }
if (player[i].email && player[i].email[0])
{ fprintf (outfil, "E:%s\n", player[i].email); }
if (player[i].dialog && player[i].dialog[0])
{ fprintf (outfil, "S:%s\n", player[i].dialog); }
for (mp = player[i].msgs; mp; mp = mp->next)
{ fprintf (outfil, "M:%ld %s\n", mp->timestmp, mp->text); }
}
}
fclose (outfil);
if (swap)
{ return (swap_files (tmpfile, outname)); }
else
{ return (1); }
}
/****************************************************************
* player_query
****************************************************************/
player_query (pl, name)
long pl;
char *name;
{ long dur = now - player[pl].lastsaw;
char answer[BIGBUF];
if (name && *name) strcpy (speaker, name);
/* Four cases: here, seen known place, seen unknown place, not seen */
if (player[pl].present)
{ if (name && streq (name, player[pl].name))
{ sprintf (answer, "\"You are here in %s", here); }
else
{ if (player[pl].active)
{ sprintf (answer, "\"%s is right here in %s",
player[pl].name, here);
}
else
{ sprintf (answer, "\"%s has been asleep here in %s for about %s",
player[pl].name, here,
time_dur (now - player[pl].lastactive));
}
}
}
else if (player[pl].lastplace >= 0 &&
player[pl].lastplace < rooms &&
player[player[pl].lastplace].name &&
player[pl].lastsaw > 0)
{ sprintf (answer, "\"%s was %sin %s about %s ago",
player[pl].name,
(((now - player[pl].lastsaw) > 600 ||
player[pl].active) ? "" : "asleep "),
room_name (player[pl].lastplace), time_dur (dur));
}
else if (player[pl].lastsaw > 0)
{ sprintf (answer, "\"I saw %s about %s ago",
player[pl].name, time_dur (dur));
}
else
{ sprintf (answer, "\"I haven't seen %s", player[pl].name); }
if (name)
{ sprintf (answer, "%s{, n}.", answer); }
else
{ strcat (answer, "."); }
reply ("%s", answer);
}
/****************************************************************
* all_player_query
****************************************************************/
typedef struct astruct { long pl, ago; } P_ORDER;
cmp_saw (a, b)
register P_ORDER *a, *b;
{ return (a->ago - b->ago); }
all_player_query (dur, name)
long dur;
char *name;
{ long limit, recent;
P_ORDER *order;
register long pl, i, cnt;
int numpl;
char who[MSGSIZ];
strcpy (who, name);
if (alone > 2 && msgtype < M_WHISPER) msgtype = M_WHISPER;
limit = now - dur;
recent = now - 300;
numpl = players;
/* Make a permutation array, keyed by "timeago" */
if ((order = (P_ORDER *) ralloc (numpl * sizeof (P_ORDER))) == NULL)
{ crash_robot ("malloc returns NULL in all_player_query"); }
/* Fill the array */
for (i=0; i<numpl; i++)
{ order[i].pl = i; order[i].ago = now - player[i].lastsaw; }
/* Sort by time ago */
qsort (order, numpl, sizeof (P_ORDER), cmp_saw);
/* Now go through the players in order of recency */
for (i = 0, cnt = 0; i < numpl && cnt < 20; i++)
{ pl = order[i].pl;
if (player[pl].name && player[pl].name[0] &&
!streq (player[pl].name, name) &&
!streq (player[pl].name, myname) &&
(player[pl].active || (now - player[pl].lastactive) < 12 * HOURS) &&
((cnt < 10) || (player[pl].lastsaw > recent)))
{ strcpy (speaker, who);
player_query (pl, NULL); cnt++;
}
}
reply ("|done");
/* Free the temporary array before returning */
free (order);
}
/****************************************************************
* reserved: Words that cannot (or should not) be real users
****************************************************************/
char *rw[] = {
"(", "a", "about", "an", "and", "any", "anything", "but", "damn", "few",
"for", "fuck", "haha", "hello", "help", "hey", "hi", "how", "i", "in",
"maybe", "more", "most", "no", "nope", "not", "of", "oh", "out", "piss",
"right", "shit", "some", "test", "testing", "thanks", "that", "the",
"these", "this", "those", "what", "when", "where", "who", "why", "with",
"wrong", "yeah", "yep", "yes", "you", "your", "yup", NULL };
reserved (word)
char *word;
{ register char **s;
for (s=rw; *s; s++)
{ if (strfoldeq (*s, word)) return (1); }
return (0);
}
/****************************************************************
* check_players: If we start a new map, clear lastpace for each
* player
****************************************************************/
check_players ()
{ register long pl;
if (rooms == 0 && players > 0)
{ for (pl=0; pl<players; pl++)
{ player[pl].lastplace = -1; }
fprintf (stderr, "Play: cleared %ld player's lastplace flags\n", players);
}
}
/****************************************************************
* look_up_player: Find a player's tinymud ID number
****************************************************************/
long look_up_player (name)
char *name;
{ long pl;
static long inlook = 0;
if ((pl = find_player (name)) < 0) return (0);
if (player[pl].number > 0) return (player[pl].number);
# ifdef OLD_EXAMINE
if (inlook)
{ fprintf (stderr, "Warn: got recursive look_up_players, dropping one\n");
return (0);
}
lastlock = 0;
inlook++;
sendmud ("%s %s\n@lock me = *%s\nexamin me\n%s %s\n@lock me = me",
opre, numpre, name, opre, outpre);
waitfor (outsuf); /* Eat lock output */
waitfor (outsuf); /* Eat examine output */
waitfor (outsuf); /* Eat relock output */
inlook--;
if (lastlock > 0)
{ player[pl].number = lastlock; }
else
{ lastlock = 0; }
# else
lastlock = 0;
# endif
return (lastlock);
}
/****************************************************************
* look_at_thing: Find a thing's description
****************************************************************/
look_at_thing (name)
char *name;
{ long pl;
if ((pl = find_player (name)) >= 0)
{ player[pl].lastlook = now; }
if (debug)
{ fprintf (stderr, "Look: issuing look at '%s'(%ld)\n", name, pl); }
sendmud ("%s %s %s\nlook %s\n%s %s",
opre, plypre, name, name, opre, outpre);
waitfor (outsuf); /* Eat look output output */
return (1);
}
/****************************************************************
* killed_me_today: Return true is 'name' attempted to kill us
****************************************************************/
killed_me_today (name)
char *name;
{ long pl, dur;
if ((pl = find_player (name)) >= 0)
{ dur = now - player[pl].lastkill;
if (dur < 18 * HOURS)
{ return (dur); }
}
return (0);
}
/****************************************************************
* object_query: Look at player names, descriptions, and inventories.
****************************************************************/
object_query (obj, name)
char *obj;
{ register long pl;
int numpl;
long printed = 0;
if (alone > 2 && msgtype < M_WHISPER) msgtype = M_WHISPER;
numpl = players;
for (pl=0; pl<numpl; pl++)
{ if (sindex (lcstr (player[pl].name), obj) ||
player[pl].desc && sindex (lcstr (player[pl].desc), obj) ||
player[pl].carry && sindex (lcstr (player[pl].carry), obj))
{ if (!printed++)
{ reply ("\"Here are the matches for %s{, n}:", obj); }
if (printed > 10 && (alone > 1 || !isowner (name)))
{ reply ("\"There are more matches, but %s, %s.",
" it's too crowded here to go on", name);
return;
}
strcpy (speaker, name);
reply ("|");
strcpy (speaker, name);
if (player[pl].desc)
{ reply ("| %s's description is: %s", player[pl].name,player[pl].desc);}
strcpy (speaker, name);
if (player[pl].carry)
{ reply ("| %s carries: %s", player[pl].name, player[pl].carry); }
if (!player[pl].desc && !player[pl].carry)
{ strcpy (speaker, name);
reply ("| %s", player[pl].name);
}
}
}
strcpy (speaker, name);
if (!printed)
{ reply ("\"I don't know anyone matching %s{, n}.", obj); }
else
{ reply ("|done."); }
}
/****************************************************************
* asleep_query: Who is asleep in this room
****************************************************************/
asleep_query (name)
char *name;
{ char buf[BUFSIZ];
register long pl;
long printed = 0;
strcpy (buf, "");
for (pl=0; pl<players; pl++)
{ if (player[pl].present && !player[pl].active)
{ if (printed) strcat (buf, " ");
strcat (buf, player[pl].name);
printed++;
}
}
if (!printed)
{ reply ("\"I don't see anyone asleep here{, n}."); }
else
{ reply ("\"Well %s, I see %ld player%s asleep here: %s", name,
printed, printed == 1 ? "" : "s", buf);
}
}
/****************************************************************
* awake_query: Who is awake in this room
****************************************************************/
awake_query (name)
char *name;
{ char buf[BUFSIZ];
register long pl;
long printed = 0;
strcpy (buf, "");
for (pl=0; pl<players; pl++)
{ if (player[pl].present && player[pl].active)
{ if (printed) strcat (buf, " ");
strcat (buf, player[pl].name);
printed++;
}
}
if (!printed)
{ reply ("\"I don't see anyone awake here{, n}."); }
else
{ reply ("\"Well %s, I see %ld player%s awake here: %s", name,
printed, printed == 1 ? "" : "s", buf);
}
}
/****************************************************************
* connected_query: Who is connected
****************************************************************/
connected_query (name)
char *name;
{ char buf[BIGBUF];
register long pl;
long printed = 0, lurk = 0;
wsynch ();
strcpy (buf, "");
for (pl=0; pl<players; pl++)
{ if (player[pl].active && (player[pl].lastactive + 30 * MINUTES > now))
{ if (printed) strcat (buf, " ");
strcat (buf, player[pl].name);
printed++;
}
}
if (!printed)
{ reply ("\"I don't see anyone idle less than 30 minutes{, n}."); }
else
{ reply ("\"Well %s, I see %ld player%s idle less than 30 minutes: %s", name,
printed, printed == 1 ? "" : "s", buf);
}
strcpy (buf, "");
for (pl=0; pl<players; pl++)
{ if (player[pl].active && (player[pl].lastactive + 30 * MINUTES <= now))
{ if (lurk) strcat (buf, " ");
strcat (buf, player[pl].name);
lurk++;
}
}
if (lurk)
{ reply ("\"I %ssee %ld player%s idle more than 30 minutes: %s",
printed ? "also " : "", lurk, lurk == 1 ? "" : "s", buf);
}
}
/****************************************************************
* who_is: Who is a particular player
****************************************************************/
who_is (pers, name)
char *pers, *name;
{ long pl, msgcnt;
long printed=0;
char who[MSGSIZ];
pl = find_player (pers);
strcpy (who, name);
if (msgtype < M_WHISPER) msgtype = M_WHISPER;
/* Unknown player */
if (pl < 0 && streq (name, "(>"))
{ switch (nrrint (189, 3))
{ case 0: zinger ("\"That's the heartsbot{, n}."); break;
case 1: zinger ("\"The hearts playing robot{, n}."); break;
case 2: zinger ("\"(> moderates games of Hearts{, n}."); break;
}
return (1);
}
/* Unknown player */
if (pl < 0)
{ switch (nrrint (190, 5))
{ case 0: reply ("\"I have never seen player %s{, n}.", pers); break;
case 1: reply ("\"I've never seen %s{, n}.", pers); break;
case 2: reply ("\"%s who{, n}?", pers); break;
case 3: reply ("\"I don't know any %s{, n}.", pers); break;
case 4: reply ("\"Never met %s{, n}.",
malep (pers) ? "him" : "her"); break;
}
return (1);
}
/* Check for person asking about robot */
if (streq (pers, lcstr (myname)))
{ reply ("\"My description is %s.", mydesc);
return (1);
}
if (debug)
{ fprintf (stderr, "Who: gave dossier of %s(%ld)\n", pers, pl); }
printed = who_is_hook (pers, name);
if (printed && is_newbie (name)) return (1);
/*
* Need short description for newbies...something that does not sound
* like it came from a computer (no X seconds ago, and no long repetition
* of description
*/
/* Give more info about non-owners */
if (!streq (pers, "fuzzy") || paging)
{
/* Report players last description */
if (!is_newbie (name) && player[pl].number > 0)
{ strcpy (speaker, who);
if (streq (pers, lcstr (name)))
{ reply ("\"Your id number is %ld{, n}", player[pl].number); }
else
{ reply ("\"%s's id number is %ld{, n}",
pers, player[pl].number);
}
printed++;
}
/* Report players last description */
if (player[pl].desc && player[pl].desc[0])
{ strcpy (speaker, who);
if (strfoldeq (pers, name))
{ reply ("\"As of %s ago, your description was: %s",
time_dur (now-player[pl].lastlook), player[pl].desc);
}
else
{ reply ("\"As of %s ago, %s's description was: %s",
time_dur (now-player[pl].lastlook), pers, player[pl].desc);
}
printed++;
}
/* Report players last Email address */
if (player[pl].email && player[pl].email[0])
{ strcpy (speaker, who);
if (strfoldeq (pers, name))
{ reply ("\"Your Email address: %s", player[pl].email); }
else
{ reply ("\"%s's Email address: %s", pers, player[pl].email); }
printed++;
}
if ((msgcnt = msg_count (pl)) > 0)
{ strcpy (speaker, who);
reply ("\"I have %d message%s for %s.",
msgcnt, msgcnt == 1 ? "" : "s", pers);
}
/* Report players last inventory */
if (player[pl].carry && player[pl].carry[0])
{ strcpy (speaker, who);
if (streq (pers, lcstr (name)))
{ reply ("\"You were carrying: %s", player[pl].carry); }
else
{ reply ("\"%s was carrying: %s", pers, player[pl].carry); }
printed++;
}
/* Report first time seen */
if (player[pl].firstsaw > 0)
{ strcpy (speaker, who);
reply ("\"I first saw %s logged in %s ago", pers,
time_dur (now - player[pl].firstsaw));
}
/*---- Report various memory things about people ----*/
/* Juicy quotes */
if (player[pl].dialog && player[pl].dialog[0])
{ strcpy (speaker, who);
quote_player (pers, name, 0); }
/* Report last time assualted */
if (player[pl].lastkill)
{ strcpy (speaker, who);
reply ("\"%s last attacked me %s ago", pers,
time_dur (now - player[pl].lastkill));
}
/* Report last time donated, and total amount */
if (player[pl].lastdona)
{ strcpy (speaker, who);
reply ("\"%s last gave me money %s ago, and %s %ld %s.", pers,
time_dur (now - player[pl].lastdona),
(player[pl].dontotal == 1) ?
"had the generosity to give me" :
"has given me a total of",
player[pl].dontotal,
(player[pl].dontotal == 1) ? "whole penny" : "pennies");
}
/* Report jerk status */
if (PLAYER_GET (pl, PL_JERK))
{ strcpy (speaker, who);
reply ("\"I will not obey commands from %s", pers); }
}
if (!printed)
{ strcpy (speaker, who);
if (streq (pers, lcstr (name)))
{ reply ("\"I don't really know who you are{, n}."); }
else
{ reply ("\"I don't really know who %s is{, n}.", pers); }
}
}
/****************************************************************
* quote_player: Repeat the words of another player
****************************************************************/
quote_player (pers, name, gossip)
char *pers, *name;
int gossip;
{ long pl;
long printed=0;
/* Check for person asking about robot */
if (streq (pers, lcstr (myname)) || streq (pers, "yourself"))
{ switch (nrrint (191, 3))
{ case 0: reply ("\"I'm not very quotable{, n}."); break;
case 1: reply ("\"I'll leave that to others{, n}."); break;
case 2: reply ("\"Sorry, I don't do recursion{, n}."); break;
}
return (1);
}
/* Check for 'me' */
if (streq (pers, "me"))
{ pers = name; }
if (streq (pers, lcstr (name)) && randint (100) < 20)
{ reply ("\"A little vain today, aren't we{, n}?");
return (1);
}
/* Now do lookup */
pl = find_player (pers);
if (debug) fprintf (stderr, "Quote query: %s(%ld)\n", pers, pl);
/* Unknown player */
if (pl < 0)
{ if (gossip) reply ("\"I have never heard player %s{, n}.", pers);
return (1);
}
/* Report various memory things about people */
if (player[pl].dialog && player[pl].dialog[0])
{ register char *s, *t, *head, *tail;
char *sent = NULL;
long cnt=0;
char buf[DIALOGSIZE];
head = player[pl].dialog;
tail = head + strlen (head);
/* Now each '|' (or s == head) is start of sentence */
for (s=head; *s; s++)
{ if (s == head || s[-1] == '|')
{ for (t=s; *t && *t != '|'; t++) ;
if (*t == '|')
{ strcpy (buf, lcstr (s));
buf[t-s] = '\0';
}
else
{ break; }
/* Dont report players queries */
if (sindex (buf, "where is") ||
sindex (buf, "tell") ||
sindex (buf, "how do i g") ||
sindex (buf, "how do you g") ||
stlmatch (buf, "something to") ||
sindex (buf, "who is"))
{ if (debug) fprintf (stderr, "Play: skipping '%s'\n", buf);
continue;
}
if (debug) fprintf (stderr, "Play: using '%s'\n", buf);
if (randint (++cnt) == 0) sent = s;
}
}
/* If we chose a sentence, repeat it */
if (sent)
{ strcpy (buf, sent);
for (s=buf; *s && *s != '|'; s++) ;
*s = '\0';
if (streq (pers, lcstr (name)) || streq (pers, name))
{ reply ("\"I once heard you say, '%s'", buf); }
else
{ reply ("\"I once heard %s say, '%s'", pers, buf); }
printed++;
}
}
if (!printed && gossip)
{ reply ("\"I haven't heard %s say anything quotable{, n}.", pers); }
return (printed);
}
/****************************************************************
* quote_random_player: Repeat the words of another player
****************************************************************/
quote_random_player (name)
char *name;
{ long pl;
long printed=0;
long cnt = 0;
/* Loop through players, finding a quote */
while (!printed && (pl = randint (players)) >= 0 && ++cnt < players)
{
/* Report various memory things about people */
if (player[pl].dialog && player[pl].dialog[0])
{ register char *s, *t, *head, *tail;
char *sent = NULL;
long cnt=0;
char buf[DIALOGSIZE];
head = player[pl].dialog;
tail = head + strlen (head);
/* Now each '|' (or s == head) is start of sentence */
for (s=head; *s; s++)
{ if (s == head || s[-1] == '|')
{ for (t=s; *t && *t != '|'; t++) ;
if (*t == '|')
{ strcpy (buf, lcstr (s));
buf[t-s] = '\0';
}
else
{ break; }
/* Dont report players queries */
if (sindex (buf, "where is") ||
sindex (buf, "tell") ||
sindex (buf, "how do i g") ||
sindex (buf, "how do you g") ||
stlmatch (buf, "something to") ||
sindex (buf, "who is"))
{ if (debug) fprintf (stderr, "Play: skipping '%s'\n", buf);
continue;
}
if (debug) fprintf (stderr, "Play: using '%s'\n", buf);
if (randint (++cnt) == 0) sent = s;
}
}
/* If we chose a sentence, repeat it */
if (sent)
{ strcpy (buf, sent);
for (s=buf; *s && *s != '|'; s++) ;
*s = '\0';
reply ("\"I once heard %s say, '%s'", player[pl].name, buf);
printed++;
}
}
}
if (!printed)
{ reply ("\"I haven't heard any good gossip lately{, n}."); }
return (printed);
}
/****************************************************************
* is_jerk: True is player is a jerk
****************************************************************/
is_jerk (name)
char *name;
{ register long pl;
if ((pl = find_player (name)) < 0) return (0);
if (PLAYER_GET (pl, PL_JERK)) return (1);
return (0);
}
/****************************************************************
* valid_user_name:
****************************************************************/
valid_user_name (str)
register char *str;
{
if (*str == '\0') return (0);
if (reserved (str))
{ return (0); }
while (*str)
{ if (*str < ' ' || *str > '~' || index (" \t=", *str)) return (0);
str++;
}
return (1);
}
/****************************************************************
* add_msg: Add a message to a players input queue
****************************************************************/
add_msg (pl, timestmp, msg)
long pl, timestmp;
char *msg;
{ MSGS *mp, *new;
long mcnt = 0;
if (!(new = (MSGS *) malloc (sizeof (MSGS))))
{ crash_robot ("malloc returns NULL in add_msg"); }
new->text = makestring (msg);
new->timestmp = timestmp;
new->next = NULL;
if ((mp = player[pl].msgs) == NULL)
{ player[pl].msgs = new; }
else
{ for (mcnt=0; mp->next; mp = mp->next) mcnt++;
mp->next = new;
}
}
/***************************************************************
* do_msgs: If someone is logged on or in the room,
* check for their messages and repeat them
****************************************************************/
do_msgs ()
{ register long pl;
MSGS *mp;
char *who, *errmsg;
static last_haven = 0;
for (pl=0; pl<players; pl++)
{ if (player[pl].active &&
(player[pl].msgs || *herald && !PLAYER_GET (pl, PL_HERALDED) ) &&
(now - player[pl].lastactive <= 5 * MINUTES) &&
(player[pl].present ||
pagedmsgs && (!PLAYER_GET(pl, PL_HAVEN) ||
(now - last_haven) > 10 * MINUTES)))
{ give_msgs (pl); }
}
}
/****************************************************************
* give_msgs: Assume a player is active, send him/her any messages
****************************************************************/
give_msgs (pl)
long pl;
{ MSGS *mp;
char *who, *errmsg, msgbuf[MSGSIZ], *unheralded();
char recip[MSGSIZ];
register char *s, *t;
int is_herald, result = 0;
static last_haven = 0;
if (pl == me)
{ PLAYER_SET (pl, PL_HERALDED); return 0; }
if (pl < 0) return 0;
if (now - player[pl].lastactive > 5 * MINUTES) return 0;
who = player[pl].name;
/* last_haven tracks the last time we paged a non-Havened player */
if (!player[pl].present && !PLAYER_GET(pl, PL_HAVEN))
{ last_haven = now; }
/* Clear out recip */
strcpy (recip, "");
/* Deliver any private messages */
while ((is_herald =
(*herald &&
!PLAYER_GET (pl, PL_HERALDED) &&
!PLAYER_GET (pl, PL_HAVEN))) ||
(mp = player[pl].msgs))
{ strcpy (speaker, who);
/* Attempt to deliver herald message */
if (is_herald)
{ strcpy (recip, "");
msgtype = player[pl].present ? M_SPOKEN : M_PAGE;
msgstat = 0;
/* Use %n as a replacement for the name of the recipient */
for (s=herald, t=msgbuf; *s; s++)
{ if (*s == '%')
{ switch (*(++s))
{
/* Name of recipient(s) */
case 'n': strcpy (recip, unheralded (msgtype, pl));
strcpy (t, recip);
while (*t) t++;
break;
case 't': strcpy (t, timeofday (0));
while (*t) t++;
break;
default: *t++ = *s;
break;
}
}
else
{ *t++ = *s; }
}
*t = '\0';
/* Find recipients */
if (*recip == '\0')
{ strcpy (recip, player[pl].name); }
/* Whisper long messages to a single recipient in a crowded
* room, instead of saying them out loud */
if (strlen (msgbuf) > 64 &&
alone > 2 &&
!sindex (recip, ", ") &&
!streq (recip, "everybody") &&
msgtype == M_SPOKEN)
{ msgtype = M_WHISPER; }
fprintf (stderr, "Hrld: [%s to %s] \"%s\"\n",
(msgtype == M_WHISPER ? "whisper" :
msgtype == M_SPOKEN ? "say" : "page"),
recip, msgbuf);
/* Deliver the message */
unlogged ("\"%s", msgbuf);
}
/* Attempt to deliver private message */
else
{ msgtype = player[pl].present ? M_WHISPER : M_PAGE;
msgstat = 0;
fprintf (stderr, "Dlvr: to %s, %s old\n",
player[pl].name, exact_dur (now - mp->timestmp));
unlogged ("\"%s ago, %s", time_dur (now - mp->timestmp), mp->text);
}
/* Mark herald bits on recipients */
if (msgstat >= 0 && is_herald)
{ if (msgtype == M_SPOKEN)
{ long i;
for (i=0; i<players; i++)
{ if (player[i].present && player[i].active)
{ PLAYER_SET (i, PL_HERALDED);
player[i].lastheralded = now;
}
}
}
else
{ PLAYER_SET (pl, PL_HERALDED);
player[pl].lastheralded = now;
result++;
}
}
/* Remove message if delivery successful */
else if (msgstat >= 0)
{ player[pl].msgs = mp->next;
freestring (mp->text);
free (mp);
PLAYER_CLR(pl, PL_HAVEN);
result++;
}
/* Error message to log if failed */
else
{ switch (msgstat)
{ case 1: errmsg = "success"; break;
case 0: errmsg = "not yet sent"; break;
case -1: player[pl].present = 0;
errmsg = "whisper to player not present"; break;
case -2: player[pl].active = 0;
errmsg = "recipient disconnected"; break;
case -3: player[pl].active = 0;
errmsg = "no such player"; break;
case -4: PLAYER_SET(pl, PL_HAVEN);
errmsg = "recipient set haven"; break;
default: errmsg = "unknown error";
}
fprintf (stderr,
"Warn: msg type %d to %s gave status %d (%s)\n",
msgtype, who, msgstat, errmsg);
break;
}
}
return (result);
}
/****************************************************************
* unheralded: Names of people present who havent gotten the herald
* if more than 3 names, use "everybody". If msg is not
* spoken, just return name.
****************************************************************/
char *unheralded (msgtype, pl)
int msgtype, pl;
{ static char buf[MSGSIZ];
register char *s = buf;
int total = 0, count = 0;
if (msgtype != M_SPOKEN)
{ return (player[pl].name); }
/* First count recipients */
for (pl=0; pl<players; pl++)
{ if (player[pl].active && player[pl].present &&
!PLAYER_GET (pl, PL_HERALDED))
{ total++; }
}
if (total > 3) return ("everybody");
/* Now collect their names */
strcpy (buf, "");
for (pl=0; pl<players; pl++)
{ if (player[pl].active && player[pl].present &&
!PLAYER_GET (pl, PL_HERALDED))
{ if (++count == 1)
{ strcpy (buf, player[pl].name); }
else
{ strcat (buf, count == total ? " and " : ", ");
strcat (buf, player[pl].name);
}
}
}
return (s);
}
/****************************************************************
* msg_count: Returns number of messages for player
****************************************************************/
long msg_count (pl)
long pl;
{ MSGS *mp;
long cnt=0;
for (mp = player[pl].msgs; mp; mp = mp->next) cnt++;
return (cnt);
}
/****************************************************************
* msg_total: Returns number of messages for player
****************************************************************/
long msg_total (name)
char *name;
{ MSGS *mp;
long cnt=0, pls=0, pl;
for (pl=0; pl<players; pl++)
{ if (mp = player[pl].msgs)
{ pls++;
for (; mp; mp = mp->next) cnt++;
}
}
if (pl > 0)
{ reply ("\"I am holding %d message%s for %d player%s, %s.",
cnt, cnt==1 ? "" : "s", pls, pls == 1 ? "" : "s", name);
}
else
{ reply ("\"I don't have any messages for anyone right now{, n}."); }
}
/****************************************************************
* most_query: Which room(s) have the most of something
****************************************************************/
# define QCNT 10
most_pl_query (name, type)
char *name;
int type;
{ long pl, b, i, j, best[QCNT], bcnt = 0;
double val, bestval[QCNT];
/* Search through every room */
for (pl=0; pl<players; pl++)
{ if ((now - player[pl].lastspoke) > 7 * DAYS) continue;
switch (type)
{ case FRIENDLY: val = (double) player[pl].user1; break;
}
/* Now find place to insert in sorted list */
for (i=0; i<bcnt; i++)
{ if (bestval[i] < val)
{
/* Move the rest of the list down by 1 */
for (j=QCNT-1; j>i; j--)
{ best[j] = best[j-1]; bestval[j] = bestval[j-1]; }
/* Now insert this room */
best[i] = pl; bestval[i] = val;
if (bcnt < QCNT) bcnt++;
break;
}
}
/* If we ran off the list, but it is short, add to the end */
if (i == bcnt && bcnt < QCNT)
{ best[bcnt] = pl;
bestval[bcnt] = val;
bcnt++;
}
}
/* best[0..bcnt-1] and bestval[0..bcnt-1] now hold sorted list */
switch (type)
{ case FRIENDLY: reply ("\"The %d friendliest players are:",
QCNT);
for (b=0; b<bcnt; b++)
{ strcpy (speaker, name);
reply ("| %s spoke to me %1.0lf times.",
player[best[b]].name, bestval[b]);
}
break;
}
}