view arogue5/rip.c @ 296:000b1c5b8d63

UltraRogue: fix inventory collision after save and restore. Inventory letters are based on "identifiers" stored in objects' o_ident field. Identifiers are allocated by get_ident(), which keeps a list of objects that have them, to avoid giving the same identifier to multiple objects. The list is not stored in the savefile, so after restore, get_ident() was not aware of existing identifiers. This resulted in picked-up objects having the same inventory letters as objects restored from the file. The restore code now adds all objects with identifiers to the list.
author John "Elwin" Edwards
date Mon, 15 Jan 2018 20:20:35 -0500
parents d3968e9cb98d
children 0250220d8cdd
line wrap: on
line source

/*
 * File for the fun ends
 * Death or a total win
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
 * All rights reserved.
 *
 * Based on "Rogue: Exploring the Dungeons of Doom"
 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

/* Print flags for scoring */
#define REALLIFE 1	/* Print out machine and logname */
#define EDITSCORE 2	/* Edit the current score file */
#define ADDSCORE 3	/* Add a new score */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "curses.h"
#include <time.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include "network.h"
#include "rogue.h"
#include "mach_dep.h"

/*
 * If you change this structure, change the compatibility routines
 * scoreout() and scorein() to reflect the change.  Also update SCORELEN.
 */

struct sc_ent {
    unsigned long	sc_score;
    char	sc_name[LINELEN];
    char	sc_system[SYSLEN];
    char	sc_login[LOGLEN];
    short	sc_flgs;
    short	sc_level;
    short	sc_ctype;
    short	sc_monster;
    short	sc_quest;
};

#define SCORELEN \
    (sizeof(unsigned long) + LINELEN + SYSLEN + LOGLEN + 5*sizeof(short))

static char *rip[] = {
"                       __________",
"                      /          \\",
"                     /    REST    \\",
"                    /      IN      \\",
"                   /     PEACE      \\",
"                  /                  \\",
"                  |                  |",
"                  |                  |",
"                  |    killed by     |",
"                  |                  |",
"                  |       1984       |",
"                 *|     *  *  *      | *",
"         ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______",
    0
};

char *killname(short monst);
void scorein(struct sc_ent scores[], FILE *inf);
void scoreout(struct sc_ent scores[], FILE *outf);
void showpack(char *howso);
int update(struct sc_ent top_ten[], unsigned long amount, short quest, 
           char *whoami, short flags, short level, short monst, short ctype, 
           char *system, char *login);


void
byebye(int sig)
{
    NOOP(sig);
    if (!isendwin()) {
        clear();
	endwin();
    }
    printf("\n");
    exit(0);
}


/*
 * death:
 *	Do something really fun when he dies
 */

void
death(short monst)
{
    register char **dp = rip, *killer;
    register struct tm *lt;
    time_t date;
    char buf[80];
    struct tm *localtime();

    time(&date);
    lt = localtime(&date);
    clear();
    move(8, 0);
    while (*dp)
	printw("%s\n", *dp++);
    mvaddstr(14, 28-((strlen(whoami)+1)/2), whoami);
    sprintf(buf, "%lu Points", pstats.s_exp );
    mvaddstr(15, 28-((strlen(buf)+1)/2), buf);
    killer = killname(monst);
    mvaddstr(17, 28-((strlen(killer)+1)/2), killer);
    mvaddstr(18, 26, (sprintf(prbuf, "%4d", 1900+lt->tm_year), prbuf));
    move(LINES-1, 0);
    refresh();
    writelog(pstats.s_exp, KILLED, monst);
    score(pstats.s_exp, KILLED, monst);
    exit(0);
}

char *
killname(short monst)
{
    static char mons_name[LINELEN];
    int i;

    if (monst > NUMMONST) return("a strange monster");

    if (monst >= 0) {
	switch (monsters[monst].m_name[0]) {
	    case 'a':
	    case 'e':
	    case 'i':
	    case 'o':
	    case 'u':
		sprintf(mons_name, "an %s", monsters[monst].m_name);
		break;
	    default:
		sprintf(mons_name, "a %s", monsters[monst].m_name);
	}
	return(mons_name);
    }
    for (i = 0; i< DEATHNUM; i++) {
	if (deaths[i].reason == monst)
	    break;
    }
    if (i >= DEATHNUM)
	return ("strange death");
    return (deaths[i].name);
}


