#include #include #include #include #include #include #include #include #include "ai.h" #include "attack.h" #include "data.h" #include "io.h" #include "flag.h" #include "god.h" #include "lf.h" #include "map.h" #include "move.h" #include "nexus.h" #include "objects.h" #include "save.h" #include "spell.h" #include "text.h" #include "vault.h" material_t *material = NULL,*lastmaterial = NULL; objectclass_t *objectclass = NULL,*lastobjectclass = NULL; objecttype_t *objecttype = NULL,*lastobjecttype = NULL; brand_t *firstbrand = NULL,*lastbrand = NULL; obmod_t *firstobmod = NULL,*lastobmod = NULL; celltype_t *firstcelltype = NULL,*lastcelltype = NULL; behaviour_t *firstbehaviour = NULL,*lastbehaviour = NULL; command_t *firstcommand = NULL,*lastcommand = NULL; race_t *firstrace = NULL,*lastrace = NULL; raceclass_t *firstraceclass = NULL,*lastraceclass = NULL; recipe_t *firstrecipe = NULL,*lastrecipe = NULL; job_t *firstjob = NULL,*lastjob = NULL; poisontype_t *firstpoisontype = NULL,*lastpoisontype = NULL; skill_t *firstskill = NULL,*lastskill = NULL; habitat_t *firsthabitat = NULL,*lasthabitat = NULL; map_t *firstmap = NULL,*lastmap = NULL; map_t *heaven = NULL; region_t *firstregion = NULL,*lastregion = NULL; regionoutline_t *firstregionoutline = NULL,*lastregionoutline = NULL; branch_t *firstbranch = NULL,*lastbranch = NULL; knowledge_t *knowledge = NULL, *lastknowledge = NULL; hiddenname_t *firsthiddenname = NULL, *lasthiddenname = NULL; npcname_t *npcname; int numnpcnames; int totalraces = 0; condset_t ccwalkable; condset_t ccwalkableroom; condset_t ccroom; extern lifeform_t *godlf[]; extern int ngodlfs; extern int gfxready; extern WINDOW *mainwin; int nextregionthingid = 0; int playerorigalignment = AL_NONE; buildingusage_t buildingusage[MAXBUILDINGTYPES]; int nbuildingusage = 0; warning_t *firstwarning = NULL,*lastwarning = NULL; option_t *firstoption = NULL,*lastoption = NULL; plural_t *firstplural = NULL,*lastplural = NULL; int maxmonhitdice = 0; // highest number of hitdice for any monster int playerhasmoved = B_FALSE; double presin[360]; double precos[360]; extern vault_t *firstvault; extern flag_t *retflag[]; extern int nretflags; glyph_t playerglyph,tempglyph; double startticks,lastticks; struct timeval starttv, tv,newtv; extern int enteringmap; // maintains unique lifeform ID numbers long nextlfid = 0; int SCREENW = DEF_SCREENW; int SCREENH = DEF_SCREENH; // object return list object_t *retobs[MAXPILEOBS+1]; int retobscount[MAXPILEOBS+1]; int nretobs = 0; FILE *logfile; prompt_t prompt; char msghist[MAXHISTORY][BUFLEN]; int nmsghist = 0; enum ERROR reason; // global for returning errors void *rdata; // globel for returning data lifeform_t *player = NULL; int gameover; int obdb = B_FALSE; enum GAMEMODE gamemode = GM_FIRST; enum WINGAMETYPE wintype = WT_NONE; long curtime = 0; // # current game clock (in seconds) long gamedays = 0; // # game days passed since start of game long gamesecs = 0; // # game seconds passed since start of game long timeleft = 0; // # secs remaining for which we need to process events int prevhour = -1; // previous Hour extern int statdirty; int needredraw = B_TRUE; int numdraws = 0; int flagcheck = B_FALSE; // for xp list debugging extern race_t **raceposs; extern int *xpposs; // random seed long seed = 0; int main(int argc, char **argv) { object_t *o; char welcomemsg[BUFLEN]; int ch; FILE *playerfile = NULL; int foundsavegame = B_FALSE; int x,y,i; cell_t *c,*targc; vault_t *v; enum SKILLLEVEL slev; flag_t *retflag[MAXCANDIDATES]; int nretflags; map_t fakemap; cell_t fakecell; atexit(cleanup); while ((ch = getopt(argc, argv, "f:hr:sS:")) != -1) { switch (ch) { case 'f': playerfile = fopen(optarg, "rt"); if (!playerfile) { fprintf(stderr, "cannot open player file: %s\n",optarg); exit(1); } break; case 'r': seed = atol(optarg); break; case 's': showhiscores(NULL, 1, 10); exit(0); break; case 'S': i = atoi(optarg); limit(&i, 1, 100); showhiscores(NULL, 1, i); exit(0); break; case 'h': case '?': default: usage(argv[0]); exit(0); } } // init params if (init()) { exit(1); } // load savegame, if available foundsavegame = loadall(); if (foundsavegame) { region_t *r; map_t *m; lifeform_t *l; // fill in gods r = findregionbytype(BH_HEAVEN); m = findregionmap(r->id, 1); ngodlfs = 0; for (l = m->lf ; l ; l = l->next) { if (getraceclass(l) == RC_GOD) { godlf[ngodlfs++] = l; } } } // init graphics initgfx(); // warn about vault problems. for (v = firstvault ; v ; v = v->next) { if (!v->valid) { msg("warning: invalid vaultfile '%s'",v->filename); more(); } } // init prompt for (i = 0; i < MAXCHOICES; i++) { choice_t *ch; ch = &prompt.choice[i]; ch->text = NULL; ch->desc = NULL; ch->longdesc = NULL; } // if we didn't load a game, add the player if (!foundsavegame) { char *user,pname[BUFLEN],buf[BUFLEN]; job_t *j = NULL; race_t *startrace = NULL; char ch; //object_t *o; cell_t *where; int dir; flag_t *f; region_t *wregion, *dregion,*hregion; map_t *dmap,*surfmap; // create the dungeon layout initmaplayout(); // populate scroll, potion, etc names genhiddennames(); // read from rc file if required if (playerfile) { char *p; while (!feof(playerfile)) { fgets(buf, BUFLEN, playerfile); buf[strlen(buf)-1] = '\0'; if (strstr(buf, "job:") == buf) { p = buf + strlen("job:"); j = findjobbyname(p); if (j) break; } } fseek(playerfile, 0, SEEK_SET); } if (!startrace) { race_t *r; gamemode = GM_CHARGEN; // ask for race initprompt(&prompt, "Select your race:"); ch = 'a'; for (r = firstrace ; r ; r = r->next) { if (hasflag(r->flags, F_PLAYABLE)) { char *longdesc; longdesc = malloc(HUGEBUFLEN * sizeof(char)); makedesc_race(r->id, longdesc, B_TRUE, B_TRUE ); addchoice(&prompt, ch++, r->name, NULL, r, longdesc); free(longdesc); } } startrace = NULL; while (!startrace) { getchoicestr(&prompt, B_FALSE, B_TRUE); startrace = prompt.result; } } if (!j) { enum JOBCATEGORY jcid; int recommended; // ask for job initprompt(&prompt, "Select your base job:"); ch = 'a'; // sort by recommendaion, then by category for (recommended = R_VRECOMMENDED ; recommended >= R_NOTRECOMMENDED; recommended--) { switch (recommended) { case R_VRECOMMENDED: addheading(&prompt, "Highly Recommended"); break; case R_RECOMMENDED: addheading(&prompt, "Recommended"); break; case R_NOTRECOMMENDED: addheading(&prompt, "Not Recommended"); break; default: break; } for (jcid = JC_FIRST ; jcid <= JC_LAST; jcid++) { for (j = firstjob ; j ; j = j->next) { int rec,recok = B_FALSE; if (j->id == J_GOD) { rec = -50; } else { rec = getjobrecommendation(startrace, j); } if ((recommended == R_VRECOMMENDED) && (rec >= 3)) { recok = B_TRUE; } else if ((recommended == R_RECOMMENDED) && (rec >= 0) && (rec < 3)) { recok = B_TRUE; } else if ((recommended == R_NOTRECOMMENDED) && (rec < 0)) { recok = B_TRUE; } if ((j->category == jcid) && recok && jobpossible(startrace->flags, j->id)) { char *longdesc,buf[BUFLEN]; longdesc = malloc(HUGEBUFLEN * sizeof(char)); makedesc_job(j, longdesc); if (!hasflag(j->flags, F_NOPLAYER)) { // letter isn't used if (j->id == J_GOD) { snprintf(buf, BUFLEN, "%-20s(%s)", "Diety", "for debugging"); } else { snprintf(buf, BUFLEN, "%-20s(%s)", j->name, getjobcatname(j->category)); } addchoice(&prompt, '-', buf, NULL, j, longdesc); } free(longdesc); } } } // end for jobcat } j = NULL; while (!j) { getchoicestr(&prompt, B_FALSE, B_TRUE); j = prompt.result; } } msg("Digging the dungeon..."); // add player in fake cell createfakes(&fakemap, &fakecell); real_addlf(&fakecell, startrace->id, 1, C_PLAYER); // this will assign 'player' // give them basic abilities addtempflag(player->flags, F_CANWILL, OT_A_CHECKSTAIRS, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_TRAIN, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_TIPTOE, NA, NA, NULL, FROMGAMESTART); addtempflag(player->flags, F_CANWILL, OT_A_DEBUG, NA, NA, NULL, FROMGAMESTART); ///////// // make the initial level // create world map. wregion = findregionbytype(BH_WORLDMAP); assert(wregion); surfmap = addmap(); createmap(firstmap, 1, wregion, NULL, D_NONE, NULL); msg("."); // create realm of gods - must do this first, so that // gods get created because any temples. hregion = findregionbytype(BH_HEAVEN); assert(hregion); heaven = addmap(); createmap(heaven, 1, hregion, NULL, D_NONE, NULL); msg("^n."); // create main dungeon dregion = findregionbytype(BH_MAINDUNGEON); assert(dregion); dmap = addmap(); createmap(dmap, 1, dregion, firstmap, D_DOWN, NULL); msg("^n."); // add player in the starting position //where = real_getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL); where = findobinmap(dmap, OT_PLAYERSTART); if (!where) { msg("error: couldn't find ot_playerstart. picking random loc."); more(); condset_t cs; initcondv(&cs, CC_WALKABLE, B_TRUE, NA, CC_ISROOM, B_TRUE, NA, CC_NONE ); where = getcell_cond(dmap, &cs); } if (!where) { dblog("fatal error: couldn't find player start position!"); msg("fatal error: couldn't find player start position!"); more(); exit(1); } if (where->lf) killlf(where->lf); movelf(player, where, B_FALSE); // new remove fakes killfakes(&fakemap, &fakecell); // this is the hole which you fell down to get here. addobfast(where->obpile, OT_HOLEINROOF); // kill any objects which were already there, or which fell down the hole killallobsexcept(where->obpile, OT_HOLEINROOF, OT_NONE); /*o = hasob(where->obpile, OT_PLAYERSTART); killob(o);*/ // place the portal to realm of gods c = getrandomcell(surfmap); while (!cellwalkable(NULL, c, NULL)) { c = getrandomcell(surfmap); } o = addobfast(c->obpile, OT_PORTAL); targc = findobinmap(heaven, OT_PORTAL); // link surface portal addflag(o->flags, F_MAPLINK, heaven->id, targc->x, targc->y, NULL); // link heaven portal o = hasob(targc->obpile, OT_PORTAL); addflag(o->flags, F_MAPLINK, surfmap->id, c->x, c->y, NULL); // get player name from environment vars user = getenv("USER"); if (user) { char pname[MAXPNAMELEN]; snprintf(pname, MAXPNAMELEN, "%s", getenv("USER")); addflag(player->flags, F_NAME, NA, NA, NA, pname); } else { addflag(player->flags, F_NAME, NA, NA, NA, "Anonymous"); } // give the player their job givejob(player, j->id); ////////////////////// // read cheat info from player file if (playerfile) { if (parseplayerfile(playerfile, player)) { // error! exit(0); } fclose(playerfile); // TODO: note that we're cheaing } // player needs hunger addflag(player->flags, F_HUNGER, 0, NA, NA, NULL); // kill any other lifeforms around the caster, to make room for pets. for (dir = DC_N; dir <= DC_NW; dir++) { cell_t *c; c = getcellindir(player->cell, dir); if (c && c->lf) { killlf(c->lf); } } // pet / npc / follower getflags(player->flags, retflag, &nretflags, F_HASPET, F_NONE); for (i = 0; i < nretflags; i++) { cell_t *c; race_t *r; lifeform_t *pet; enum OBTYPE avoidob = OT_WATERDEEP; condset_t cs,cs2; initcondv(&cs, CC_WALKABLE, B_TRUE, NA, CC_HASOBTYPE, B_FALSE, avoidob, CC_NONE ); initcondv(&cs2, CC_HASOBTYPE, B_FALSE, avoidob, CC_NONE ); f = retflag[i]; r = findracebyname(f->text, NULL); assert(r); // create pet, in view of player if possible. c = real_getrandomadjcell(player->cell, &cs, B_ALLOWEXPAND, LOF_NEED, NULL, player); if (!c) { // if no valid spaces, try to make one. c = real_getrandomadjcell(player->cell, &cs2, B_NOEXPAND, LOF_DONTNEED, NULL, player); assert(c); setcelltype(c, getmapempty(player->cell->map)); } assert(c); pet = addlf(c, r->id, 1); // mark us as its master petify(pet, player); } // some abilities should always use certain shortcut numbers autoshortcut(player, OT_A_TRAIN, 9); autoshortcut(player, OT_A_TIPTOE, 0); autoshortcut(player, OT_A_COOK, 1); autoshortcut(player, OT_S_SIXTHSENSE, 6); // populate the remainder with our other abilities autoshortcut(player, OT_NONE, NA); getplayernamefull(pname); snprintf(welcomemsg, BUFLEN, "Greetings %s, welcome to nexus!", pname); // 00:00 - 23:59 curtime = rnd(0,DAYSECS-1); } else { char pname[BUFLEN]; getplayernamefull(pname); snprintf(welcomemsg, BUFLEN, "Welcome back, %s!", pname); } // start game - this will cause debug messages to now // go to the log file instead of stdout. timeleft = 0; // reset game timer enteringmap = B_FALSE; // no time passes for lfs on a different map to the player. // calculate initial light //calclight(player->cell->map); // pre-calc line-of-sight for player //precalclos(player); if (!foundsavegame) { player->facing = D_ALL; } setlosdirty(player); if (!foundsavegame) { // changes for anything within los/lof of player's starting pos: // - don't want any mosnters starting here // - don't want any impassable objects other than doors // - don't want any locked doors slev = getskill(player, SK_CARTOGRAPHY); for (y = 0; y < player->cell->map->h; y++) { for (x = 0; x < player->cell->map->w; x++) { c = getcellat(player->cell->map, x, y); if (c && (haslos(player, c) || haslof(player->cell, c, LOF_WALLSTOP, NULL))) { object_t *o,*nexto; if (c->lf && !isplayer(c->lf) && !ispetof(c->lf, player)) { killlf(c->lf); } for (o = c->obpile->first ; o ; o = nexto) { nexto = o->next; if (hasflag(o->flags, F_IMPASSABLE) && !hasflag(o->flags, F_DOOR)) { killob(o); continue; } else { killflagsofid(o->flags, F_LOCKED); } } } } } } needredraw = B_TRUE; statdirty = B_TRUE; // start game gamemode = GM_GAMESTARTED; // redo light and player los //calclight(player->cell->map); // make player face the direction which gives them the most visibility // as we check, set all cells around us to start off known. if (!foundsavegame) { int bestdir = D_NONE; int bestlos = -1; for (i = DC_N; i <= DC_NW; i++) { setfacing(player, i); precalclos(player); updateknowncells(); if (player->nlos > bestlos) { bestlos = player->nlos; bestdir = i; } } player->facing = bestdir; precalclos(player); } // show level drawscreen(); if (!hasjob(player, J_GOD) && (lfhasflag(player, F_CANCAST) || lfhasflag(player, F_CANWILL))) { int order[3] = { FROMRACE, FROMJOB, FROMSKILL }, norder = 3; int first = B_TRUE,i,nspells = 0, nabils = 0, n; char buf[BUFLEN]; cls(); wmove(mainwin, 0, 0); textwithcol(mainwin, welcomemsg); textwithcol(mainwin, "\n\n"); // show spells getflags(player->flags, retflag, &nspells, F_CANCAST, F_NONE); for (n = 0; n < norder; n++) { for (i = 0; i < nspells; i++) { objecttype_t *sp; if (retflag[i]->lifetime != order[n]) continue; sp = findot(retflag[i]->val[0]); if (sp) { if (first) { textwithcol(mainwin, "Your starting spells are:\n"); first = B_FALSE; } snprintf(buf, BUFLEN, " - %-20s (%s", sp->name, getschoolname(getspellschoolknown(player, sp->id)) ); if (order[n] == FROMRACE) { char rname[BUFLEN]; strcat(buf, ", ^g"); snprintf(rname, BUFLEN, "%s", player->race->name); capitalise(rname); strcat(buf, rname); strcat(buf, " perk^n"); } else if (order[n] == FROMJOB) { strcat(buf, ", ^G"); strcat(buf, getjobname(player)); strcat(buf, " perk^n"); } else if (order[n] == FROMSKILL) { skill_t *sk; enum SKILLLEVEL whichlev; sk = retflag[i]->skillfrom; assert(sk); whichlev = whichlevforabil(sk->id, sp->id); assert(whichlev != PR_INEPT); strcat(buf, ", ^w"); strcat(buf, getskilllevelname(whichlev)); strcat(buf, " "); strcat(buf, sk->name); strcat(buf, " perk^n"); } strcat(buf, ")\n"); textwithcol(mainwin, buf); } } } if (nspells) { textwithcol(mainwin, "\n"); } // show racial and job abilities first = B_TRUE; getflags(player->flags, retflag, &nabils, F_CANWILL, F_NONE); for (n = 0; n < norder; n++) { for (i = 0; i < nabils; i++) { objecttype_t *sp; // don't list abilities which everyone has. if (retflag[i]->lifetime == FROMGAMESTART) continue; if (retflag[i]->lifetime != order[n]) continue; sp = findot(retflag[i]->val[0]); if (sp) { if (first) { textwithcol(mainwin, "Your unique starting abilities are:\n"); first = B_FALSE; } snprintf(buf, BUFLEN, " - %-20s", sp->name); if (order[n] == FROMRACE) { char rname[BUFLEN]; strcat(buf, " (^g"); snprintf(rname, BUFLEN, "%s", player->race->name); capitalise(rname); strcat(buf, rname); strcat(buf, " perk^n)"); } else if (order[n] == FROMJOB) { strcat(buf, " (^G"); strcat(buf, getjobname(player)); strcat(buf, " perk^n)"); } else if (order[n] == FROMSKILL) { skill_t *sk; enum SKILLLEVEL whichlev; sk = retflag[i]->skillfrom; assert(sk); whichlev = whichlevforabil(sk->id, sp->id); assert(whichlev != PR_INEPT); strcat(buf, " (^w"); strcat(buf, getskilllevelname(whichlev)); strcat(buf, " "); strcat(buf, sk->name); strcat(buf, " perk^n)"); } strcat(buf, "\n"); textwithcol(mainwin, buf); } } } if (nabils) { textwithcol(mainwin, "\n"); } wprintw(mainwin, "\n[Press any key to begin]"); getch(); restoregamewindows(); } clearmsg(); msg("%s",welcomemsg); //more(); playerorigalignment = getalignment(player); // MAIN LOOP // basic flow is: // // donextturn() - process a turn for a lifeform // startlfturn() Rest effects, Damage from floor objects, etc // lifeform takes action // checkdeath() - check for object/player death. remove dead things. // timeeffectsworld() Fires burn out, ice melts, etc // Also keeps lf->timespent values under control. // // // redraw screen // // checkendgame() - has the game ended yet? gameover = B_FALSE; while (!gameover) { map_t *curmap; curmap = player->cell->map; // default to no redraw - donextturn() will change this if needed. needredraw = B_FALSE; numdraws = 0; // someone has a turn - this will then call taketime donextturn(curmap); // show level (if required) drawscreen(); //dblog("**** END of turn, numdraws = %d", numdraws); // check end of game checkendgame(); } // identify all objects for (o = player->pack->first ; o ; o = o->next) { identify(o); } // show possessions dofinaloblist(player->pack); // print tombstone tombstone(player); //WriteMemLeak(); return B_FALSE; } celltype_t *addcelltype(int id, char *name, int glyph, int colour, int altcol, int solid, int transparent, enum MATERIAL mat, int floorheight, int hp, int volumemod, int absorbent) { celltype_t *a; // add to the end of the list if (firstcelltype == NULL) { firstcelltype = malloc(sizeof(celltype_t)); a = firstcelltype; a->prev = NULL; } else { // go to end of list a = lastcelltype; a->next = malloc(sizeof(celltype_t)); a->next->prev = a; a = a->next; } lastcelltype = a; a->next = NULL; // set props a->id = id; a->name = strdup(name); a->glyph.ch = glyph; a->glyph.colour = colour; if (altcol == NA) { a->altcol = C_NONE; } else { a->altcol = altcol; } a->solid = solid; a->transparent = transparent; a->material = findmaterial(mat); a->floorheight = floorheight; a->hp = hp; a->volumemod = volumemod; a->absorbent = absorbent; a->flags = addflagpile(NULL, NULL); return a; } int addcond(condset_t *cs, enum CELLCONDITION cond, int val, int arg) { cs->cond[cs->nconds] = cond; cs->arg[cs->nconds] = arg; cs->val[cs->nconds] = val; (cs->nconds)++; return cs->nconds; } warning_t *addwarning(char *text, int lifetime) { warning_t *a; // add to the end of the list if (firstwarning == NULL) { firstwarning = malloc(sizeof(celltype_t)); a = firstwarning; a->prev = NULL; } else { // go to end of list a = lastwarning; a->next = malloc(sizeof(warning_t)); a->next->prev = a; a = a->next; } lastwarning = a; a->next = NULL; // set props a->text = strdup(text); a->lifetime = lifetime; return a; } void checkdeath(void) { lifeform_t *lf, *nextlf; int x,y; for (lf = player->cell->map->lf; lf ; lf = nextlf) { nextlf = lf->next; // check for object death removedeadobs(lf->pack); // check for death if (lf->hp <= 0) { if (hasactivespell(lf, OT_S_DELAYDEATH) && (lf->hp > -15)) { } else { // die! if (die(lf)) { // saved! if (isstuckinwall(lf)) { // move elsewhere cell_t *newcell; newcell = real_getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, lf); if (newcell) { movelf(lf, newcell, B_FALSE); } } } else { continue; } } } } // check for object death on map for (y = 0; y < player->cell->map->h; y++) { for (x = 0; x < player->cell->map->w; x++) { cell_t *c; c = getcellat(player->cell->map, x, y); if (c) { removedeadobs(c->obpile); } } } } void checkendgame(void) { flag_t *f; if (!player->alive) { gamemode = GM_GAMEOVER; gameover = B_TRUE; } else if ((f = lfhasflag(player, F_WINNER)) != NULL) { gamemode = GM_GAMEOVER; gameover = B_TRUE; wintype = f->val[0]; } } void cleanup(void) { int i; gamemode = GM_CLEANUP; free(xpposs); free(raceposs); if (logfile) fclose(logfile); // free commands & options while (firstcommand) killcommand(firstcommand); while (firstoption) killoption(firstoption); // free maps (this will kill its lifeforms & obs & cells too) while (firstmap) killmap(firstmap); // free branches while (firstbranch) killbranch(firstbranch); // free knowledge while (knowledge) killknowledge(knowledge); // free brands while (firstbrand) killbrand(firstbrand); // free obmods while (firstobmod) killobmod(firstobmod); // free obtypes while (objecttype) killot(objecttype); // free obclasses while (objectclass) killoc(objectclass); // free materials while (material) killmaterial(material); // free races while (firstrace) killrace(firstrace); // free posiontypes while (firstpoisontype) killpoisontype(firstpoisontype); // free celltypes while (firstcelltype) killcelltype(firstcelltype); // free npcname strings for (i = 0; i < numnpcnames; i++) { free(npcname[i].name); } // free npcname structures free(npcname); // free hidden names while (firsthiddenname) killhiddenname(firsthiddenname); // behaviours while (firstbehaviour) killbehaviour(firstbehaviour); //WriteMemLeak(); cleanupgfx(); } void dbtimestartlf(lifeform_t *lf) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%s (id %d), seenbyplayer = %s",lf->race->name, lf->id, cansee(player, lf) ? "YES" : "NO"); dbtimestart(buf); } void dbtimeendlf(lifeform_t *lf) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%s (id %d), numdraws=%d",lf->race->name, lf->id, numdraws); dbtimeend(buf); } void dbtimestart(char *format, ...) { char buf[BUFLEN]; va_list args; gettimeofday(&tv, NULL); starttv = tv; va_start(args, format); vsnprintf( buf, BUFLEN, format, args ); va_end(args); dblog("START\t%s", buf); } void dbtime(char *format, ...) { double ticks; char buf[BUFLEN]; va_list args; va_start(args, format); vsnprintf( buf, BUFLEN, format, args ); va_end(args); gettimeofday(&newtv, NULL); ticks = ((newtv.tv_sec - tv.tv_sec) * 1000000) + (newtv.tv_usec - tv.tv_usec); dblog("+%f\t%s", ticks, buf); tv = newtv; } void dbtimeend(char *format, ...) { double ticks; char buf[BUFLEN]; va_list args; va_start(args, format); vsnprintf( buf, BUFLEN, format, args ); va_end(args); gettimeofday(&newtv, NULL); ticks = ((newtv.tv_sec - starttv.tv_sec) * 1000000) + (newtv.tv_usec - starttv.tv_usec); dblog("FINISHED %s (total time %f)", buf, ticks); } void dobresnham(int d, int xinc1, int yinc1, int dinc1, int xinc2, int yinc2, int dinc2, int *xinc, int *yinc, int *dinc) { if (d < 0) { *xinc = xinc1; *yinc = yinc1; *dinc = dinc1; } else { *xinc = xinc2; *yinc = yinc2; *dinc = dinc2; } } void donextturn(map_t *map) { lifeform_t *who,*l; int db = B_FALSE; map_t *oldpmap; oldpmap = player->cell->map; who = map->lf; if (who) { if (db) dblog("**** donextturn for: id %d %s", who->id, who->race->name); //checkflagpile(who->flags); if (who->timespent > 0) { // shuffling lf times... shuffledown(map); } assert(who->timespent == 0); if (getoption(OPT_TIMEDEBUG)) { dbtimestartlf(who); } startlfturn(who); // calculate light //calclight(map); // pre-calculate line of sight for this lifeform //precalclos(who); // cope with eyesight adjusting to the dark //do_eyesight_adjust(who); // update gun targets autotarget(who); // keep looping until they actually do something or are dead! while (who->timespent == 0) { if (isdead(who)) { // skip turn taketime(who, SPEED_DEAD); } else { // do we need to run away from something? if (!flee(who)) { int donormalmove = B_TRUE; flag_t *f = NULL,*lyflag = NULL; // lycanthrope in human form at midnight? if (gettimephase() == TP_MIDNIGHT) { char changerace[BUFLEN]; int willchange = B_FALSE; int willrage = B_FALSE; //int mintime=20,maxtime=30; strcpy(changerace, ""); if (!ispolymorphed(who)) { lyflag = lfhasflag(who, F_LYCANTHROPE); if (lyflag && (lyflag->val[0] != 0)) { strcpy(changerace, lyflag->text); willchange = B_TRUE; } else { flag_t *incflag; incflag = lfhasflagval(who, F_INCUBATING, P_LYCANTHROPY, NA, NA, NULL); if (incflag) { char *p; char *localtext; char buf[BUFLEN]; // figure out race name from f_incubating flag. localtext = strdup(incflag->text); p = readuntil(buf, localtext, '^'); readuntil(buf, p, '^'); strcpy(changerace, buf); // turn f_incubating into f_poisoned completeincubation(who, incflag); willchange = B_TRUE; free(localtext); willrage = B_TRUE; } } } if (willchange) { race_t *r; if (isplayer(who)) { msg("You feel a change coming over your body!"); } r = findracebyname(changerace, NULL); //polymorphto(who, r->id, rnd(mintime,maxtime)); polymorphto(who, r->id, PERMENANT); donormalmove = B_FALSE; if (willrage) { enrage(who, PERMENANT); } } } else if (ispolymorphed(who)) { int autorevert = B_FALSE; // not midnight. polymorphed uncontrollably? if (lfhasflagval(who, F_POISONED, P_LYCANTHROPY, NA, NA, NULL) && lfhasflag(who, F_AICONTROLLED)) { autorevert = B_TRUE; } else if (isplayer(who)) { flag_t *lyflag; lyflag = lfhasflag(who, F_LYCANTHROPE); if (lyflag && (lyflag->val[0] > 0)) { autorevert = B_TRUE; } } if (autorevert) { abilityeffects(who, OT_A_POLYREVERT, who->cell, who, NULL); } } // charmed ? if (donormalmove) { f = lfhasflag(who, F_CHARMEDBY); if (f) { if (!charmedaction(who, f)) { donormalmove = B_FALSE; } } } // pathfinding? if (donormalmove && isplayer(who)) { if (lfhasflag(who, F_PATHFINDING)) { cell_t *c; // follow..... c = ai_getnextcellinpath(who); if (c) { if (c->map != who->cell->map) { msg("Destination is on different level. Aborting."); stoppathfinding(who); } else { int dir,ok; enum ERROR errcode; donormalmove = B_FALSE; dir = whichwayto(who->cell, c, who, B_TRUE); ok = moveclear(who, dir, &errcode); if (!ok && (errcode != E_DOORINWAY)) { // something other than a door in the way? msg("Stopped pathfinding."); stoppathfinding(who); } else { if (trymove(who, dir, B_TRUE, B_FALSE)) { msg("Can't move towards target cell."); stoppathfinding(who); } if (who->cell == c) { // success ai_popnextcellinpath(who); } } } } else { msg("Arrived at destination."); stoppathfinding(who); } } } // casting a spell? if (donormalmove) { f = lfhasflag(who, F_CASTINGSPELL); if (f) { donormalmove = B_FALSE; f->val[2]--; if (f->val[2] <= 0) { // spell triggers! char *p; char buf[BUFLEN]; int lfid,mapid,x,y,power; long obid; lifeform_t *targlf; object_t *targob; cell_t *targcell = NULL; map_t *targmap; enum OBTYPE sid; sid = f->val[0]; power = f->val[1]; // finished! p = f->text; p = readuntil(buf, p, ';'); lfid = atoi(buf); p = readuntil(buf, p, ';'); obid = atol(buf); p = readuntil(buf, p, ';'); mapid = atoi(buf); p = readuntil(buf, p, ';'); x = atoi(buf); p = readuntil(buf, p, ';'); y = atoi(buf); if (lfid >= 0) { targlf = findlf(NULL, lfid); } else { targlf = NULL; } if (obid >= 0) { targob = findobidinmap(who->cell->map, obid); } else { targob = NULL; } if (mapid >= 0) { targmap = findmap(mapid); targcell = getcellat(targmap, x, y); } taketime(who, getspellspeed(who)); dospelleffects(who, sid, power, targlf, targob, targcell, B_UNCURSED, NULL, B_FALSE, NULL); killflagsofid(who->flags, F_CASTINGSPELL); } else { if (isplayer(who)) { objecttype_t *sp; sp = findot(f->val[0]); msg("You continue casting %s.", sp->name); } else if (cansee(player, who)) { flag_t *f2; char lfname[BUFLEN]; // still going... getlfname(who, lfname); f2 = lfhasflagval(who,F_SPELLCASTCONTTEXT, f->val[0], NA, NA, NULL); if (!f2) { f2 = lfhasflagval(who,F_SPELLCASTCONTTEXT, OT_NONE, NA, NA, NULL); } if (f2 && strlen(f2->text)) { msg("%s %s.", lfname, f2->text); } else { msg("%s continues casting a spell.", lfname); } } } } } // digging? if (donormalmove) { f = lfhasflag(who, F_DIGGING); if (f) { if (isplayer(who) && checkforkey()) { msg("Stopped digging."); killflag(f); } else { if (!continuedigging(who)) donormalmove = B_FALSE; } } } // repairing? if (donormalmove) { f = lfhasflag(who, F_REPAIRING); if (f) { // this flag might appear more than once. that's okay, we'll handle a new // one each time. if (isplayer(who) && checkforkey()) { msg("Stopped repairing items."); killflag(f); } else { if (!continuerepairing(who, f)) donormalmove = B_FALSE; } } } // eating? if (donormalmove) { f = lfhasflag(who, F_EATING); if (f) { object_t *o; o = findobbyid(who->pack, atol(f->text)); if (!o) { o = findobidinmap(who->cell->map, atol(f->text)); } if (o && caneat(who, o) && (getoblocation(o) == who->cell)) { int cancelled = B_FALSE; // has it gone bad while we're eating it? if (isbadfood(o) && isplayer(who) && (getskill(who, SK_COOKING) >= PR_BEGINNER)) { char ques[BUFLEN],obname[BUFLEN]; char ch; getobname(o, obname, 1); snprintf(ques, BUFLEN, "%s has gone bad - stop eating?", obname); ch = askchar(ques, "yn", "y", B_TRUE, B_FALSE); if (ch == 'y') { killflag(f); cancelled = B_TRUE; } } if (!cancelled) { if (eat(who,o)) { // failed killflag(f); } else { donormalmove = B_FALSE; } } } else { killflag(f); } } } // resting? if (donormalmove) { f = isresting(who); if (!f) { f = lfhasflag(who, F_TRAINING); } if (f) { // check for interrupt of resting... if (isplayer(who) && checkforkey()) { msg("Stopped %s.",(f->id == F_TRAINING) ? "training" : "resting"); stopresting(who); } else { if (isplayer(who)) { if (++who->turnsskipped >= 10) { //msg("Time passes..."); msg("."); who->turnsskipped = 0; } } noise(who->cell, who, NC_OTHER, SV_SHOUT, "sounds of training.", NULL); rest(who, B_TRUE); donormalmove = B_FALSE; } } } if (donormalmove) { // paralyzed etc? if (isimmobile(who)) { if (isplayer(who)) { if (++who->turnsskipped >= 10) { //msg("Time passes..."); msg("."); who->turnsskipped = 0; } } rest(who, B_FALSE); donormalmove = B_FALSE; } } if (donormalmove) { if (isplayer(who)) { drawcursor(); // find out what player wants to do if (lfhasflag(who, F_AICONTROLLED)) { aiturn(who); } else { handleinput(); } if (who->bartimer) { who->bartimer--; if (who->bartimer == 0) { who->damlastturn = 0; who->mplastturn = 0; who->stamlastturn = 0; statdirty = B_TRUE; drawstatus(); } } } else { //char lfname[BUFLEN]; //char buf[BUFLEN]; // do ai move //real_getlfname(who, lfname, B_FALSE); //snprintf(buf, BUFLEN, "aimove %s",lfname); //dbtimestart(buf); aiturn(who); //dbtimeend(buf); } } if (!isplayer(who) && (who->timespent == 0) && !donormalmove) { // our auto action failed! taketime(who, getactspeed(who)); } } } // us or the player moved into a new map? stop turn. if ((who->cell->map != map) || (player->cell->map != map)) { break; } } if (hasflag(player->flags, F_ASLEEP)) { needredraw = B_FALSE; } if (!isdead(who)) endlfturn(who); if (getoption(OPT_TIMEDEBUG)) { dbtimeendlf(who); } //checkflagpile(who->flags); } // end 'if (who)' for (l = map->lf ; l ; l = l->next) { //checkflagpile(l->flags); } if (isplayer(who)) { // in order to force the player's turn to be first, // monsters will not take any actions until // playerhasmoved is set playerhasmoved = B_TRUE; } // check for death etc checkdeath(); ////////////////////////////////// // effects which happen every GAME TICK // ie. object hp drain etc ////////////////////////////////// // note: can't use 'who->' below since 'who' might have died // and been de-alloced during checkdeath() above if they // died. //timeeffectsworld(player->cell->map); timeeffectsworld(map, B_TRUE); // the previous call to timeeffectsworld might cause the player to // change levels (ie. falling down through one or more pits). // // if this happens, we need to call it again to make sure that ->timespent // values don't get out of whack. while (player->cell->map != oldpmap) { oldpmap = player->cell->map; timeeffectsworld(player->cell->map, B_FALSE); } } command_t *findcommand(enum COMMAND id) { command_t *c; for (c = firstcommand ; c ; c = c->next) { if (c->id == id) return c; } return NULL; } warning_t *findwarning(char *text) { warning_t *w; for (w = firstwarning ; w ; w = w->next) { if (streq(w->text, text)) return w; } return NULL; } void getmpdicestats(lifeform_t *lf, int *ndice,int *plus) { flag_t *f; if (ndice) *ndice = 0; if (plus) *plus = 0; f = hasflag(lf->flags, F_MPDICE); if (f) { if (ndice) *ndice = f->val[0]; if (f->val[1] != NA) { if (plus) *plus = f->val[1]; } } } void gethitdicestats(lifeform_t *lf, int *ndice,int *nsides,int *plus) { flag_t *f; if (ndice) *ndice = 0; if (nsides) *nsides = HITDIESIDES; if (plus) *plus = 0; f = hasflag(lf->flags, F_HITDICE); if (f) { if (ndice) *ndice = f->val[0]; if (f->val[1] > 0) { if (plus) *plus = f->val[1]; } if (f->val[2] > 0) { if (nsides) *nsides = f->val[2]; } } else { if (ndice) *ndice = 1; if (plus) *plus = 0; } } // TODO: "range" is currently unused. void gettrrange(int depth, int *min, int *max, int range, int oodok) { int upchance = 3; int downchance = 1; // initial threat rating is the same as dungeon depth. // ie. dungeon level 1 has TR 1 monsters // ie. dungeon level 6 has TR 6 monsters // // then add an ever-decreasing chance of this difficulty // incrementing for each level. *min = depth; *max = depth; // adjust max depth for out-of-depth monsters if (oodok && (onein(upchance))) { (*max)++; upchance++; // repeated chances of incing level while ((upchance < 10) && (*max < maxmonhitdice) && onein(upchance)) { (*max)++; upchance++; } } // adjust min depth for chance of easier monsters if (onein(downchance)) { (*min)--; downchance++; while ((downchance < 10) && (*min > 1) && onein(downchance)) { (*min)--; downchance += 2; } } //*min = mid - range - 10; //*min = mid - (range*2); //limit(min, 0, 8); //*max = mid + range; // these shouldn't really be needed now limit(min, 1, NA); limit(max, *min, maxmonhitdice); } int getoption(enum OPTION id) { option_t *opt; for (opt = firstoption ; opt ; opt = opt->next) { if (opt->id == id) { if (opt->enabled) return B_TRUE; } } return B_FALSE; } enum COLOUR getpctcol(float num, float max) { float pct; pct = (num / max) * 100; if (pct > 100) { return C_LIGHTBLUE; } else if (pct == 100) { return C_LIGHTGREEN; } else if (pct >= 75) { return C_GREEN; } else if (pct >= 50) { return C_DARKYELLOW; } else if (pct >= 25) { return C_YELLOW; } else { // ie. < 25% return C_RED; } return C_ORANGE; } char getpctletter(float num, float max) { float pct; pct = (num / max) * 100; if (pct >= 100) { return 'S'; } else if (pct >= 85) { return 'A'; } else if (pct >= 70) { return 'B'; } else if (pct >= 55) { return 'C'; } else if (pct >= 40) { return 'D'; } else { // ie. < 40% return 'E'; } return '?'; } int init(void) { int i; // random numbers if (seed) { srand(seed); } else { srand(time(NULL)); } //sranddev(); // preset conditions initcondv(&ccwalkable, CC_WALKABLE, B_TRUE, NA, CC_NONE); initcondv(&ccroom, CC_ISROOM, B_TRUE, NA, CC_NONE); initcondv(&ccwalkableroom, CC_WALKABLE, B_TRUE, NA, CC_ISROOM, B_TRUE, NA, CC_NONE); // precalc sin/cos for (i = 0; i < 360; i++) { double rads; rads = i * (M_PI / 180); presin[i] = sin(rads); precos[i] = cos(rads); } gamemode = GM_INIT; playerglyph.ch = '@'; playerglyph.colour = C_GREY; tempglyph.ch = '@'; tempglyph.colour = C_GREY; inithiscores(); // load npc names loadnpcnames(); inittext(); initoptions(); initcommands(); initobjects(); validatehiddennames(); initraceclasses(); initskills(); initjobs(); initrace(); initmap(); // open log file (want to do this before digging the first map) logfile = fopen("log.txt","wt"); fprintf(logfile, "\n\n\n====== NEW LOGFILE ====\n"); validatehiddennames(); gamemode = GM_VALIDATION; if (validateobs()) { return B_TRUE; } if (validateraces()) { return B_TRUE; } // load in vaults loadvaults(); // validate regions if (validateregions()) { printf("Errors found in map outlines - check log for details. Bailing out."); exit(1); } return B_FALSE; } void initcond(condset_t *cs) { cs->nconds = 0; } void initcondv(condset_t *cs, ...) { enum CELLCONDITION condition; va_list args; int value,arg; va_start(args, cs); cs->nconds = 0; condition = va_arg(args, enum CELLCONDITION); while (condition != CC_NONE) { value = va_arg(args, int); assert((value == B_TRUE) || (value == B_FALSE) || (value == B_ONEOF)); arg = va_arg(args, int); addcond(cs, condition, value, arg); condition = va_arg(args, enum CELLCONDITION); } va_end(args); } char incletter(char *ch) { int newchar; if (*ch == '\0') { newchar = 'a'; } else if (*ch == 'z') { newchar = 'A'; } else { newchar = (*ch) + 1; } *ch = newchar; return *ch; } void inctime(long nunits) { curtime += (nunits*(TIMECONST)); // don't let it get higher than 23:59 while (curtime >= DAYSECS) { curtime -= DAYSECS; } } // retcell[0] will be initial location void calcbresnham(map_t *m, int x1, int y1, int x2, int y2, cell_t **retcell, int *numpixels) { int xinc1,xinc2,yinc1,yinc2,dinc1,dinc2,d; int xinc,yinc,dinc; int i; int x,y; initbresnham( x1, y1, x2, y2, &xinc1, &yinc1, &dinc1, &xinc2, &yinc2, &dinc2, numpixels, &d); x = x1; y = y1; for (i = 0; i < *numpixels; i++) { retcell[i] = getcellat(m, x, y); if (!retcell[i]) { // we've gone off the map - stop here *numpixels = i; return; } dobresnham(d, xinc1, yinc1, dinc1, xinc2, yinc2, dinc2, &xinc, &yinc, &dinc); // move to next cell d += dinc; x += xinc; y += yinc; } } void initbresnham(int x1, int y1, int x2, int y2, int *xinc1, int *yinc1, int *dinc1, int *xinc2, int *yinc2, int *dinc2, int *numpixels, int *d) { int deltax,deltay; deltax = (x2 - x1); if (deltax < 0) deltax = -deltax; deltay = (y2 - y1); if (deltay < 0) deltay = -deltay; if (deltax >= deltay) { *numpixels = deltax + 1; *d = (deltay*2) - deltax; *dinc1 = deltay << 1; *dinc2 = (deltay-deltax) << 1; *xinc1 = 1; *xinc2 = 1; *yinc1 = 0; *yinc2 = 1; } else { *numpixels = deltay + 1; *d = (deltax*2) - deltay; *dinc1 = deltax << 1; *dinc2 = (deltax - deltay) << 1; *xinc1 = 0; *xinc2 = 1; *yinc1 = 1; *yinc2 = 1; } if (x1 > x2) { *xinc1 = - *xinc1; *xinc2 = - *xinc2; } if (y1 > y2) { *yinc1 = - *yinc1; *yinc2 = - *yinc2; } } int isplayerturn(void) { if (!player) return B_FALSE; if (isplayer(player->cell->map->lf)) { return B_TRUE; } return B_FALSE; } void killwarning(warning_t *w) { warning_t *nextone, *lastone; // free mem if (w->text) free(w->text); // remove from list nextone = w->next; if (nextone != NULL) { nextone->prev = w->prev; } else { /* last */ lastwarning = w->prev; } if (w->prev == NULL) { /* first */ nextone = w->next; free(firstwarning); firstwarning = nextone; } else { lastone = w->prev; free (lastone->next ); lastone->next = nextone; } } void killwarningtext(char *text) { warning_t *w; w = findwarning(text); if (w) killwarning(w); } int limit(int *what, int min, int max) { int limited = B_FALSE; if (min != NA) { if (*what < min) { *what = min; limited = B_TRUE; } } if (max != NA) { if (*what > max) { *what = max; limited = B_TRUE; } } return limited; } int limitf(float *what, float min, float max) { int limited = B_FALSE; if (min != NA) { if (*what < min) { *what = min; limited = B_TRUE; } } if (max != NA) { if (*what > max) { *what = max; limited = B_TRUE; } } return limited; } int limitd(double *what, double min, double max) { int limited = B_FALSE; if (min != NA) { if (*what < min) { *what = min; limited = B_TRUE; } } if (max != NA) { if (*what > max) { *what = max; limited = B_TRUE; } } return limited; } int loadnpcnames(void) { FILE *f; char buf[BUFLEN]; int i = 0; f = fopen("data/npcnames.txt", "rt"); if (!f) return B_TRUE; // count lines... fgets(buf, BUFLEN, f); // VALGRIND: memleak here numnpcnames = 0; while (!feof(f)) { if (strlen(buf)) { buf[strlen(buf)-1] = '\0'; // strip newline } if (strlen(buf)) { numnpcnames++; } fgets(buf, BUFLEN, f); } // alloc mem //npcname = malloc(numnpcnames * sizeof(npcname_t)); npcname = calloc(numnpcnames, sizeof(npcname_t)); // back to start fseek(f, 0, SEEK_SET); // now read in names fgets(buf, BUFLEN, f); while (!feof(f)) { if (strlen(buf)) { buf[strlen(buf)-1] = '\0'; // strip newline } if (strlen(buf)) { capitalise(buf); npcname[i].name = strdup(buf); npcname[i].valid = B_TRUE; i++; } fgets(buf, BUFLEN, f); } return B_FALSE; } void *mymalloc(size_t sz) { return malloc(sz); } int onein(int howmany) { if (howmany <= 0) return B_FALSE; if (rnd(1,howmany) == 1) return B_TRUE; return B_FALSE; } int parseplayerfile(FILE *f, lifeform_t *lf) { // add extra obs etc from f char *pp; char localbuf[BUFLEN]; char buf[BUFLEN]; int goterror = B_FALSE; fgets(buf, BUFLEN, f); while (!feof(f)) { if (buf[strlen(buf)-1] == '\n') { buf[strlen(buf)-1] = '\0'; } //dblog("got line: [%s]",buf); if (strstr(buf, "skill:") == buf) { skill_t *sk; enum SKILLLEVEL slev; strcpy(localbuf, buf + strlen("skill:")); pp = strtok(localbuf, " "); if (!pp) { dblog("ERROR in playerfile. unknown skill level in this line:\n%s\n",buf); goterror = B_TRUE; } slev = findskilllevbyname(pp); pp += (strlen(pp) + 1); if (!pp) { dblog("ERROR in playerfile. missing skill name in this line:\n%s\n",buf); goterror = B_TRUE; } sk = findskillbyname(pp); if (sk) { giveskilllev(lf, sk->id, slev); } else { dblog("ERROR in playerfile. unknown skill (%s) in this line:\n%s\n",pp, buf); goterror = B_TRUE; } } else if (strstr(buf, "ob:") == buf) { object_t *o; strcpy(localbuf, buf + strlen("ob:")); o = addob(lf->pack, localbuf); if (o) { identify(o); } else { dblog("ERROR in playerfile. unknown object in this line:\n%s\n",buf); goterror = B_TRUE; } } fgets(buf, BUFLEN, f); } return goterror; } int pctchance(int pct) { if (rnd(1,100) <= pct) { return B_TRUE; } return B_FALSE; } float pctof(float pct, float num) { return ((pct / 100.0) * num); } // get a random number between min and max int rnd(int min, int max) { int res; res = (rand() % (max - min + 1)) + min; return res; } int real_roll(char *string, int wantmax) { int ndice,nsides,bonus; int roll; texttodice(string, &ndice,&nsides,&bonus); if (wantmax) { roll = (ndice * nsides) + bonus; } else { roll = rolldie(ndice, nsides) + bonus; } return roll; } int roll(char *string) { return real_roll(string, B_FALSE); } // get a random number int rolldie(int ndice, int sides) { int i; int res = 0; if (sides <= 0) return 0; for (i = 0; i < ndice; i++) { res += rnd(1,sides); } return res; } int rollhitdice(lifeform_t *lf, int wantmax) { int ndice, nsides = HITDIESIDES, plus = 0; int myroll = 0; int maxroll; float pctofmax; int db = B_FALSE; int myfitness; gethitdicestats(lf, &ndice,&nsides,&plus); maxroll = (ndice * nsides) + plus; if (wantmax) { myroll = maxroll; } else { myroll = rolldie(ndice, nsides) + plus; } if (db) dblog("TOTAL: %d",myroll); // modify for fitness/con if (isplayer(lf)) { pctofmax = ((float)myroll / (float) maxroll) * 100.0; myfitness = getattr(lf, A_CON); if (pctofmax < myfitness) { myroll = pctof(myfitness, maxroll); if (db) dblog(" -> modified by fitness to: %d",myroll); } } limit(&myroll, 1, NA); return myroll; } int rollmpdice(lifeform_t *lf, int wantmax) { int ndice, plus,roll,i; float mod; flag_t *retflag[MAXCANDIDATES]; int nretflags; int max; if (lfhasflag(lf, F_NOSPELLS)) { return 0; } getmpdicestats(lf, &ndice,&plus); max = (ndice * 4) + plus; if (max <= 0) return 0; if (wantmax) { roll = max; } else { roll = rolldie(ndice, 4) + plus; } mod = 100 + getstatmod(lf, A_IQ); roll = pctof(mod, roll); // modify via racial flags getflags(lf->flags, retflag, &nretflags, F_MPMOD, F_NONE); for (i = 0; i < nretflags; i++) { roll += retflag[i]->val[0]; } limit(&roll, 0, NA); return roll; } /* void sortlf(map_t *map) { int donesomething; int db = B_FALSE; lifeform_t *l,*nextl; int iter = 0; // bubblesort donesomething = B_TRUE; dblog("doing sort..."); while (donesomething) { donesomething = B_FALSE; //dblog("ITER %d",iter++); if (db) { dblog("ITER %d",iter++); for (l = map->lf ; l ; l = l->next) { dblog("- %s (timespent= %d) (sorted=%d)", (l == player) ? "player" : l->race->name, l->timespent,l->sorted); } } for (l = map->lf ; l->next ; l = nextl) { nextl = l->next; //if (!l->sorted && (l->timespent >= l->next->timespent) ) { if (l->sorted) { //dblog("skipping id %d %s, already sorted ",l->id, l->race->name); continue; } if (l->timespent >= l->next->timespent) { lifeform_t *temp; //dblog("moving id %d %s upwards",l->id, l->race->name); // remember next element temp = l->next; // remove this element from list // don't bother checking if (l->next == NULL) as we know // this won't be true due to the for loop condition if (l->prev == NULL) { // first map->lf = l->next; l->next->prev = NULL; } else { // not first l->prev->next = l->next; l->next->prev = l->prev; } // TESTING: re-add at correct position. while (temp->next && (temp->next->timespent <= l->timespent)) { //dblog("moving past %d %s (timespend=%d)...",temp->next->id, temp->next->race->name, temp->next->timespent); temp = temp->next; } // re-add element afterwards l->next = temp->next; l->prev = temp; temp->next = l; if (l->next == NULL) { map->lastlf = l; } else { l->next->prev = l; } l->sorted = B_TRUE; donesomething = B_TRUE; break; } else { l->sorted = B_TRUE; } } } //dblog("finished sort."); // reset sorted var for next time for (l = map->lf ; l->next ; l = l->next) { l->sorted = B_FALSE; } // sanity check if (player->next) { assert(player->next->prev == player); } if (player->prev) { assert(player->prev->next == player); } if (db) { dblog("AFTER SORT:"); for (l = map->lf ; l ; l = l->next) { // if (haslos(player, l->cell)) { dblog("- %s (timespent= %d) (sorted=%d)", (l == player) ? "player" : l->race->name, l->timespent,l->sorted); // } } } } */ void setcurtime(int hours, int minutes) { int h = -1,m = -1,s = -1; splittime(&h, &m, &s); while ((h != hours) || (m != minutes)) { curtime += TIMECONST; // don't let it get higher than 23:59 while (curtime >= DAYSECS) { curtime -= DAYSECS; } splittime(&h, &m, &s); } msg("DEBUG: time fastforwarded to %d:%d",hours, minutes); } void timeeffectsworld(map_t *map, int updategametime) { flag_t *retflag[MAXCANDIDATES]; int nretflags; lifeform_t *l,*nextl; int db = B_FALSE; object_t *o,*nexto; int x,y; long firstlftime; enum SKILLLEVEL cartskill; warning_t *w,*nextw; //dblog("------ tick (time=%ld) ----", curtime); cartskill = getskill(player, SK_CARTOGRAPHY); // now go through the list and make the first element be 0 l = map->lf; if (l) { // if (db) dblog("first lf is: %s (id %ld)\n",l->race->name, l->id); firstlftime = l->timespent; assert(firstlftime >= 0); } else { dblog("no lifeforms on map!\n"); // no first lf! firstlftime = 0; } //if (db) dblog("timespent = %d\n", timespent); if (db) dblog("firstlftime = %d\n", firstlftime); /* for (l = map->lf ; l ; l = nextl) { nextl = l->next; //checkflagpile(l->flags); } */ if (firstlftime > 0) { if (db) dblog("making firstlf timespent = 0 (currently %d):", firstlftime); shuffledown(map); } else { if (db) dblog("firstlf timespent is not greater than 0. no shuffle."); } timeleft += firstlftime; // now do effects based on time... while (timeleft >= TICK_INTERVAL) { flag_t *f; int i; int th,tm,ts; splittime(&th, &tm, &ts); timeleft -= TICK_INTERVAL; dblog("------ tick (time=%ld %02d:%02d:%02d) ----", curtime,th,tm,ts); // global time-based effects on map or map objects for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { cell_t *c; c = getcellat(map, x, y); if (c) { object_t *pit; if (cartskill <= PR_INEPT) { // if you are inept at cartography, you will forget // cells outside your view which are far away. if (c->knowntime > 0) { c->knowntime--; if (c->knowntime <= 0) { c->known = B_FALSE; c->knowntime = 0; } } } // writing fading? if (c->writing && (c->writinglifetime > 0)) { c->writinglifetime--; if (c->writinglifetime <= 0) { free(c->writing); c->writing = NULL; if (haslos(player, c)) { if (c == player->cell) { msg("The magical inscription beneath you vanishes."); } else { msg("A magical inscription nearby vanishes."); } } } } pit = hasobwithflagval(c->obpile, F_PIT, D_DOWN, NA, NA, NULL); if (!pit) pit = hasob(c->obpile, OT_GRATINGFLOOR); if (pit) { obsfallthrough(c, pit); } // go through each object in the cell... for (o = c->obpile->first ; o ; o = nexto) { nexto = o->next; assert(nexto != o); //checkflagpile(o->flags); timeeffectsob(o); } // end foreach object here /* // expire light effects if (c->littimer > 0) { c->littimer--; if (c->littimer == 0) { c->lit = c->origlit; } } */ // water/fire spreads... if (doelementspread(c) && haslos(player, c)) { needredraw = B_TRUE; } } // end if c } } // end for (y... // now finish off water spread redrawpause(); getflags(map->flags, retflag, &nretflags, F_NEWWATERDEPTH, F_NONE); assert(nretflags < MAXCANDIDATES); // oooooooooooooo for (i = 0; i < nretflags; i++) { cell_t *c; f = retflag[i]; c = getcellat(map, f->val[0], f->val[1]); if (c) { if (f->val[2] > DP_NONE) { o = hasobwithflag(c->obpile, F_DEEPWATER); if (!o) { o = addobfast(c->obpile, OT_WATERDEEP); } } setwaterdepth(c, f->val[2]); } } killflagsofid(map->flags, F_NEWWATERDEPTH); redrawresume(); // now handle effects on lifeforms and/or their objects for (l = map->lf ; l ; l = nextl) { nextl = l->next; //checkflagpile(l->flags); timeeffectslf(l); //checkflagpile(l->flags); } // time out warnings for (w = firstwarning ;w ; w = nextw) { nextw = w->next; if (w->lifetime > 0) { w->lifetime--; if (w->lifetime <= 0) killwarning(w); } } //dblog("AFTER SORT AND ADJUST....."); //dumplf(); } // end if timespent if (updategametime) { // inc game time inctime(firstlftime); // inc total gametime passed gamesecs += firstlftime; while (gamesecs >= DAYSECS) { gamesecs -= DAYSECS; gamedays++; } } // if it's the player's turn, announce sun set/rise if (isplayer(map->lf)) { int h,m,s; splittime(&h, &m, &s); flag_t *f; if (h != prevhour) { // effects which occur at certain times. if (godprayedto(R_GODLIFE)) { if (h == 6) { char text[BUFLEN]; switch (rnd(1,4)) { case 1: snprintf(text, BUFLEN, "The hour of Glorana's Peace is here."); break; case 2: snprintf(text, BUFLEN, "Mortal, rejoice in the hour of Glorana's Peace!"); break; case 3: snprintf(text, BUFLEN, "Now is the time of Glorana's Peace."); break; case 4: snprintf(text, BUFLEN, "Be healed my child - Glorana's Peace is upon you."); break; } godsay(R_GODLIFE, B_TRUE, text); more(); } else if (h == 7) { char text[BUFLEN]; switch (rnd(1,3)) { case 1: snprintf(text, BUFLEN, "Glorana's Peace has come to an end for today."); break; case 2: snprintf(text, BUFLEN, "...and so ends Glorana's Peace."); break; case 3: snprintf(text, BUFLEN, "I declare Glorana's Peace ended."); break; } godsay(R_GODLIFE, B_TRUE, text); more(); } } // midnight if (h == 0) { cell_t *c; condset_t cs; initcondv(&cs, CC_WALKABLE, B_TRUE, NA, CC_ISROOM, B_TRUE, NA, CC_NONE ); // lunar gate appears in a random spot on the player's level //c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); c = getcell_cond(map, &cs); if (c) { o = addobfast(c->obpile, OT_LUNARGATE); if (o) { // 60 turns is approximately 1 hour. maketemporary(o, 60, "vanishes"); } } } // if it's a new hour, announce the time. f = lfhasflagval(player, F_KNOWSTIME, B_TRUE, NA, NA, NULL); // check for full 24-hour knowledge first. if (!f) f = lfhasflag(player, F_KNOWSTIME); if (f && !isasleep(player) && !isblind(player)) { if (!isinbattle(player, B_FALSE, B_FALSE)) { announcetime(h,m,s, f->val[0]); } } } // end if h != prevhour prevhour = h; } if (db) dblog("cur time is %ld\n",curtime); } void usage(char *progname) { char *fmt = " -%c %-10s %s\n"; printf("usage: %s [options]\n",progname); printf(fmt, 'f', "filename", "Read player details from given file."); printf(fmt, 'h', "", "Show this text."); printf(fmt, 's', "", "Show the top 10 hiscores."); printf(fmt, 'S', "num", "Show the top 'num' hiscores."); printf("\n"); }