#include #include #include #include #include #include #include #include #include "ai.h" #include "attack.h" #include "data.h" #include "io.h" #include "flag.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; 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; 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; regiontype_t *firstregiontype = NULL,*lastregiontype = NULL; knowledge_t *knowledge = NULL, *lastknowledge = NULL; hiddenname_t *firsthiddenname = NULL, *lasthiddenname = NULL; npcname_t *npcname; int numnpcnames; int maxmonhitdice = 0; // highest number of hitdice for any monster double presin[360]; double precos[360]; void (*precalclos)(lifeform_t *) = NULL; 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; 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 extern int statdirty; extern int noredraw; int needredraw = B_TRUE; int numdraws = 0; // for xp list debugging extern race_t **raceposs; extern int *xpposs; int main(int argc, char **argv) { int newworld = B_FALSE; object_t *o; char welcomemsg[BUFLEN]; int ch; FILE *playerfile = NULL; int x,y,i; cell_t *c; vault_t *v; enum SKILLLEVEL slev; atexit(cleanup); while ((ch = getopt(argc, argv, "f:")) != -1) { switch (ch) { case 'f': playerfile = fopen(optarg, "rt"); if (!playerfile) { fprintf(stderr, "cannot open player file: %s\n",optarg); exit(1); } break; case 'h': case '?': default: usage(argv[0]); exit(0); } } // init params if (init()) { exit(1); } // load whatever maps are available loadall(); // 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 *c; c = &prompt.choice[i]; c->text = NULL; c->desc = NULL; c->longdesc = NULL; } // if no player (ie. didn't load a game), add them if (!player) { 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; // 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)) { addchoice(&prompt, ch++, r->name, NULL, r, r->desc); } } startrace = NULL; while (!startrace) { getchoicestr(&prompt, B_FALSE, B_TRUE); startrace = prompt.result; } } if (!j) { // ask for job initprompt(&prompt, "Select your job:"); ch = 'a'; for (j = firstjob ; j ; j = j->next) { if (!hasflag(j->flags, F_NOPLAYER)) { addchoice(&prompt, ch++, (j->id == J_GOD) ? "Diety (for debugging)" : j->name, NULL, j, j->desc); } } j = NULL; while (!j) { getchoicestr(&prompt, B_FALSE, B_TRUE); j = prompt.result; } } // make the initial level newworld = B_TRUE; // create world map. wregion = addregion(RG_WORLDMAP, NULL, -1); assert(wregion); addmap(); createmap(firstmap, 1, wregion, NULL, D_NONE, NULL); // create first dungeon dregion = findregionbytype(RG_FIRSTDUNGEON); assert(dregion); dmap = addmap(); createmap(dmap, 1, dregion, firstmap, D_DOWN, NULL); // create heaven hregion = addregion(RG_HEAVEN, NULL, -1); assert(hregion); heaven = addmap(); createmap(heaven, 1, hregion, NULL, D_NONE, NULL); // find staircase where = findobinmap(dmap, OT_STAIRSUP); assert(where); // make sure no lifeforms are there if (where->lf) { killlf(where->lf); } // add player in the starting position //where = real_getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL); where = findobinmap(dmap, OT_PLAYERSTART); if (!where) { dblog("fatal error: couldn't find player start position!"); msg("fatal error: couldn't find player start position!"); more(); exit(1); } real_addlf(where, startrace->id, 1, C_PLAYER); // this will assign 'player' addflag(player->flags, F_CANWILL, OT_A_CHECKSTAIRS, NA, NA, NULL); addflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL); addflag(player->flags, F_CANWILL, OT_A_TRAIN, NA, NA, NULL); addflag(player->flags, F_CANWILL, OT_A_DEBUG, NA, NA, NULL); ///////// o = hasob(where->obpile, OT_PLAYERSTART); killob(o); 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"); } givejob(player, j->id); // extra choices for some jobs if (j->id == J_WIZARD) { skill_t *sk; object_t *sb1,*sb2; initprompt(&prompt, "Select your primary spell specialty:"); addchoice(&prompt, 'a', getskillname(SK_SS_AIR), NULL, findskill(SK_SS_AIR), NULL); addchoice(&prompt, 'c', getskillname(SK_SS_COLD), NULL, findskill(SK_SS_COLD), NULL); addchoice(&prompt, 'f', getskillname(SK_SS_FIRE), NULL, findskill(SK_SS_FIRE), NULL); addchoice(&prompt, 'n', getskillname(SK_SS_DEATH), NULL, findskill(SK_SS_DEATH), NULL); addchoice(&prompt, 'w', getskillname(SK_SS_WILD), NULL, findskill(SK_SS_WILD), NULL); getchoice(&prompt); sk = (skill_t *) prompt.result; giveskilllev(player, sk->id, PR_NOVICE); switch (sk->id) { case SK_SS_AIR: addflag(player->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL); sb1 = addob(player->pack, "spellbook of air magic"); break; case SK_SS_COLD: sb1 = addob(player->pack, "spellbook of cold magic"); break; case SK_SS_FIRE: sb1 = addob(player->pack, "spellbook of fire magic"); break; case SK_SS_DEATH: sb1 = addob(player->pack, "spellbook of necromancy"); break; case SK_SS_WILD: sb1 = addob(player->pack, "spellbook of wild magic"); break; default: break; } if (sb1) { addflag(player->flags, F_CANCAST, sb1->contents->first->type->id, NA, NA, NULL); } initprompt(&prompt, "Select your secondary spell school:"); addchoice(&prompt, 'd', getskillname(SK_SS_DIVINATION), NULL, findskill(SK_SS_DIVINATION), NULL); addchoice(&prompt, 'g', getskillname(SK_SS_GRAVITY), NULL, findskill(SK_SS_GRAVITY), NULL); addchoice(&prompt, 'm', getskillname(SK_SS_MODIFICATION), NULL, findskill(SK_SS_MODIFICATION), NULL); addchoice(&prompt, 's', getskillname(SK_SS_SUMMONING), NULL, findskill(SK_SS_SUMMONING), NULL); addchoice(&prompt, 't', getskillname(SK_SS_TRANSLOCATION), NULL, findskill(SK_SS_TRANSLOCATION), NULL); getchoice(&prompt); sk = (skill_t *) prompt.result; giveskilllev(player, sk->id, PR_NOVICE); switch (sk->id) { case SK_SS_DIVINATION: sb2 = addob(player->pack, "spellbook of divination magic"); break; case SK_SS_GRAVITY: sb2 = addob(player->pack, "spellbook of gravitation magic"); break; case SK_SS_MODIFICATION: sb2 = addob(player->pack, "spellbook of modification magic"); break; case SK_SS_SUMMONING: sb2 = addob(player->pack, "spellbook of summoning magic"); break; case SK_SS_TRANSLOCATION: sb2 = addob(player->pack, "spellbook of translocation magic"); break; default: break; } if (sb2) { addflag(player->flags, F_CANCAST, sb2->contents->first->type->id, NA, NA, NULL); } identify(sb1); identify(sb2); } // 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 f = lfhasflag(player, F_HASPET); if (f) { cell_t *c; race_t *r; lifeform_t *pet; r = findracebyname(f->text); assert(r); // create it c = getrandomadjcell(player->cell, WE_WALKABLE, B_ALLOWEXPAND); assert(c); pet = addlf(c, r->id, 1); // mark us as its master petify(pet, player); } getplayernamefull(pname); snprintf(welcomemsg, BUFLEN, "Greetings %s, welcome to nexus!", pname); // 00:00 - 23:59 curtime = rnd(0,DAYSECS-1); } else { snprintf(welcomemsg, BUFLEN, "Welcome back!"); } // 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); player->facing = D_ALL; setlosdirty(player); // don't want any mosnters starting within los/lof of player 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))) { setcellknown(c, slev); if (c->lf && !isplayer(c->lf) && !ispetof(c->lf, player)) { killlf(c->lf); } } } } needredraw = B_TRUE; statdirty = B_TRUE; // start game gamemode = GM_GAMESTARTED; // redo light and player los calclight(player->cell->map); player->facing = rnd(DC_N, DC_NW); precalclos(player); // show level drawscreen(); msg("%s",welcomemsg); more(); // 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) //if (haslos(player, curmap->lf->cell)) { 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); return B_FALSE; } celltype_t *addcelltype(int id, char *name, char glyph, int colour, int solid, int transparent, enum MATERIAL mat, int floorheight) { 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; a->solid = solid; a->transparent = transparent; a->material = findmaterial(mat); a->floorheight = floorheight; a->flags = addflagpile(NULL, NULL); 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) { // die! die(lf); 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) { if (!player->alive) { gamemode = GM_GAMEOVER; gameover = B_TRUE; } } void cleanup(void) { free(xpposs); free(raceposs); fclose(logfile); cleanupgfx(); // free maps // free knowledge // free brands // free obtypes // free objects // free materials // free races } void dbtimestart(char *text) { gettimeofday(&tv, NULL); starttv = tv; dblog("START\t%s", text); } void dbtime(char *text) { double ticks; gettimeofday(&newtv, NULL); ticks = ((newtv.tv_sec - tv.tv_sec) * 1000000) + (newtv.tv_usec - tv.tv_usec); dblog("+%f\t%s", ticks, text); tv = newtv; } void dbtimeend(char *text) { double ticks; gettimeofday(&newtv, NULL); ticks = ((newtv.tv_sec - starttv.tv_sec) * 1000000) + (newtv.tv_usec - starttv.tv_usec); dblog("FINISHED %s (total time %f)", text, 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; 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); assert(who->timespent == 0); 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; // casting a spell? if (donormalmove) { f = lfhasflag(who, F_CASTINGSPELL); if (f) { donormalmove = B_FALSE; f->val[2]--; if (f->val[2] <= 0) { 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(p); p = readuntil(buf, p, ';'); obid = atol(p); p = readuntil(buf, p, ';'); mapid = atoi(p); p = readuntil(buf, p, ';'); x = atoi(p); p = readuntil(buf, p, ';'); y = atoi(p); 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); 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 = lfhasflag(who,F_SPELLCASTTEXT); if (f2 && strlen(f2->text)) { msg("%s %s.", lfname, f2->text); } else { msg("%s continues casting a spell.", lfname); } } } } } // 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)) { eat(who,o); 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; } } 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 handleinput(); } 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); } } } } // moved into a new map? stop turn. if (who->cell->map != map) { break; } } if (hasflag(player->flags, F_ASLEEP)) { needredraw = B_FALSE; } if (!isdead(who)) endlfturn(who); } // end 'if (who)' // 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); } } void gethitdicerange(int depth, int *min, int *max, int range, int oodok) { int mid; // adjust depth for out-of-depth monsters if (oodok && (onein(6))) { depth++; // repeated chances of incing level while (onein(6)) { depth++; } } mid = (depth/2); *min = mid - range; limit(min, 0, maxmonhitdice); *max = mid + range; limit(max, *min, maxmonhitdice); } enum COLOUR getpctcol(float num, float max) { float pct; pct = (num / max) * 100; if (pct > 100) { return C_BOLDBLUE; } else if (pct == 100) { return C_BOLDGREEN; } else if (pct >= 75) { return C_GREEN; } else if (pct >= 50) { return C_BROWN; } else if (pct >= 25) { return C_YELLOW; } else { // ie. < 25% return C_RED; } return C_ORANGE; } void getrarityrange(int depth, int *min, int *max, int range, int oodok) { int mid; int num; // adjust depth for out-of-depth things if (oodok && (onein(6))) { // repeated 1/3 chances of incing level num = rnd(1,6) - 4; while (num > 0) { depth += num; num = rnd(1,6) - 4; } } mid = 100 - (depth * 3); *min = mid - range; if (*min < 0) *min = 0; // *max = mid + range; if (*max > 100) *max = 100; *max = 100; //if (*min > 85) *min = 85; if (*max < 25) *max = 25; } int init(void) { int i; // random numbers srand(time(NULL)); // precalc sin/cos for (i = 0; i < 360; i++) { double rads; rads = i * (M_PI / 180); presin[i] = sin(rads); precos[i] = cos(rads); } precalclos = precalclos_new; gamemode = GM_INIT; playerglyph.ch = '@'; playerglyph.colour = C_GREY; tempglyph.ch = '@'; tempglyph.colour = C_GREY; // load npc names loadnpcnames(); initcommands(); initobjects(); initskills(); initjobs(); initrace(); initmap(); gamemode = GM_VALIDATION; if (validateobs()) { return B_TRUE; } if (validateraces()) { return B_TRUE; } // open log file logfile = fopen("log.txt","wt"); fprintf(logfile, "\n\n\n====== NEW LOGFILE ====\n"); // 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 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; } 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); numnpcnames = 0; while (!feof(f)) { buf[strlen(buf)-1] = '\0'; // strip newline if (strlen(buf)) { numnpcnames++; } fgets(buf, BUFLEN, f); } // alloc mem npcname = malloc(numnpcnames * sizeof(npcname_t)); // back to start fseek(f, 0, SEEK_SET); // now read in names fgets(buf, BUFLEN, f); while (!feof(f)) { 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; } 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 roll(char *string) { int ndice,nsides,bonus; int roll; texttodice(string, &ndice,&nsides,&bonus); roll = rolldie(ndice, nsides) + bonus; return roll; } // get a random number int rolldie(int ndice, int sides) { int i; int res = 0; for (i = 0; i < ndice; i++) { res += rnd(1,sides); } return res; } int rollhitdice(lifeform_t *lf, int wantmax) { flag_t *f; int ndice, nsides = 4, plus = 0; int myroll = 0; float mod; int db = B_FALSE; f = hasflag(lf->flags, F_HITDICE); if (f) { myroll = roll(f->text); } else { ndice = 1; nsides = 4; plus = 0; myroll = roll(DEF_HITDICE); } mod = 100 + getstatmod(lf, A_CON); if (db) dblog("rollhitdice() - mod is +%0.0f%%",mod); if (db) dblog("TOTAL: %d",myroll); // modify for fitness/con myroll = pctof(mod, myroll); if (db) dblog(" -> modified to: %d",myroll); limit(&myroll, 1, NA); return myroll; } int rollmpdice(lifeform_t *lf, int wantmax) { flag_t *f; int ndice, plus,roll,i; float mod; flag_t *retflag[MAXCANDIDATES]; int nretflags; f = hasflag(lf->flags, F_MPDICE); if (f) { ndice = f->val[0]; if (f->val[1] == NA) plus = 0; else plus = f->val[1]; } else { return 0; } mod = 100 + getstatmod(lf, A_IQ); if (wantmax) { roll = (ndice * 4) + plus; } else { roll = rolldie(ndice, 4) + plus; } 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 timeeffectsworld(map_t *map, int updategametime) { flag_t *retflag[MAXCANDIDATES]; int nretflags; lifeform_t *l; int db = B_FALSE; object_t *o,*nexto; int x,y; long firstlftime; enum SKILLLEVEL cartskill; 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); if (firstlftime > 0) { if (db) dblog("making firstlf timespent = 0 (currently %d):", firstlftime); //dumplf(); for (l = map->lf ; l ; l = l->next) { //dblog("shuffling id %d %s timespent=%d -> %d",l->id,l->race->name, l->timespent, l->timespent - firstlftime); l->timespent -= firstlftime; assert(l->timespent >= 0); /* if (isplayer(l)) { statdirty = B_TRUE; } */ } //dblog("after shuffle:"); //dumplf(); } 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; timeleft -= TICK_INTERVAL; // 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) { obsfallthrough(c, pit); } // go through each object in the cell... for (o = c->obpile->first ; o ; o = nexto) { nexto = o->next; 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 noredraw = B_TRUE; getflags(map->flags, retflag, &nretflags, F_NEWWATERDEPTH, F_NONE); 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]); } killflag(f); } noredraw = B_FALSE; // now handle effects on lifeforms and/or their objects for (l = map->lf ; l ; l = l->next) { timeeffectslf(l); } //dblog("AFTER SORT AND ADJUST....."); //dumplf(); } // end if timespent if (updategametime) { // inc game time curtime += (firstlftime*(TIMECONST)); // don't let it get higher than 23:59 while (curtime >= DAYSECS) { curtime -= DAYSECS; } // 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) && isoutdoors(map)) { int h,m,s; splittime(&h, &m, &s); if (h == 6) { msg("The sun is rising."); } else if (h == 18) { msg("The sun is setting."); } } if (db) dblog("cur time is %ld\n",curtime); } void usage(char *progname) { printf("usage: %s [ -f playerfile ]\n",progname); printf("\t-f xx\tReads player details from file xx.\n"); }