/*
 * score -- figure score and post it.
 */

/* VARARGS2 */
void
score(unsigned long amount, int flags, short monst)
{
    static struct sc_ent top_ten[NUMSCORE];
    register struct sc_ent *scp;
    register int i;
    register struct sc_ent *sc2;
    register char *killer;
    register int prflags = 0;
    short upquest = 0, wintype = 0, uplevel = 0, uptype = 0;	/* For network updating */
    char upsystem[SYSLEN], uplogin[LOGLEN];
    char *thissys;	/* Holds the name of this system */
    char *compatstr=NULL; /* Holds scores for writing compatible score files */
#define REASONLEN 3
    static char *reason[] = {
	"killed",
	"quit",
	"A total winner",
	"somehow left",
    };
    char *packend;
    memset(top_ten,0,sizeof(top_ten));
    signal(SIGINT, byebye);
    if (flags != WINNER && flags != SCOREIT && flags != UPDATE) {
	if (flags == CHICKEN)
	    packend = "when you quit";
	else
	    packend = "at your untimely demise";
	mvaddstr(LINES - 1, 0, retstr);
	refresh();
	wgetnstr(cw,prbuf,80);
	showpack(packend);
    }
    purse = 0;	/* Steal all the gold */

    /*
     * Open file and read list
     */

    if (scoreboard == NULL) 
    {
       printf("\nCannot open score_file.\n");
       return;
    }

    /* Get this system's name */
    thissys = md_gethostname();

    for (scp = top_ten; scp <= &top_ten[NUMSCORE-1]; scp++)
    {
	scp->sc_score = 0L;
	for (i = 0; i < 80; i++)
	    scp->sc_name[i] = rnd(255);
	scp->sc_quest= RN;
	scp->sc_flgs = RN;
	scp->sc_level = RN;
	scp->sc_monster = RN;
	scp->sc_ctype = 0;
	strncpy(scp->sc_system, thissys, SYSLEN);
	scp->sc_login[0] = '\0';
    }

    /*
     * If this is a SCOREIT optin (rogue -s), don't call byebye.  The
     * endwin() call in byebye() will result in a core dump.
     */
    if (flags == SCOREIT) signal(SIGINT, SIG_DFL);
    else signal(SIGINT, byebye);

    if (flags != SCOREIT && flags != UPDATE)
    {
	mvaddstr(LINES - 1, 0, retstr);
	refresh();
	fflush(stdout);
	wgetnstr(stdscr,prbuf,80);
    }

    /* Check for special options */
    if (strcmp(prbuf, "names") == 0)
	prflags = REALLIFE;
#ifdef WIZARD
    else if (wizard) {
	if (strcmp(prbuf, "edit") == 0) prflags = EDITSCORE;
	else if (strcmp(prbuf, "add") == 0) {
	    prflags = ADDSCORE;
	    waswizard = FALSE;	/* We want the new score recorded */
	}
    }
#endif

    /* Read the score and convert it to a compatible format */
    scorein(top_ten, scoreboard);	/* Convert it */

    /* Get some values if this is an update */
    if (flags == UPDATE) {
	int errcheck, errors = 0;

	upquest = (short) netread(&errcheck, sizeof(short), stdin);
	if (errcheck) errors++;

	if (fread(whoami, 1, LINELEN, stdin) != LINELEN) errors++;

	wintype = (short) netread(&errcheck, sizeof(short), stdin);
	if (errcheck) errors++;

	uplevel = (short) netread(&errcheck, sizeof(short), stdin);
	if (errcheck) errors++;

	uptype = (short) netread(&errcheck, sizeof(short), stdin);
	if (errcheck) errors++;

	if (fread(upsystem, 1, SYSLEN, stdin) != SYSLEN)
		errors++;
	if (fread(uplogin, 1, LOGLEN, stdin) != LOGLEN)
		errors++;
	
	if (errors) {
	    fclose(scoreboard);
	    free(compatstr);
	    return;
	}
    }

    /*
     * Insert player in list if need be
     */
    if (!waswizard) {
	char *login = NULL;

	if (flags != UPDATE) {
	    login = md_getusername();
	}

	if (flags == UPDATE)
	    (void) update(top_ten, amount, upquest, whoami, wintype,
		   uplevel, monst, uptype, upsystem, uplogin);
	else {
#ifdef WIZARD
	    if (prflags == ADDSCORE) {	/* Overlay characteristic by new ones */
	 	char buffer[80];
		int atoi();

		clear();
		mvaddstr(1, 0, "Score: ");
		mvaddstr(2, 0, "Quest (number): ");
		mvaddstr(3, 0, "Name: ");
		mvaddstr(4, 0, "System: ");
		mvaddstr(5, 0, "Login: ");
		mvaddstr(6, 0, "Level: ");
		mvaddstr(7, 0, "Char type: ");
		mvaddstr(8, 0, "Result: ");

		/* Get the score */
		move(1, 7);
		get_str(buffer, stdscr);
		amount = atol(buffer);

		/* Get the character's quest -- must be a number */
		move(2, 16);
		get_str(buffer, stdscr);
		quest_item = atoi(buffer);

		/* Get the character's name */
		move(3, 6);
		get_str(buffer, stdscr);
		strncpy(whoami, buffer, LINELEN);

		/* Get the system */
		move(4, 8);
		get_str(buffer, stdscr);
		strncpy(thissys, buffer, SYSLEN);

		/* Get the login */
		move(5, 7);
		get_str(buffer, stdscr);
		strncpy(login, buffer, LOGLEN);

		/* Get the level */
		move(6, 7);
		get_str(buffer, stdscr);
		level = max_level = (short) atoi(buffer);

		/* Get the character type */
		move(7, 11);
		get_str(buffer, stdscr);
		switch (buffer[0]) {
		    case 'F':
		    case 'f':
		    default:
			player.t_ctype = C_FIGHTER;
			break;

		    case 'C':
		    case 'c':
			player.t_ctype = C_CLERIC;
			break;

		    case 'M':
		    case 'm':
			player.t_ctype = C_MAGICIAN;
			break;

		    case 'T':
		    case 't':
			player.t_ctype = C_THIEF;
			break;
		}

		/* Get the win type */
		move(8, 8);
		get_str(buffer, stdscr);
		switch (buffer[0]) {
		    case 'W':
		    case 'w':
		    case 'T':
		    case 't':
			flags = WINNER;
			break;

		    case 'Q':
		    case 'q':
			flags = CHICKEN;
			break;

		    case 'k':
		    case 'K':
		    default:
			flags = KILLED;
			break;
		}

		/* Get the monster if player was killed */
		if (flags == KILLED) {
		    mvaddstr(9, 0, "Death type: ");
		    get_str(buffer, stdscr);
		    if (buffer[0] == 'M' || buffer[0] == 'm')
			monst = makemonster(FALSE);
		    else monst = getdeath();
		}
	    }
#endif

	    if (update(top_ten, amount, (short) quest_item, whoami, flags,
		    (flags == WINNER) ? (short) max_level : (short) level,
		    monst, player.t_ctype, thissys, login)
#ifdef NUMNET
		&& fork() == 0		/* Spin off network process */
#endif
		) {
#ifdef NUMNET
		/* Send this update to the other systems in the network */
		int i, j;
		char cmd[256];	/* Command for remote execution */
		FILE *rmf, *popen();	/* For input to remote command */

		for (i=0; i<NUMNET; i++)
		    if (Network[i].system[0] != '!' &&
			strcmp(Network[i].system, thissys)) {
			sprintf(cmd,
			   "usend -s -d%s -uNoLogin -!'%s -u' - 2>/dev/null",
				Network[i].system, Network[i].rogue);

			/* Execute the command */
			if ((rmf=popen(cmd, "w")) != NULL) {
			    unsigned long temp;	/* Temporary value */

			    /* Write out the parameters */
			    (void) netwrite((unsigned long) amount,
					  sizeof(unsigned long), rmf);

			    (void) netwrite((unsigned long) monst,
					  sizeof(short), rmf);

			    (void) netwrite((unsigned long) quest_item,
					sizeof(short), rmf);

			    (void) fwrite(whoami, 1, strlen(whoami), rmf);
			    for (j=strlen(whoami); j<LINELEN; j++)
				putc('\0', rmf);

			    (void) netwrite((unsigned long) flags,
					  sizeof(short), rmf);

			    temp = (unsigned long)
				(flags==WINNER ? max_level : level);
			    (void) netwrite(temp, sizeof(short), rmf);

			    (void) netwrite((unsigned long) player.t_ctype,
					  sizeof(short), rmf);

			    (void) fwrite(thissys, 1,
						strlen(thissys), rmf);
			    for (j=strlen(thissys); j<SYSLEN; j++)
				putc('\0', rmf);

			    (void) fwrite(login, 1, strlen(login), rmf);
			    for (j=strlen(login); j<LOGLEN; j++)
				putc('\0', rmf);

			    /* Close off the command */
			    (void) pclose(rmf);
			}
		    }
		    _exit(0);	/* Exit network process */
#endif
	    }
	}
    }

    fseek(scoreboard, 0L, 0);
    /*
     * Update the list file
     */
    scoreout(top_ten, scoreboard);
    fclose(scoreboard);

    /*
     * SCOREIT -- rogue -s option.  Never started curses if this option.
     * UPDATE -- network scoring update.  Never started curses if this option.
     * EDITSCORE -- want to delete or change a score.
     */
/*   if (flags != SCOREIT && flags != UPDATE && prflags != EDITSCORE)
	endwin();	*/

    if (flags != UPDATE) {
	if (flags != SCOREIT) {
	    clear();
	    refresh();
	    endwin();
	}
	/*
	* Print the list
	*/
	printf("\nTop %d Adventurers:\nRank     Score\tName\n",
		NUMSCORE);

	for (scp = top_ten; scp <= &top_ten[NUMSCORE-1]; scp++) {
	    char *class;

	    if (scp->sc_score != 0) {
		switch (scp->sc_ctype) {
		    case C_FIGHTER:	class = "fighter";
		    when C_MAGICIAN:	class = "magician";
		    when C_CLERIC:	class = "cleric";
		    when C_THIEF:	class = "thief";
		    otherwise:		class = "unknown";
		}

		/* Make sure we have an in-bound reason */
		if (scp->sc_flgs > REASONLEN) scp->sc_flgs = REASONLEN;

		printf("%3d %10lu\t%s (%s)", scp - top_ten + 1,
		    scp->sc_score, scp->sc_name, class);
   
		if (prflags == REALLIFE) printf(" [in real life %.*s!%.*s]",
				SYSLEN, scp->sc_system, LOGLEN, scp->sc_login);
		printf(":\n\t\t%s on level %d", reason[scp->sc_flgs],
			    scp->sc_level);

		switch (scp->sc_flgs) {
		    case KILLED:
			printf(" by");
			killer = killname(scp->sc_monster);
			printf(" %s", killer);
			break;

		    case WINNER:
			printf(" with the %s",
				rel_magic[scp->sc_quest].mi_name);
			break;
		}

		if (prflags == EDITSCORE)
		{
		    fflush(stdout);
		    fgets(prbuf,80,stdin);
		    printf("\n");
		    if (prbuf[0] == 'd') {
			for (sc2 = scp; sc2 < &top_ten[NUMSCORE-1]; sc2++)
			    *sc2 = *(sc2 + 1);
			top_ten[NUMSCORE-1].sc_score = 0;
			for (i = 0; i < 80; i++)
			    top_ten[NUMSCORE-1].sc_name[i] = rnd(255);
			top_ten[NUMSCORE-1].sc_flgs = RN;
			    top_ten[NUMSCORE-1].sc_level = RN;
			    top_ten[NUMSCORE-1].sc_monster = RN;
			    scp--;
		    }
		    else if (prbuf[0] == 'e') {
			printf("Death type: ");
			fgets(prbuf,80,stdin);
			if (prbuf[0] == 'M' || prbuf[0] == 'm')
			    scp->sc_monster = makemonster(FALSE);
			else scp->sc_monster = getdeath();
			clear();
			refresh();
		    }
		}
		else printf("\n");
	    }
	}
/*	if (prflags == EDITSCORE) endwin(); */	/* End editing windowing */
    }

    if ((flags != SCOREIT) && (flags != UPDATE)) {
        printf("\n[Press return to exit]");
        fflush(stdout);
        fgets(prbuf,80,stdin);
    }
}

