nexus/nexus.c

2468 lines
58 KiB
C

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#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;
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: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 '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) {
sprintf(buf, "%-20s(%s)", "Diety", "for debugging");
} else {
sprintf(buf, "%-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;
}
sprintf(buf, " - %-20s (%s", sp->name, getschoolname(getspellschoolknown(player, sp->id)) );
if (order[n] == FROMRACE) {
char rname[BUFLEN];
strcat(buf, ", ^g");
sprintf(rname, "%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;
}
sprintf(buf, " - %-20s", sp->name);
if (order[n] == FROMRACE) {
char rname[BUFLEN];
strcat(buf, " (^g");
sprintf(rname, "%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);
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);
sprintf(ques, "%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
//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;
// 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: sprintf(text, "The hour of Glorana's Peace is here."); break;
case 2: sprintf(text, "Mortal, rejoice in the hour of Glorana's Peace!"); break;
case 3: sprintf(text, "Now is the time of Glorana's Peace."); break;
case 4: sprintf(text, "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: sprintf(text, "Glorana's Peace has come to an end for today."); break;
case 2: sprintf(text, "...and so ends Glorana's Peace."); break;
case 3: sprintf(text, "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) {
printf("usage: %s [options] \n",progname);
printf("\t-f xx\tReads player details from file xx.\n");
printf("\t-s\tShows top 10 hiscores.\n");
printf("\t-S xx\tShows top xx hiscores (max 100).\n");
}