extern void sendmud (const char *fmt, ...) __attribute__((__format__(__printf__,1,2))); /**************************************************************** * 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 # include # include # include # include # include # include # include # include # include # include #include #include # include "robot.h" # include "vars.h" #include "zog.h" #include "rand.h" #include "words.h" #include "reply1.h" #include "explore.h" #include "players.h" static void realloc_players(long int); static int valid_user_name(const char *); static const char *unheralded(int, long int); /**************************************************************** * find_player: return player name ****************************************************************/ long int find_player(const 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= 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= 0) { player[me].active = 1; player[me].present = 1; } } /**************************************************************** * saw_player: update player database ****************************************************************/ void saw_player(const char *name, const char *place, const char *desc) { long pl = -1, rm = -1, clock; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void arrive_player(const char *name, const char *place, const char *desc) { long pl = -1, rm = -1, clock; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void leave_player(const char *name, const char *place, const char *desc) { long pl = -1, rm = -1, clock; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void idle_player(const char *name, long int idle) { long pl = -1, clock; if ((pl = add_player (name)) < 0) return; clock = now; player[pl].lastactive = clock - idle; player[pl].active = 1; } /**************************************************************** * active_player: update player database ****************************************************************/ void active_player(const char *name, const char *place, const char *desc) { long pl = -1, rm = -1, clock; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void gave_player(const char *name) { long pl = -1, rm = -1, clock; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void donate_player(const char *name, long int amount) { long pl = -1, rm = -1, clock; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void assault_player(const char *name) { long pl = -1, rm = -1; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void heard_player(const char *name, const char *str) { long pl = -1; char buf[(MSGSIZ+DIALOGSIZE+2)]; const char *s; char *t; long bytes; if ((pl = add_player (name)) < 0) return; 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 ****************************************************************/ void spoke_player(const char *name) { long pl = -1; if ((pl = add_player (name)) < 0) return; 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)) void read_players(const char *fn) { char buf[BIGBUF], msgtxt[BIGBUF], wname[MSGSIZ]; FILE *mfile; long npl = 0, timestmp, cur = -1; 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; } 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; } /* 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:%ld %[^\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, "Whoa! cur %ld > maxplayer %ld.\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; } /**************************************************************** * realloc_players: ****************************************************************/ static void realloc_players(long int 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[%ld..%ld] to new array...\n", i, oldmax-1); for (; i 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 0) { fprintf (outfil, "G:%ld %s\n", heraldtime, herald); } for (i=0; inext) { fprintf (outfil, "M:%ld %s\n", mp->timestmp, mp->text); } } } fclose (outfil); if (swap) swap_files (tmpfile, outname); } /**************************************************************** * player_query ****************************************************************/ void player_query(long int pl, const 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; static int cmp_saw(const void *av, const void *bv) { return (((const P_ORDER *)av)->ago - ((const P_ORDER *)bv)->ago); } void all_player_query(long int dur, const 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 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 ****************************************************************/ static const char * const 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", 0 }; int reserved(const char *word) { const char * const *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 ****************************************************************/ void check_players(void) { register long pl; if (rooms == 0 && players > 0) { for (pl=0; pl 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); } #endif /**************************************************************** * look_at_thing: Find a thing's description ****************************************************************/ void look_at_thing(const 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; } /**************************************************************** * killed_me_today: Return true is 'name' attempted to kill us ****************************************************************/ int killed_me_today(const 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. ****************************************************************/ void object_query(const char *obj, const char *name) { register long pl; int numpl; long printed = 0; if (alone > 2 && msgtype < M_WHISPER) msgtype = M_WHISPER; numpl = players; for (pl=0; pl 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 ****************************************************************/ void asleep_query(const char *name) { char buf[BUFSIZ]; register long pl; long printed = 0; strcpy (buf, ""); for (pl=0; pl 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")) { 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; } /* 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; } /* Check for person asking about robot */ if (streq (pers, lcstr (myname))) { reply ("\"My description is %s.", mydesc); return; } 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; /* * 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 %ld 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 ****************************************************************/ int quote_player(const char *pers, const char *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++; } } // XXX Do we want to set printed in this conditional? // At present, nothing depends on our return value if !gossip. 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 ****************************************************************/ void quote_random_player(void) { 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}."); } } /**************************************************************** * is_jerk: True is player is a jerk ****************************************************************/ int is_jerk(const 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: ****************************************************************/ static int valid_user_name(const 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 ****************************************************************/ void add_msg(long int pl, long int timestmp, const 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 ****************************************************************/ void do_msgs(void) { register long pl; for (pl=0; pl 10 * MINUTES)))) { give_msgs (pl); } } } /**************************************************************** * give_msgs: Assume a player is active, send him/her any messages ****************************************************************/ int give_msgs(long int pl) { MSGS *mp; char *who, msgbuf[MSGSIZ]; const char *errmsg; char recip[MSGSIZ]; register char *s, *t; int is_herald, result = 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; /* 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= 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 %ld 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. ****************************************************************/ static const char *unheralded(int msgtype, long int 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 3) return ("everybody"); /* Now collect their names */ strcpy (buf, ""); for (pl=0; plnext) cnt++; return (cnt); } /**************************************************************** * msg_total: Returns number of messages for player ****************************************************************/ void msg_total(const char *name) { MSGS *mp; long cnt=0, pls=0, pl; for (pl=0; plnext) cnt++; } } if (pl > 0) { reply ("\"I am holding %ld message%s for %ld 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}."); } } #if 0 // not used by anything /**************************************************************** * 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 7 * DAYS) continue; switch (type) { case FRIENDLY: val = (double) player[pl].user1; break; } /* Now find place to insert in sorted list */ for (i=0; ii; 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