void writelog(unsigned long amount, int flags, short monst) {
#ifdef LOGFILE
  char fate[100];
  char *class;
  struct linked_list *item;
  struct object *obj;
  char had_quest = '0';

  if (waswizard)
    return;
  if (logfile == NULL)
    return;
  switch (player.t_ctype) {
    case C_FIGHTER:	class = "Fighter";
    when C_MAGICIAN:	class = "Magician";
    when C_CLERIC:	class = "Cleric";
    when C_THIEF:	class = "Thief";
    otherwise:		class = "Unknown";
  }
  for (item = pack; item != NULL; item = next(item)) {
    obj = OBJPTR(item);
    if (obj->o_type == RELIC && obj->o_which == quest_item)
      had_quest = '1';
  }
  if (flags == KILLED) {
    sprintf(fate, "killed by %s", killname(monst));
  }
  else if (flags == CHICKEN) {
    sprintf(fate, "quit");
  }
  else if (flags == WINNER) {
    sprintf(fate, "escaped");
  }
  else
    return;

  fprintf(logfile, "%d %d %s %d %s %d %d %d %c %s\n", time(NULL), amount, 
          whoami, pstats.s_lvl, class, level, max_level, quest_item, had_quest, 
          fate);
  fclose(logfile);
#endif
  return;
}

