#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: scores.c,v 1.22 2021/05/02 12:50:45 rillig Exp $");
#endif
#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 {
long score;
long suid;
short what;
short level;
short hardlev;
short order;
char who[40];
char sciv[26][2];
};
struct wscofmt {
long score;
long timeused;
long taxes;
long suid;
short hardlev;
short order;
char who[40];
};
struct log_fmt {
long score;
int32_t diedtime;
short cavelev;
short diff;
#ifdef EXTRA
long elapsedtime;
long bytout;
long bytin;
long moves;
short ac;
short hp, hpmax;
short cputime;
short killed, spused;
short usage;
short lev;
#endif
char who[12];
char what[46];
};
static struct scofmt sco[SCORESIZE];
static struct wscofmt winr[SCORESIZE];
static struct log_fmt logg;
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",
"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);
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);
}
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);
}
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);
}
int
hashewon(void)
{
int i;
c[HARDGAME] = 0;
if (readboard() < 0)
return (0);
for (i = 0; i < SCORESIZE; i++)
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(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)
if (winr[i].score > 0) {
amt = winr[i].taxes;
if (x < amt)
amt = x;
winr[i].taxes -= amt;
outstanding_taxes -= amt;
set_score_output();
if (writeboard() < 0)
return (0);
return (amt);
}
return (0L);
}
static int
winshou(void)
{
struct wscofmt *p;
int i, j, count;
for (count = j = i = 0; i < SCORESIZE; i++)
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++)
for (j = 0; j < SCORESIZE; j++) {
p = &winr[j];
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);
}
static int
shou(int x)
{
int i, j, n, k;
int count;
for (count = j = i = 0; i < SCORESIZE; i++)
if (sco[i].score != 0) {
j++;
break;
}
if (j) {
lprcat("\n Score Difficulty Larn Visitor Log\n");
for (i = 0; i < SCORESIZE; i++)
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);
}
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();
}
void
showallscores(void)
{
int i, j;
lflush();
lcreat((char *) 0);
if (readboard() < 0)
return;
c[WEAR] = c[WIELD] = c[SHIELD] = -1;
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();
}
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);
}
static void
newscore(long score, char *whoo, int whyded, int winner)
{
int i;
long taxes;
if (readboard() < 0)
return;
if (cheat)
winner = 0;
if (winner) {
for (i = 0; i < SCORESIZE; i++)
if (sco[i].suid == userid)
sco[i].score = 0;
taxes = score * TAXRATE;
score += 100000 * c[HARDGAME];
for (i = 0; i < SCORESIZE; i++)
if (winr[i].suid == userid) {
new1sub(score, i, whoo, taxes);
return;
}
for (i = 0; i < SCORESIZE; i++)
if (winr[i].order == SCORESIZE - 1) {
new1sub(score, i, whoo, taxes);
return;
}
} else if (!cheat) {
for (i = 0; i < SCORESIZE; i++)
if (sco[i].suid == userid) {
new2sub(score, i, whoo, whyded);
return;
}
for (i = 0; i < SCORESIZE; i++)
if (sco[i].order == SCORESIZE - 1) {
new2sub(score, i, whoo, whyded);
return;
}
}
}
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;
}
}
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];
}
}
}
static int scorerror;
void
died(int x)
{
int f, win;
char ch;
const char *mod;
time_t zzz;
if (c[LIFEPROT] > 0) {
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;
};
--c[LIFEPROT];
c[HP] = 1;
--c[CONSTITUTION];
cursors();
lprcat("\nYou feel wiiieeeeerrrrrd all over! ");
beep();
lflush();
sleep(4);
return;
}
invalid:
clearvt100();
lflush();
f = 0;
if (ckpflag)
unlink(ckpfile);
if (x < 0) {
f++;
x = -x;
}
if ((x == 300) || (x == 257))
exit(0);
if (x == 263)
win = 1;
else
win = 0;
c[GOLD] += c[BANKACCOUNT];
c[BANKACCOUNT] = 0;
newscore(c[GOLD], logname, x, win);
diedsub(x);
lflush();
set_score_output();
if ((wizard == 0) && (c[GOLD] > 0)) {
#ifndef NOLOG
if (gid != egid)
setegid(egid);
if (lappend(logfile) < 0) {
if (lcreat(logfile) < 0) {
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);
logg.diedtime = zzz;
#ifdef EXTRA
times(&cputime);
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
if (x != 257) {
if (sortboard()) {
set_score_output();
scorerror = writeboard();
}
}
}
if ((x == 256) || (x == 257) || (f != 0))
exit(0);
if (scorerror == 0)
showscores();
if (x == 263)
mailbill();
exit(0);
}
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');
}
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
static int havepid = -1;
int
getplid(nam)
char *nam;
{
int fd7, high = 999, no;
char *p, *p2;
char name[80];
if (havepid != -1)
return (havepid);
lflush();
snprintf(name, sizeof(name), "%s\n", nam);
if (lopen(playerids) < 0) {
if ((fd7 = creat(playerids, 0664)) < 0)
return (-1);
close(fd7);
goto addone;
}
for (;;) {
p = lgetl();
if (p == NULL)
break;
no = atoi(p);
p2 = lgetl();
if (p2 == NULL)
break;
if (no > high)
high = no;
if (strcmp(p2, name) == 0) {
return (no);
}
}
lrclose();
addone:
if (lappend(playerids) < 0)
return (-1);
lprintf("%ld\n%s", (long) ++high, name);
lwclose();
lcreat((char *) 0);
return (high);
}
#endif