/* * static char *rcsid_hiscore_c = * "$Id: hiscore.C,v 1.4 2006/09/03 00:18:42 root Exp $"; */ /* CrossFire, A Multiplayer game for X-windows Copyright (C) 2002 Mark Wedel & Crossfire Development Team Copyright (C) 1992 Frank Tore Johansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The authors can be reached via e-mail at crossfire-devel@real-time.com */ #include #ifndef __CEXTRACT__ #include #endif /* * The score structure is used when treating new high-scores */ typedef struct scr { char name[BIG_NAME]; /* name */ char title[BIG_NAME]; /* Title */ char killer[BIG_NAME]; /* name (+ title) or "quit" */ sint64 exp; /* Experience */ char maplevel[BIG_NAME]; /* Killed on what level */ int maxhp,maxsp,maxgrace; /* Max hp, sp, grace when killed */ int position; /* Position in the highscore list */ } score; /* * spool works mostly like strtok(char *, ":"), but it can also * log a specified error message if something goes wrong. */ char *spool(char *bp,char *error) { static char *prev_pos = NULL; char *next_pos; if (bp == NULL) { if (prev_pos == NULL) { LOG(llevError, "Called spool (%s) with NULL without previous call.\n", error); return NULL; } bp = prev_pos; } if (*bp == '\0') { LOG(llevError, "spool: End of line at %s\n", error); return NULL; } if ((next_pos = strchr(bp, ':')) != NULL) { *next_pos = '\0'; prev_pos = next_pos + 1; } else prev_pos = NULL; return bp; } /* * Does what it says, copies the contents of the first score structure * to the second one. */ static void copy_score(score *sc1,score *sc2) { strncpy(sc2->name, sc1->name, BIG_NAME); sc2->name[BIG_NAME - 1] = '\0'; strncpy(sc2->title, sc1->title, BIG_NAME); sc2->title[BIG_NAME - 1] = '\0'; strncpy(sc2->killer, sc1->killer, BIG_NAME); sc2->killer[BIG_NAME - 1] = '\0'; sc2->exp = sc1->exp; strcpy(sc2->maplevel,sc1->maplevel); sc2->maxhp = sc1->maxhp; sc2->maxsp = sc1->maxsp; sc2->maxgrace = sc1->maxgrace; } /* * Writes the given score structure to a static buffer, and returns * a pointer to it. */ static char *put_score(score *sc) { static char buf[MAX_BUF]; sprintf(buf,"%s:%s:%lld:%s:%s:%d:%d:%d",sc->name,sc->title, (long long)sc->exp,sc->killer,sc->maplevel, sc->maxhp,sc->maxsp,sc->maxgrace); return buf; } /* * The oposite of put_score, get_score reads from the given buffer into * a static score structure, and returns a pointer to it. */ static score *get_score(char *bp) { static score sc; char *cp; if((cp=strchr(bp,'\n'))!=NULL) *cp='\0'; if ((cp = spool(bp, "name")) == NULL) return NULL; strncpy(sc.name,cp,BIG_NAME); sc.name[BIG_NAME - 1] = '\0'; if ((cp = spool(NULL, "title")) == NULL) return NULL; strncpy(sc.title,cp,BIG_NAME); sc.title[BIG_NAME - 1] = '\0'; if ((cp = spool(NULL, "score")) == NULL) return NULL; long long exp; sscanf(cp,"%lld", &exp); sc.exp = exp; if ((cp = spool(NULL, "killer")) == NULL) return NULL; strncpy(sc.killer, cp, BIG_NAME); sc.killer[BIG_NAME - 1] = '\0'; if ((cp = spool(NULL, "map")) == NULL) return NULL; strncpy(sc.maplevel, cp, BIG_NAME); sc.maplevel[BIG_NAME - 1] = '\0'; if ((cp = spool(NULL, "maxhp")) == NULL) return NULL; sscanf(cp, "%d", &sc.maxhp); if ((cp = spool(NULL, "maxsp")) == NULL) return NULL; sscanf(cp, "%d", &sc.maxsp); if ((cp = spool(NULL, "maxgrace")) == NULL) return NULL; sscanf(cp, "%d", &sc.maxgrace); return ≻ } static char * draw_one_high_score(score *sc) { static char retbuf[MAX_BUF]; if(!strncmp(sc->killer,"quit",MAX_NAME)) sprintf(retbuf,"%3d %10lld %s the %s quit the game on map %s [%d][%d][%d].", sc->position, (long long)sc->exp,sc->name,sc->title,sc->maplevel,sc->maxhp,sc->maxsp, sc->maxgrace); else if(!strncmp(sc->killer,"left",MAX_NAME)) sprintf(retbuf,"%3d %10lld %s the %s left the game on map %s [%d][%d][%d].", sc->position, (long long)sc->exp,sc->name,sc->title,sc->maplevel,sc->maxhp,sc->maxsp, sc->maxgrace); else sprintf(retbuf,"%3d %10lld %s the %s was killed by %s on map %s [%d][%d][%d].", sc->position, (long long)sc->exp,sc->name,sc->title,sc->killer,sc->maplevel, sc->maxhp,sc->maxsp,sc->maxgrace); return retbuf; } /* * add_score() adds the given score-structure to the high-score list, but * only if it was good enough to deserve a place. */ static score *add_score(score *new_score) { FILE *fp; static score old_score; score *tmp_score,pscore[HIGHSCORE_LENGTH]; char buf[MAX_BUF], filename[MAX_BUF], *bp; int nrofscores=0,flag=0,i,comp; new_score->position=HIGHSCORE_LENGTH+1; old_score.position= -1; sprintf(filename,"%s/%s",settings.localdir,HIGHSCORE); if((fp=open_and_uncompress(filename,1,&comp))!=NULL) { while(fgets(buf,MAX_BUF,fp)!=NULL&&nrofscoresexp>=tmp_score->exp) { copy_score(new_score,&pscore[nrofscores]); new_score->position=nrofscores; flag=1; if(++nrofscores>=HIGHSCORE_LENGTH) break; } if(!strcmp(new_score->name,tmp_score->name)) { /* Another entry */ copy_score(tmp_score,&old_score); old_score.position=nrofscores; if(flag) continue; } copy_score(tmp_score,&pscore[nrofscores++]); } close_and_delete(fp, comp); } if(old_score.position!=-1&&old_score.exp>=new_score->exp) return &old_score; /* Did not beat old score */ if(!flag&&nrofscoresposition= -1; if(old_score.position!=-1) return &old_score; if(nrofscores) { copy_score(&pscore[nrofscores-1],&old_score); return &old_score; } LOG(llevError,"Highscore error.\n"); return NULL; } void check_score(object *op) { score new_score; score *old_score; if(op->stats.exp==0) return; if(!op->contr->name_changed) { if(op->stats.exp>0) { new_draw_info(NDI_UNIQUE, 0,op,"As you haven't changed your name, you won't"); new_draw_info(NDI_UNIQUE, 0,op,"get into the high-score list."); } return; } if(QUERY_FLAG(op,FLAG_WAS_WIZ)) { new_draw_info(NDI_UNIQUE, 0,op,"Since you have been in wizard mode,"); new_draw_info(NDI_UNIQUE, 0,op,"you can't enter the high-score list."); return; } if (op->contr->explore) { new_draw_info(NDI_UNIQUE, 0,op,"Since you were in explore mode,"); new_draw_info(NDI_UNIQUE, 0,op,"you can't enter the high-score list."); return; } strncpy(new_score.name,op->name,BIG_NAME); new_score.name[BIG_NAME-1] = '\0'; strncpy(new_score.title,op->contr->own_title,BIG_NAME); if(new_score.title[0]=='\0') strncpy(new_score.title,op->contr->title,BIG_NAME); new_score.title[BIG_NAME-1] = '\0'; strncpy(new_score.killer,op->contr->killer,BIG_NAME); if(new_score.killer[0]=='\0') strcpy(new_score.killer,"a dungeon collapse"); new_score.killer[BIG_NAME-1] = '\0'; new_score.exp=op->stats.exp; if(op->map == NULL) *new_score.maplevel = '\0'; else { strncpy(new_score.maplevel, op->map->name?op->map->name:op->map->path, BIG_NAME-1); new_score.maplevel[BIG_NAME-1] = '\0'; } new_score.maxhp=(int) op->stats.maxhp; new_score.maxsp=(int) op->stats.maxsp; new_score.maxgrace=(int) op->stats.maxgrace; if((old_score=add_score(&new_score))==NULL) { new_draw_info(NDI_UNIQUE, 0,op,"Error in the highscore list."); return; } if(new_score.position == -1) { new_score.position = HIGHSCORE_LENGTH+1; /* Not strictly correct... */ if(!strcmp(old_score->name,new_score.name)) new_draw_info(NDI_UNIQUE, 0,op,"You didn't beat your last highscore:"); else new_draw_info(NDI_UNIQUE, 0,op,"You didn't enter the highscore list:"); new_draw_info(NDI_UNIQUE, 0,op, draw_one_high_score(old_score)); new_draw_info(NDI_UNIQUE, 0,op, draw_one_high_score(&new_score)); return; } if(old_score->exp>=new_score.exp) new_draw_info(NDI_UNIQUE, 0,op,"You didn't beat your last score:"); else new_draw_info(NDI_UNIQUE, 0,op,"You beat your last score:"); new_draw_info(NDI_UNIQUE, 0,op, draw_one_high_score(old_score)); new_draw_info(NDI_UNIQUE, 0,op, draw_one_high_score(&new_score)); } /* displays the high score file. object is the calling object * (null if being called via command line.) max is the maximum * number of scores to display. match, if set, is the name or class * to match to. */ void display_high_score(object *op,int max, const char *match) { const size_t maxchar = 80; FILE *fp; char buf[MAX_BUF],*scorebuf, *bp, *cp; int i=0,j=0,comp; score *sc; sprintf(buf,"%s/%s",settings.localdir,HIGHSCORE); if((fp=open_and_uncompress(buf,0,&comp))==NULL) { LOG(llevError, "Cannot open highscore file %s: %s\n", buf, strerror(errno)); if(op!=NULL) new_draw_info(NDI_UNIQUE, 0,op,"There is no highscore file."); return; } if(op != NULL) clear_win_info(op); new_draw_info(NDI_UNIQUE, 0,op,"Nr Score Who [max hp][max sp][max grace]"); while(fgets(buf,MAX_BUF,fp)!=NULL) { if(j>=HIGHSCORE_LENGTH||i>=(max-1)) break; if((sc=get_score(buf))==NULL) break; sc->position=++j; if (match==NULL) { scorebuf=draw_one_high_score(sc); i++; } else { if (!strcasecmp(sc->name, match) || !strcasecmp(sc->title, match)) { scorebuf=draw_one_high_score(sc); i++; } else continue; } /* Replaced what seemed to an overly complicated word wrap method * still word wraps, but assumes at most 2 lines of data. * mw - 2-12-97 */ strncpy(buf,scorebuf,MAX_BUF); buf[MAX_BUF-1] = '\0'; cp=buf; while (strlen(cp)> maxchar) { bp = cp+maxchar-1; while (*bp != ' ' && bp>cp) bp--; *bp='\0'; if (op == NULL) { LOG(llevDebug, "%s\n", cp); } else { new_draw_info(NDI_UNIQUE, 0,op,cp); } sprintf(buf, " %s", bp+1); cp = buf; i++; } if(op == NULL) LOG(llevDebug, "%s\n", buf); else new_draw_info(NDI_UNIQUE, 0,op,buf); } close_and_delete(fp, comp); }