/*
 * scorein:
 *	Convert a character string that has been translated from a
 * score file by scoreout() back to a score file structure.
 */
void
scorein(struct sc_ent scores[], FILE *inf)
{
    int i;
    char scoreline[100];

    for(i = 0; i < NUMSCORE; i++)
    {
        encread((char *) &scores[i].sc_name, LINELEN, inf);
        encread((char *) &scores[i].sc_system, SYSLEN, inf);
        encread((char *) &scores[i].sc_login, LINELEN, inf);
        encread((char *) scoreline, 100, inf);
        sscanf(scoreline, " %lu %d %d %d %d %d \n",
        &scores[i].sc_score,   &scores[i].sc_flgs,
        &scores[i].sc_level,   &scores[i].sc_ctype,
        &scores[i].sc_monster, &scores[i].sc_quest);
    }
}

/*
 * scoreout:
 *	Convert a score file structure to a character string.  We do
 * this for compatibility sake since some machines write out fields in
 * different orders.
 */
void
scoreout(struct sc_ent scores[], FILE *outf)
{
    int i;
    char scoreline[100];

    for(i = 0; i < NUMSCORE; i++)  {
        memset(scoreline,0,100); 
        encwrite((char *) scores[i].sc_name, LINELEN, outf); 
        encwrite((char *) scores[i].sc_system, SYSLEN, outf); 
        encwrite((char *) scores[i].sc_login, LINELEN, outf); 
        sprintf(scoreline, " %lu %d %d %d %d %d \n", 
            scores[i].sc_score,  scores[i].sc_flgs, 
            scores[i].sc_level,  scores[i].sc_ctype, 
            scores[i].sc_monster,scores[i].sc_quest);
        encwrite((char *) scoreline, 100, outf); 
    } 
}

