NetBSD/games/larn/scores.c

856 lines
23 KiB
C

/* $NetBSD: scores.c,v 1.22 2021/05/02 12:50:45 rillig Exp $ */
/*
* scores.c Larn is copyrighted 1986 by Noah Morgan.
*
* Functions in this file are:
*
* readboard() Function to read in the scoreboard into a static buffer
* writeboard() Function to write the scoreboard from readboard()'s buffer
* makeboard() Function to create a new scoreboard (wipe out old one)
* hashewon() Function to return 1 if player has won a game before, else 0
* long paytaxes(x) Function to pay taxes if any are due winshou()
* ubroutine to print out the winning scoreboard shou(x)
* ubroutine to print out the non-winners scoreboard showscores()
* unction to show the scoreboard on the terminal showallscores()
* Function to show scores and the iven lists that go with them sortboard()
* unction to sort the scoreboard newscore(score, whoo, whyded, winner)
* Function to add entry to scoreboard new1sub(score,i,whoo,taxes)
* Subroutine to put player into a new2sub(score,i,whoo,whyded)
* Subroutine to put player into a died(x) Subroutine to record who
* played larn, and what the score was diedsub(x) Subroutine to print out a
* line showing player when he is killed diedlog() Subroutine to read a
* log file and print it out in ascii format getplid(name)
* on to get players id # from id file
*
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: scores.c,v 1.22 2021/05/02 12:50:45 rillig Exp $");
#endif /* not lint */
#include <sys/types.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "header.h"
#include "extern.h"
struct scofmt { /* This is the structure for the scoreboard */
long score; /* the score of the player */
long suid; /* the user id number of the player */
short what; /* the number of the monster that killed
* player */
short level; /* the level player was on when he died */
short hardlev;/* the level of difficulty player played at */
short order; /* the relative ordering place of this entry */
char who[40];/* the name of the character */
char sciv[26][2]; /* this is the inventory list of the
* character */
};
struct wscofmt { /* This is the structure for the winning
* scoreboard */
long score; /* the score of the player */
long timeused; /* the time used in mobuls to win the
* game */
long taxes; /* taxes he owes to LRS */
long suid; /* the user id number of the player */
short hardlev;/* the level of difficulty player played at */
short order; /* the relative ordering place of this entry */
char who[40];/* the name of the character */
};
struct log_fmt { /* 102 bytes struct for the log file */
long score; /* the players score */
int32_t diedtime; /* time when game was over */
short cavelev;/* level in caves */
short diff; /* difficulty player played at */
#ifdef EXTRA
long elapsedtime; /* real time of game in seconds */
long bytout; /* bytes input and output */
long bytin;
long moves; /* number of moves made by player */
short ac; /* armor class of player */
short hp, hpmax; /* players hitpoints */
short cputime;/* CPU time needed in seconds */
short killed, spused; /* monsters killed and spells cast */
short usage; /* usage of the CPU in % */
short lev; /* player level */
#endif
char who[12];/* player name */
char what[46]; /* what happened to player */
};
static struct scofmt sco[SCORESIZE]; /* the structure for the scoreboard */
static struct wscofmt winr[SCORESIZE]; /* struct for the winning scoreboard */
static struct log_fmt logg; /* structure for the log file */
static const char *whydead[] = {
"quit", "suspended", "self - annihilated", "shot by an arrow",
"hit by a dart", "fell into a pit", "fell into a bottomless pit",
"a winner", "trapped in solid rock", "killed by a missing save file",
"killed by an old save file", "caught by the greedy cheater checker trap",
"killed by a protected save file", "killed his family and committed suicide",
"erased by a wayward finger", "fell through a bottomless trap door",
"fell through a trap door", "drank some poisonous water",
"fried by an electric shock", "slipped on a volcano shaft",
"killed by a stupid act of frustration", "attacked by a revolting demon",
"hit by his own magic", "demolished by an unseen attacker",
"fell into the dreadful sleep", "killed by an exploding chest",
/* 26 */ "killed by a missing maze data file", "annihilated in a sphere",
"died a post mortem death", "wasted by a malloc() failure"
};
static int readboard(void);
static int writeboard(void);
static int winshou(void);
static int shou(int);
static int sortboard(void);
static void newscore(long, char *, int, int);
static void new1sub(long, int, char *, long);
static void new2sub(long, int, char *, int);
static void diedsub(int);
/*
* readboard() Function to read in the scoreboard into a static buffer
*
* returns -1 if unable to read in the scoreboard, returns 0 if all is OK
*/
static int
readboard(void)
{
int i;
if (gid != egid)
setegid(egid);
i = lopen(scorefile);
if (gid != egid)
setegid(gid);
if (i < 0) {
lprcat("Can't read scoreboard\n");
lflush();
return (-1);
}
lrfill((char *) sco, sizeof(sco));
lrfill((char *) winr, sizeof(winr));
lrclose();
lcreat((char *) 0);
return (0);
}
/*
* writeboard() Function to write the scoreboard from readboard()'s buffer
*
* returns -1 if unable to write the scoreboard, returns 0 if all is OK
*/
static int
writeboard(void)
{
int i;
set_score_output();
if (gid != egid)
setegid(egid);
i = lcreat(scorefile);
if (gid != egid)
setegid(gid);
if (i < 0) {
lprcat("Can't write scoreboard\n");
lflush();
return (-1);
}
lwrite((char *) sco, sizeof(sco));
lwrite((char *) winr, sizeof(winr));
lwclose();
lcreat((char *) 0);
return (0);
}
/*
* makeboard() Function to create a new scoreboard (wipe out old one)
*
* returns -1 if unable to write the scoreboard, returns 0 if all is OK
*/
int
makeboard(void)
{
int i;
set_score_output();
for (i = 0; i < SCORESIZE; i++) {
winr[i].taxes = winr[i].score = sco[i].score = 0;
winr[i].order = sco[i].order = i;
}
if (writeboard())
return (-1);
if (gid != egid)
setegid(egid);
chmod(scorefile, 0660);
if (gid != egid)
setegid(gid);
return (0);
}
/*
* hashewon() Function to return 1 if player has won a game before, else 0
*
* This function also sets c[HARDGAME] to appropriate value -- 0 if not a
* winner, otherwise the next level of difficulty listed in the winners
* scoreboard. This function also sets outstanding_taxes to the value in
* the winners scoreboard.
*/
int
hashewon(void)
{
int i;
c[HARDGAME] = 0;
if (readboard() < 0)
return (0); /* can't find scoreboard */
for (i = 0; i < SCORESIZE; i++) /* search through winners scoreboard */
if (winr[i].suid == userid)
if (winr[i].score > 0) {
c[HARDGAME] = winr[i].hardlev + 1;
outstanding_taxes = winr[i].taxes;
return (1);
}
return (0);
}
/*
* long paytaxes(x) Function to pay taxes if any are due
*
* Enter with the amount (in gp) to pay on the taxes.
* Returns amount actually paid.
*/
long
paytaxes(long x)
{
int i;
long amt;
if (x < 0)
return (0L);
if (readboard() < 0)
return (0L);
for (i = 0; i < SCORESIZE; i++)
if (winr[i].suid == userid) /* look for players winning
* entry */
if (winr[i].score > 0) { /* search for a winning
* entry for the player */
amt = winr[i].taxes;
if (x < amt)
amt = x; /* don't overpay taxes
* (Ughhhhh) */
winr[i].taxes -= amt;
outstanding_taxes -= amt;
set_score_output();
if (writeboard() < 0)
return (0);
return (amt);
}
return (0L); /* couldn't find user on winning scoreboard */
}
/*
* winshou() Subroutine to print out the winning scoreboard
*
* Returns the number of players on scoreboard that were shown
*/
static int
winshou(void)
{
struct wscofmt *p;
int i, j, count;
for (count = j = i = 0; i < SCORESIZE; i++) /* is there anyone on
* the scoreboard? */
if (winr[i].score != 0) {
j++;
break;
}
if (j) {
lprcat("\n Score Difficulty Time Needed Larn Winners List\n");
for (i = 0; i < SCORESIZE; i++) /* this loop is needed to
* print out the */
for (j = 0; j < SCORESIZE; j++) { /* winners in order */
p = &winr[j]; /* pointer to the scoreboard
* entry */
if (p->order == i) {
if (p->score) {
count++;
lprintf("%10ld %2ld %5ld Mobuls %s \n",
(long) p->score, (long) p->hardlev, (long) p->timeused, p->who);
}
break;
}
}
}
return (count); /* return number of people on scoreboard */
}
/*
* shou(x) Subroutine to print out the non-winners scoreboard
* int x;
*
* Enter with 0 to list the scores, enter with 1 to list inventories too
* Returns the number of players on scoreboard that were shown
*/
static int
shou(int x)
{
int i, j, n, k;
int count;
for (count = j = i = 0; i < SCORESIZE; i++) /* is the scoreboard
* empty? */
if (sco[i].score != 0) {
j++;
break;
}
if (j) {
lprcat("\n Score Difficulty Larn Visitor Log\n");
for (i = 0; i < SCORESIZE; i++) /* be sure to print them out
* in order */
for (j = 0; j < SCORESIZE; j++)
if (sco[j].order == i) {
if (sco[j].score) {
count++;
lprintf("%10ld %2ld %s ",
(long) sco[j].score, (long) sco[j].hardlev, sco[j].who);
if (sco[j].what < 256)
lprintf("killed by a %s", monster[sco[j].what].name);
else
lprintf("%s", whydead[sco[j].what - 256]);
if (x != 263)
lprintf(" on %s", levelname[sco[j].level]);
if (x) {
for (n = 0; n < 26; n++) {
iven[n] = sco[j].sciv[n][0];
ivenarg[n] = sco[j].sciv[n][1];
}
for (k = 1; k < 99; k++)
for (n = 0; n < 26; n++)
if (k == iven[n]) {
srcount = 0;
show3(n);
}
lprcat("\n\n");
} else
lprc('\n');
}
j = SCORESIZE;
}
}
return (count); /* return the number of players just shown */
}
/*
* showscores() Function to show the scoreboard on the terminal
*
* Returns nothing of value
*/
static char esb[] = "The scoreboard is empty.\n";
void
showscores(void)
{
int i, j;
lflush();
lcreat((char *) 0);
if (readboard() < 0)
return;
i = winshou();
j = shou(0);
if (i + j == 0)
lprcat(esb);
else
lprc('\n');
lflush();
}
/*
* showallscores() Function to show scores and the iven lists that go with them
*
* Returns nothing of value
*/
void
showallscores(void)
{
int i, j;
lflush();
lcreat((char *) 0);
if (readboard() < 0)
return;
c[WEAR] = c[WIELD] = c[SHIELD] = -1; /* not wielding or wearing
* anything */
for (i = 0; i < MAXPOTION; i++)
potionname[i] = potionhide[i];
for (i = 0; i < MAXSCROLL; i++)
scrollname[i] = scrollhide[i];
i = winshou();
j = shou(1);
if (i + j == 0)
lprcat(esb);
else
lprc('\n');
lflush();
}
/*
* sortboard() Function to sort the scoreboard
*
* Returns 0 if no sorting done, else returns 1
*/
static int
sortboard(void)
{
int i, j = 0, pos;
long jdat;
for (i = 0; i < SCORESIZE; i++)
sco[i].order = winr[i].order = -1;
pos = 0;
while (pos < SCORESIZE) {
jdat = 0;
for (i = 0; i < SCORESIZE; i++)
if ((sco[i].order < 0) && (sco[i].score >= jdat)) {
j = i;
jdat = sco[i].score;
}
sco[j].order = pos++;
}
pos = 0;
while (pos < SCORESIZE) {
jdat = 0;
for (i = 0; i < SCORESIZE; i++)
if ((winr[i].order < 0) && (winr[i].score >= jdat)) {
j = i;
jdat = winr[i].score;
}
winr[j].order = pos++;
}
return (1);
}
/*
* newscore(score, whoo, whyded, winner) Function to add entry to scoreboard
* int score, winner, whyded;
* char *whoo;
*
* Enter with the total score in gp in score, players name in whoo,
* died() reason # in whyded, and TRUE/FALSE in winner if a winner
* ex. newscore(1000, "player 1", 32, 0);
*/
static void
newscore(long score, char *whoo, int whyded, int winner)
{
int i;
long taxes;
if (readboard() < 0)
return; /* do the scoreboard */
/* if a winner then delete all non-winning scores */
if (cheat)
winner = 0; /* if he cheated, don't let him win */
if (winner) {
for (i = 0; i < SCORESIZE; i++)
if (sco[i].suid == userid)
sco[i].score = 0;
taxes = score * TAXRATE;
score += 100000 * c[HARDGAME]; /* bonus for winning */
/*
* if he has a slot on the winning scoreboard update it if
* greater score
*/
for (i = 0; i < SCORESIZE; i++)
if (winr[i].suid == userid) {
new1sub(score, i, whoo, taxes);
return;
}
/*
* he had no entry. look for last entry and see if he has a
* greater score
*/
for (i = 0; i < SCORESIZE; i++)
if (winr[i].order == SCORESIZE - 1) {
new1sub(score, i, whoo, taxes);
return;
}
} else if (!cheat) { /* for not winning scoreboard */
/*
* if he has a slot on the scoreboard update it if greater
* score
*/
for (i = 0; i < SCORESIZE; i++)
if (sco[i].suid == userid) {
new2sub(score, i, whoo, whyded);
return;
}
/*
* he had no entry. look for last entry and see if he has a
* greater score
*/
for (i = 0; i < SCORESIZE; i++)
if (sco[i].order == SCORESIZE - 1) {
new2sub(score, i, whoo, whyded);
return;
}
}
}
/*
* new1sub(score,i,whoo,taxes) Subroutine to put player into a
* int score,i,whyded,taxes; winning scoreboard entry if his score
* char *whoo; is high enough
*
* Enter with the total score in gp in score, players name in whoo,
* died() reason # in whyded, and TRUE/FALSE in winner if a winner
* slot in scoreboard in i, and the tax bill in taxes.
* Returns nothing of value
*/
static void
new1sub(long score, int i, char *whoo, long taxes)
{
struct wscofmt *p;
p = &winr[i];
p->taxes += taxes;
if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
strcpy(p->who, whoo);
p->score = score;
p->hardlev = c[HARDGAME];
p->suid = userid;
p->timeused = gltime / 100;
}
}
/*
* new2sub(score,i,whoo,whyded) Subroutine to put player into a
* int score,i,whyded,taxes; non-winning scoreboard entry if his
* char *whoo; score is high enough
*
* Enter with the total score in gp in score, players name in whoo,
* died() reason # in whyded, and slot in scoreboard in i.
* Returns nothing of value
*/
static void
new2sub(long score, int i, char *whoo, int whyded)
{
int j;
struct scofmt *p;
p = &sco[i];
if ((score >= p->score) || (c[HARDGAME] > p->hardlev)) {
strcpy(p->who, whoo);
p->score = score;
p->what = whyded;
p->hardlev = c[HARDGAME];
p->suid = userid;
p->level = level;
for (j = 0; j < 26; j++) {
p->sciv[j][0] = iven[j];
p->sciv[j][1] = ivenarg[j];
}
}
}
/*
* died(x) Subroutine to record who played larn, and what the score was
* int x;
*
* if x < 0 then don't show scores
* died() never returns! (unless c[LIFEPROT] and a reincarnatable death!)
*
* < 256 killed by the monster number
* 256 quit
* 257 suspended
* 258 self - annihilated
* 259 shot by an arrow
* 260 hit by a dart
* 261 fell into a pit
* 262 fell into a bottomless pit
* 263 a winner
* 264 trapped in solid rock
* 265 killed by a missing save file
* 266 killed by an old save file
* 267 caught by the greedy cheater checker trap
* 268 killed by a protected save file
* 269 killed his family and killed himself
* 270 erased by a wayward finger
* 271 fell through a bottomless trap door
* 272 fell through a trap door
* 273 drank some poisonous water
* 274 fried by an electric shock
* 275 slipped on a volcano shaft
* 276 killed by a stupid act of frustration
* 277 attacked by a revolting demon
* 278 hit by his own magic
* 279 demolished by an unseen attacker
* 280 fell into the dreadful sleep
* 281 killed by an exploding chest
* 282 killed by a missing maze data file
* 283 killed by a sphere of annihilation
* 284 died a post mortem death
* 285 malloc() failure
* 300 quick quit -- don't put on scoreboard
*/
static int scorerror;
void
died(int x)
{
int f, win;
char ch;
const char *mod;
time_t zzz;
if (c[LIFEPROT] > 0) { /* if life protection */
switch ((x > 0) ? x : -x) {
case 256:
case 257:
case 262:
case 263:
case 265:
case 266:
case 267:
case 268:
case 269:
case 271:
case 282:
case 284:
case 285:
case 300:
goto invalid; /* can't be saved */
};
--c[LIFEPROT];
c[HP] = 1;
--c[CONSTITUTION];
cursors();
lprcat("\nYou feel wiiieeeeerrrrrd all over! ");
beep();
lflush();
sleep(4);
return; /* only case where died() returns */
}
invalid:
clearvt100();
lflush();
f = 0;
if (ckpflag)
unlink(ckpfile);/* remove checkpoint file if used */
if (x < 0) {
f++;
x = -x;
} /* if we are not to display the scores */
if ((x == 300) || (x == 257))
exit(0); /* for quick exit or saved game */
if (x == 263)
win = 1;
else
win = 0;
c[GOLD] += c[BANKACCOUNT];
c[BANKACCOUNT] = 0;
/* now enter the player at the end of the scoreboard */
newscore(c[GOLD], logname, x, win);
diedsub(x); /* print out the score line */
lflush();
set_score_output();
if ((wizard == 0) && (c[GOLD] > 0)) { /* wizards can't score */
#ifndef NOLOG
if (gid != egid)
setegid(egid);
if (lappend(logfile) < 0) { /* append to file */
if (lcreat(logfile) < 0) { /* and can't create new
* log file */
lcreat((char *) 0);
lprcat("\nCan't open record file: I can't post your score.\n");
sncbr();
resetscroll();
lflush();
exit(0);
}
if (gid != egid)
setegid(egid);
chmod(logfile, 0660);
if (gid != egid)
setegid(gid);
}
if (gid != egid)
setegid(gid);
strcpy(logg.who, loginname);
logg.score = c[GOLD];
logg.diff = c[HARDGAME];
if (x < 256) {
ch = *monster[x].name;
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
mod = "an";
else
mod = "a";
snprintf(logg.what, sizeof(logg.what),
"killed by %s %s", mod, monster[x].name);
} else
snprintf(logg.what, sizeof(logg.what),
"%s", whydead[x - 256]);
logg.cavelev = level;
time(&zzz); /* get CPU time -- write out score info */
logg.diedtime = zzz;
#ifdef EXTRA
times(&cputime);/* get CPU time -- write out score info */
logg.cputime = i = (cputime.tms_utime + cputime.tms_stime) / 60 + c[CPUTIME];
logg.lev = c[LEVEL];
logg.ac = c[AC];
logg.hpmax = c[HPMAX];
logg.hp = c[HP];
logg.elapsedtime = (zzz - initialtime + 59) / 60;
logg.usage = (10000 * i) / (zzz - initialtime);
logg.bytin = c[BYTESIN];
logg.bytout = c[BYTESOUT];
logg.moves = c[MOVESMADE];
logg.spused = c[SPELLSCAST];
logg.killed = c[MONSTKILLED];
#endif
lwrite((char *) &logg, sizeof(struct log_fmt));
lwclose();
#endif /* NOLOG */
/*
* now for the scoreboard maintenance -- not for a suspended
* game
*/
if (x != 257) {
if (sortboard()) {
set_score_output();
scorerror = writeboard();
}
}
}
if ((x == 256) || (x == 257) || (f != 0))
exit(0);
if (scorerror == 0)
showscores(); /* if we updated the scoreboard */
if (x == 263)
mailbill();
exit(0);
}
/*
* diedsub(x) Subroutine to print out the line showing the player when he is killed
* int x;
*/
static void
diedsub(int x)
{
char ch;
const char *mod;
lprintf("Score: %ld, Diff: %ld, %s ", (long) c[GOLD], (long) c[HARDGAME], logname);
if (x < 256) {
ch = *monster[x].name;
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
mod = "an";
else
mod = "a";
lprintf("killed by %s %s", mod, monster[x].name);
} else
lprintf("%s", whydead[x - 256]);
if (x != 263)
lprintf(" on %s\n", levelname[level]);
else
lprc('\n');
}
/*
* diedlog() Subroutine to read a log file and print it out in ascii format
*/
void
diedlog(void)
{
int n;
char *p;
static char q[] = "?";
struct stat stbuf;
time_t t;
lcreat((char *) 0);
if (lopen(logfile) < 0) {
lprintf("Can't locate log file <%s>\n", logfile);
return;
}
if (fstat(io_infd, &stbuf) < 0) {
lprintf("Can't stat log file <%s>\n", logfile);
return;
}
for (n = stbuf.st_size / sizeof(struct log_fmt); n > 0; --n) {
lrfill((char *) &logg, sizeof(struct log_fmt));
t = logg.diedtime;
if ((p = ctime(&t)) == NULL)
p = q;
else {
p[16] = '\n';
p[17] = 0;
}
lprintf("Score: %ld, Diff: %ld, %s %s on %ld at %s", (long) (logg.score), (long) (logg.diff), logg.who, logg.what, (long) (logg.cavelev), p + 4);
#ifdef EXTRA
if (logg.moves <= 0)
logg.moves = 1;
lprintf(" Experience Level: %ld, AC: %ld, HP: %ld/%ld, Elapsed Time: %ld minutes\n", (long) (logg.lev), (long) (logg.ac), (long) (logg.hp), (long) (logg.hpmax), (long) (logg.elapsedtime));
lprintf(" CPU time used: %ld seconds, Machine usage: %ld.%02ld%%\n", (long) (logg.cputime), (long) (logg.usage / 100), (long) (logg.usage % 100));
lprintf(" BYTES in: %ld, out: %ld, moves: %ld, deaths: %ld, spells cast: %ld\n", (long) (logg.bytin), (long) (logg.bytout), (long) (logg.moves), (long) (logg.killed), (long) (logg.spused));
lprintf(" out bytes per move: %ld, time per move: %ld ms\n", (long) (logg.bytout / logg.moves), (long) ((logg.cputime * 1000) / logg.moves));
#endif
}
lflush();
lrclose();
return;
}
#ifndef UIDSCORE
/*
* getplid(name) Function to get players id # from id file
*
* Enter with the name of the players character in name.
* Returns the id # of the players character, or -1 if failure.
* This routine will try to find the name in the id file, if its not there,
* it will try to make a new entry in the file. Only returns -1 if can't
* find him in the file, and can't make a new entry in the file.
* Format of playerids file:
* Id # in ascii \n character name \n
*/
static int havepid = -1; /* playerid # if previously done */
int
getplid(nam)
char *nam;
{
int fd7, high = 999, no;
char *p, *p2;
char name[80];
if (havepid != -1)
return (havepid); /* already did it */
lflush(); /* flush any pending I/O */
snprintf(name, sizeof(name), "%s\n", nam);/* append a \n to name */
if (lopen(playerids) < 0) { /* no file, make it */
if ((fd7 = creat(playerids, 0664)) < 0)
return (-1); /* can't make it */
close(fd7);
goto addone; /* now append new playerid record to file */
}
for (;;) { /* now search for the name in the player id
* file */
p = lgetl();
if (p == NULL)
break; /* EOF? */
no = atoi(p); /* the id # */
p2 = lgetl();
if (p2 == NULL)
break; /* EOF? */
if (no > high)
high = no; /* accumulate highest id # */
if (strcmp(p2, name) == 0) { /* we found him */
return (no); /* his id number */
}
}
lrclose();
/* if we get here, we didn't find him in the file -- put him there */
addone:
if (lappend(playerids) < 0)
return (-1); /* can't open file for append */
lprintf("%ld\n%s", (long) ++high, name); /* new id # and name */
lwclose();
lcreat((char *) 0); /* re-open terminal channel */
return (high);
}
#endif /* UIDSCORE */