/*
 * showpack:
 *	Display the contents of the hero's pack
 */
void
showpack(char *howso)
{
	reg char *iname;
	reg int cnt, packnum;
	reg struct linked_list *item;
	reg struct object *obj;

	idenpack();
	cnt = 1;
	clear();
	mvprintw(0, 0, "Contents of your pack %s:\n",howso);
	packnum = 'a';
	for (item = pack; item != NULL; item = next(item)) {
		obj = OBJPTR(item);
		iname = inv_name(obj, FALSE);
		mvprintw(cnt, 0, "%c) %s\n",packnum++,iname);
		if (++cnt >= LINES - 2 && 
		    next(item) != NULL) {
			cnt = 1;
			mvaddstr(LINES - 1, 0, morestr);
			refresh();
			wait_for(stdscr,' ');
			clear();
		}
	}
	mvprintw(cnt + 1,0,"--- %d  Gold Pieces ---",purse);
	refresh();
}

void
total_winner(void)
{
    register struct linked_list *item;
    register struct object *obj;
    register int worth;
    register char c;
    register int oldpurse;

    clear();
    standout();
    addstr("                                                               \n");
    addstr("  @   @               @   @           @          @@@  @     @  \n");
    addstr("  @   @               @@ @@           @           @   @     @  \n");
    addstr("  @   @  @@@  @   @   @ @ @  @@@   @@@@  @@@      @  @@@    @  \n");
    addstr("   @@@@ @   @ @   @   @   @     @ @   @ @   @     @   @     @  \n");
    addstr("      @ @   @ @   @   @   @  @@@@ @   @ @@@@@     @   @     @  \n");
    addstr("  @   @ @   @ @  @@   @   @ @   @ @   @ @         @   @  @     \n");
    addstr("   @@@   @@@   @@ @   @   @  @@@@  @@@@  @@@     @@@   @@   @  \n");
    addstr("                                                               \n");
    addstr("     Congratulations, you have made it to the light of day!    \n");
    standend();
    addstr("\nYou have joined the elite ranks of those who have escaped the\n");
    addstr("Dungeons of Doom alive.  You journey home and sell all your loot at\n");
    addstr("a great profit and are appointed leader of a ");
    switch (player.t_ctype) {
	case C_MAGICIAN:addstr("magic user's guild.\n");
	when C_FIGHTER:	addstr("fighters guild.\n");
	when C_CLERIC:	addstr("monastery.\n");
	when C_THIEF:	addstr("thief's guild.\n");
	otherwise:	addstr("tavern.\n");
    }
    mvaddstr(LINES - 1, 0, spacemsg);
    refresh();
    wait_for(stdscr,' ');
    clear();
    mvaddstr(0, 0, "   Worth  Item");
    oldpurse = purse;
    for (c = 'a', item = pack; item != NULL; c++, item = next(item))
    {
	obj = OBJPTR(item);
	worth = get_worth(obj);
	if (obj->o_group == 0)
	    worth *= obj->o_count;
	whatis(item);
	mvprintw(c - 'a' + 1, 0, "%c) %6d  %s", c, worth, inv_name(obj, FALSE));
	purse += worth;
    }
    mvprintw(c - 'a' + 1, 0,"   %5d  Gold Pieces          ", oldpurse);
    refresh();
    writelog(pstats.s_exp + (long) purse, WINNER, '\0');
    score(pstats.s_exp + (long) purse, WINNER, '\0');
    exit(0);
}

int
update(struct sc_ent top_ten[], unsigned long amount, short quest, 
       char *whoami, short flags, short level, short monst, short ctype, 
       char *system, char *login)
{
    register struct sc_ent *scp, *sc2;
    int retval=0;	/* 1 if a change, 0 otherwise */

    for (scp = top_ten; scp < &top_ten[NUMSCORE]; scp++) {
	if (amount >= scp->sc_score)
	    break;

#ifdef LIMITSCORE	/* Limits player to one entry per class per uid */
	/* If this good score is the same class and uid, then forget it */
	if (strncmp(scp->sc_login, login, LOGLEN) == 0 &&
	    scp->sc_ctype == ctype &&
	    strncmp(scp->sc_system, system, SYSLEN) == 0) return(0);
#endif
    }

    if (scp < &top_ten[NUMSCORE])
    {
	retval = 1;

#ifdef LIMITSCORE	/* Limits player to one entry per class per uid */
    /* If a lower scores exists for the same login and class, delete it */
    for (sc2 = scp ;sc2 < &top_ten[NUMSCORE]; sc2++) {
	if (sc2->sc_score == 0L) break;	/* End of useful scores */

	if (strncmp(sc2->sc_login, login, LOGLEN) == 0 &&
	    sc2->sc_ctype == ctype &&
	    strncmp(sc2->sc_system, system, SYSLEN) == 0) {
	    /* We want to delete this entry */
	    while (sc2 < &top_ten[NUMSCORE-1]) {
		*sc2 = *(sc2+1);
		sc2++;
	    }
	    sc2->sc_score = 0L;
	}
    }
#endif

	for (sc2 = &top_ten[NUMSCORE-1]; sc2 > scp; sc2--)
	    *sc2 = *(sc2-1);
	scp->sc_score = amount;
	scp->sc_quest = quest;
	strncpy(scp->sc_name, whoami, LINELEN);
	scp->sc_flgs = flags;
	scp->sc_level = level;
	scp->sc_monster = monst;
	scp->sc_ctype = ctype;
	strncpy(scp->sc_system, system, SYSLEN);
	strncpy(scp->sc_login, login, LOGLEN);
    }

    return(retval);
}