commit 19d30819ae9a9e41d252496645bd83cea81ea881 Author: Rob Pearce Date: Thu Dec 2 01:17:54 2010 +0000 initial checkin diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cfecc09 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +nexus: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h flag.c flag.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h + gcc -g -o nexus nexus.c ai.c attack.c flag.c io.c lf.c map.c move.c objects.c text.c save.c -lncurses diff --git a/ai.c b/ai.c new file mode 100644 index 0000000..b875245 --- /dev/null +++ b/ai.c @@ -0,0 +1,124 @@ +#include +#include +#include "ai.h" +#include "defs.h" +#include "flag.h" +#include "lf.h" +#include "map.h" +#include "objects.h" + +extern lifeform_t *player; + +void aimove(lifeform_t *lf) { + int dir; + int db = B_TRUE; + flag_t *f; + lifeform_t *target; + char buf[BUFLEN]; + if (db) dblog("AIMOVE: %s", lf->race->name); + + // if lifeform isn't alive, skip turn + if (isdead(lf)) { + taketime(lf, SPEED_DEAD); + return; + } + + + f = hasflag(lf->flags, F_TARGET); + if (f) { + int targid; + targid = f->val[0]; + target = findlf(lf->cell->map, targid); + if (target) { + if (haslos(lf, target->cell)) { + movetowards(lf, target->cell); + return; + } else { + // TODO: move towards last known location + + // just try to move in a random direction + dorandommove(lf); + return; + } + } + } else { + // not attacking anyone in particular + + // TODO: are we hostile? if so, look for a target + f = hasflag(lf->flags, F_HOSTILE); + if (f) { + int x,y; + cell_t *c; + // look around for a target + // TODO: use our vis rang einstead of 10! + for (y = lf->cell->y - 10; y <= lf->cell->y + 10; y++) { + for (x = lf->cell->x - 10; x <= lf->cell->x + 10; x++) { + c = getcellat(lf->cell->map, x, y); + // cell exists and we can see it? + if (c && haslos(lf, c)) { + // player there? + if (c->lf && c->lf->controller == C_PLAYER) { + // target them! + addflag(lf->flags, F_TARGET, c->lf->id, -1, -1, ""); + // tell the player + if (haslos(player, lf->cell)) { + getlfname(lf, buf); + capitalise(buf); + msg("%s sees you!", buf); + } + // then move towards them... + movetowards(lf, c); + return; + } + } + } + } + } + + // just try to move in a random direction + dorandommove(lf); + return; + } + + // if we get this far, just wait + dowait(lf); +} + +int getdirtowards(lifeform_t *lf, cell_t *dst) { + int d; + cell_t *c; + int mindist=9999,bestdir=D_NONE; + + for (d = DC_N; d <= DC_NW; d++) { + c = getcellindir(lf->cell, d); + if (!c) continue; + if (c == dst) { + // destination is adjacent! + bestdir = d; + break; + } + + if (canmove(lf, d)) { + int thisdist; + thisdist = getcelldist(c, dst); + if (thisdist < mindist) { + mindist = thisdist; + bestdir = d; + } + } + } + + // TODO: handle ties + + return bestdir; +} + +void movetowards(lifeform_t *lf, cell_t *dst) { + int dir; + // move towards them + dir = getdirtowards(lf, dst); + if (dir != D_NONE) { + trymove(lf, dir); + } +} + diff --git a/ai.h b/ai.h new file mode 100644 index 0000000..fc62c19 --- /dev/null +++ b/ai.h @@ -0,0 +1,5 @@ +#include "defs.h" + +void aimove(lifeform_t *lf); +int getdirtowards(lifeform_t *lf, cell_t *dst); +void movetowards(lifeform_t *lf, cell_t *dst); diff --git a/attack.c b/attack.c new file mode 100644 index 0000000..645583b --- /dev/null +++ b/attack.c @@ -0,0 +1,50 @@ +#include +#include +#include "attack.h" +#include "defs.h" + +extern lifeform_t *player; + +void doattack(lifeform_t *lf, lifeform_t *victim) { + int dam; + char buf[BUFLEN]; + char attackername[BUFLEN]; + char victimname[BUFLEN]; + int fatal = B_FALSE; + + // depends on weapon, race attackspeed modifier flag, etc + taketime(lf, getattackspeed(lf)); + + // TODO: figure out if you hit + // TODO: figure out if you do damage + dam = 1; + + getlfname(lf, attackername); + getlfname(victim, victimname); + + losehp(victim, dam, lf, attackername); // TODO: use player name if required + + if (victim->hp <= 0) { + fatal = B_TRUE; + } + + // announce it + if (lf->controller == C_PLAYER) { + msg("You %s %s%s", + fatal ? "kill" : "hit", + victimname, + fatal ? "!" : "."); + + // don't also say "the xx dies" + addflag(victim->flags, F_NODEATHANNOUNCE, B_TRUE, -1, -1, ""); + } else { + if (haslos(player, lf->cell)) { + // capitalise first letter + sprintf(buf, "%s",attackername); + capitalise(buf); + + msg("%s hits %s.", buf, victimname); + } + } + +} diff --git a/attack.h b/attack.h new file mode 100644 index 0000000..f99bfe4 --- /dev/null +++ b/attack.h @@ -0,0 +1,3 @@ +#include "defs.h" + +void doattack(lifeform_t *lf, lifeform_t *victim); diff --git a/defs.h b/defs.h new file mode 100644 index 0000000..4e99e01 --- /dev/null +++ b/defs.h @@ -0,0 +1,374 @@ +#ifndef __DEFS_H +#define __DEFS_H + +// MACROS +#define MAXOF(a,b) (a > b ? a : b) + +// save/load +#define MAPDIR "data/maps" +#define SAVEDIR "data/save" + +// SPECIAL NUMBERS/CONSTANTS +#define UNLIMITED (-9876) +#define ALL (-9875) +#define NA (-9874) + + +// STRINGS +#define BUFLENSMALL 64 +#define BUFLEN 128 +#define HUGEBUFLEN 1024 + + +#define MORESTRING "--More--" + + +// LIMITS + + +#define SCREENW 80 +#define SCREENH 24 + +#define MAXPILEOBS 52 + +#define MAXRANDOMOBCANDIDATES 100 + +#define MAX_MAPW 80 +#define MAX_MAPH 50 +//#define MAX_MAPROOMS 10 + +#define MIN_ROOMH 4 +#define MIN_ROOMW 4 +#define MAX_ROOMW (MAX_MAPW / 5) +#define MAX_ROOMH (MAX_MAPH / 5) + +#define MAXDIR_ORTH 4 +#define MAXDIR_COMPASS 8 + +// MAP BUILDING +#define DEF_TURNPCT 40 +#define DEF_SPARSENESS 14 +//#define DEF_SPARSENESS 0 +#define DEF_LOOPPCT 70 +//#define DEF_LOOPPCT 0 +#define MINROOMS 15 +#define MAXROOMS 25 +#define DEF_WINDOWPCT 5 + +// +#define ANIMDELAY (1000000 / 100) // 1/100 of a second + +// CONTROLLERS +#define C_AI 0 +#define C_PLAYER 1 + +// speed settings (lower is faster) +#define SPEED_ATTACK 10 +#define SPEED_DEAD 50 +#define SPEED_MOVE 10 +#define SPEED_DROP 5 +#define SPEED_PICKUP 5 +#define SPEED_THROW 10 +#define SPEED_WAIT 10 + + +// DIRECTION TYPES +#define DT_ORTH 0 +#define DT_COMPASS 1 + +// DIRECTIONS + + +#define D_NONE -1 +#define D_UNKNOWN -2 + +// Orthogonal directions +#define D_N 0 +#define D_E 1 +#define D_S 2 +#define D_W 3 + +// Compass directions +#define DC_N 4 +#define DC_NE 5 +#define DC_E 6 +#define DC_SE 7 +#define DC_S 8 +#define DC_SW 9 +#define DC_W 10 +#define DC_NW 11 + + + +// Cell types +#define CT_WALL 0 +#define CT_ROOMWALL 1 +#define CT_CORRIDOR 2 +#define CT_ROOM 3 +#define CT_DOOROPEN 4 +#define CT_DOORCLOSED 5 +#define CT_LOOPCORRIDOR 6 + +// Object Classes +enum OBCLASS { + OC_MONEY, + OC_WEAPON, + OC_ARMOUR, + OC_SCROLL, + OC_POTION, + OC_FOOD, + OC_ROCK, + OC_MISC, + OC_NULL = -999 +}; + +enum BLESSTYPE { + B_UNCURSED = 0, + B_BLESSED = 1, + B_CURSED = -1 +}; + +enum HABITAT { + H_DUNGEON = 1, + H_ALL = 999 +}; + +enum RARITY { + RR_NEVER = 6, + RR_VERYRARE = 5, + RR_RARE = 4, + RR_UNCOMMON = 3, + RR_COMMON = 2, + RR_FREQUENT = 1, +}; + + +enum RACE { + R_BAT, + R_GIANTFLY, + R_GIANTBLOWFLY, + R_HUMAN, + R_GOBLIN, +}; + +// Object Materials +enum MATERIAL { + MT_STONE, + MT_FIRE, + MT_PLASTIC, + MT_METAL, + MT_FLESH, + MT_WOOD, + MT_GOLD +}; + +// Object Types +enum OBTYPE { + // rocks + OT_GOLD, + OT_STONE, + // corpses + OT_CORPSEHUMAN, + OT_CORPSEGOBLIN, + OT_CORPSEBAT, + OT_CORPSEFLY, +}; + +enum FLAG { + // object flags + F_STACKABLE, // can stack multiple objects togethr + F_NO_PLURAL, // this obname doesn't need an 's' for plurals (eg. gold, money) + F_NO_A, // this obname doesn't need to start with 'a' for singular (eg. gold) + F_EDIBLE, // you can eat this. val2 = nutrition + // lifeform flags + F_CORPSETYPE, // text field specifies what corpse obtype to leave + F_FLYING, // lf is flying + F_HOSTILE, // lf will attack anything the player if in sight + F_NODEATHANNOUNCE, // don't say 'the xx dies' if this lf dies + F_TARGET, // lf will attack this lf id + F_ATTACKSPEED, // override default attack speed + F_MOVESPEED, // override default move speed + F_RARITY, // val[0] = habitat, val[1] = rarity + F_NUMAPPEAR, // when randomly appearing, can have > 1. val[0] = min, val[1] = max + // + F_NULL = -1 +}; + + +// probabilities +//#define CH_DEADENDOB 35 +//#define CH_EMPTYCELLOB 3 +#define CH_PILLAR 5 + +// Booleans +#define B_FALSE (0) +#define B_TRUE (-1) + +#define B_NOSTACK (0) +#define B_STACK (-1) +#define B_STACKOK (-1) + +#define NOOWNER (NULL) +#define NOLOC (NULL) + +#define B_NOTSOLID (0) +#define B_EMPTY (0) +#define B_SOLID (-1) + +#define B_OPAQUE (0) +#define B_TRANSPARENT (-1) +#define B_TRANS (-1) + + +// errors +enum ERROR { + E_OK = 0, + E_WALLINWAY = 1, + E_LFINWAY = 2, + E_NOSPACE = 3 +}; + +typedef struct map_s { + int id; + char *name; // name of this map + enum HABITAT habitat; // eg. dungeon, forest, etc + unsigned int seed; + int w,h; // width/height of this map + struct cell_s *cell[MAX_MAPW*MAX_MAPH]; // list of cells in this map + int nextmap[MAXDIR_ORTH]; // which map is in each direction + + long nextlfid; + struct lifeform_s *lf,*lastlf; + + struct map_s *next, *prev; +} map_t; //////////////// remember to modify save/load for new props!! + +typedef struct cell_s { + map_t *map; // pointer back to map + int x,y; // map coords + int roomid; + struct celltype_s *type; + struct obpile_s *obpile; + + // lifeform pile + struct lifeform_s *lf; + // known to player? + int known; + + // FOR CONSTRUCTION + int visited; +} cell_t; + +typedef struct celltype_s { + int id; // eg. dungeonfloor, wall, door + char glyph; // how to display it + int solid; // can you walk through it? + int transparent; // can you see through it? + struct celltype_s *next, *prev; +} celltype_t; + +typedef struct race_s { + enum RACE id; + char *name; + char glyph; + struct flagpile_s *flags; + // speed modifiers + // hit dice + struct race_s *next, *prev; +} race_t; + +typedef struct lifeform_s { + int id; + int controller; + struct race_s *race; + int hp,maxhp; + int alive; + char *lastdam; + + int timespent; + int sorted; + + struct obpile_s *pack; + + struct flagpile_s *flags; + + // for loading + long oblist[MAXPILEOBS]; + int x,y; + + + struct cell_s *cell; + struct lifeform_s *next, *prev; +} lifeform_t; + + +typedef struct obpile_s { + lifeform_t *owner;// } Only one of these + cell_t *where; // } should be filled in + struct object_s *first,*last; + + // for loading + long oblist[MAXPILEOBS]; +} obpile_t; + + +typedef struct flagpile_s { + struct flag_s *first,*last; +} flagpile_t; + +typedef struct flag_s { + enum FLAG id; + int nvals; + int val[3]; + char *text; + struct flagpile_s *pile; + struct flag_s *next, *prev; +} flag_t; + +typedef struct material_s { + int id; + char *name; + struct material_s *next,*prev; +} material_t; + +typedef struct objectclass_s { + enum OBCLASS id; + char *name; + char glyph; + struct objectclass_s *next, *prev; +} objectclass_t; + +typedef struct objecttype_s { + enum OBTYPE id; + char *name; + char *desc; + struct objectclass_s *obclass; + material_t *material; + float weight; // in kilograms + struct flagpile_s *flags; + struct objecttype_s *next, *prev; +} objecttype_t; + +typedef struct object_s { + long id; // unique for every ob in the game! + struct objecttype_s *type; + struct obpile_s *pile; // reverse pointer back to pile + // these variables are initially + // inherited from objecttype: + material_t *material; + float weight; // in kilograms + // flags + // these variables are NOT inherited + char *inscription; + char letter; + enum BLESSTYPE blessed; + int blessknown; + int amt; // for stackable objects + flagpile_t *flags; + + struct object_s *next, *prev; +} object_t; + + +#endif + diff --git a/doc/add_obclass.txt b/doc/add_obclass.txt new file mode 100644 index 0000000..e5e5c4d --- /dev/null +++ b/doc/add_obclass.txt @@ -0,0 +1,8 @@ +when adding a new obejct class: + +In defs.h: + add its OC_ enum + +In objects.c: + define the class with addoc() + add the class to sortorder[] at the top diff --git a/doc/add_race.txt b/doc/add_race.txt new file mode 100644 index 0000000..48c8a31 --- /dev/null +++ b/doc/add_race.txt @@ -0,0 +1,11 @@ +defs.h: + add RACE enum (R_xx) + add OT_CORPSExxx obtype + +lf.c: + addrace(R_xx) + + flagso + +objects.c: + addot(OTCORPSE) + diff --git a/flag.c b/flag.c new file mode 100644 index 0000000..322e93c --- /dev/null +++ b/flag.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include "defs.h" +#include "flag.h" +#include "text.h" + +flag_t *addflag(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text) { + flag_t *f; + int i; + + if (fp->first == NULL) { + fp->first = malloc(sizeof(flag_t)); + f = fp->first; + f->prev = NULL; + } else { + // go to end of list + f = fp->last; + f->next = malloc(sizeof(flag_t)); + f->next->prev = f; + f = f->next; + } + fp->last = f; + + f->next = NULL; + + // fill in props + f->id = id; // increment next ob id + + // first blank values + for (i = 0; i < 3; i++) { + f->val[i] = 0; + } + + f->val[0] = val1; + f->nvals = 1; + if (val2 != NA) { + f->val[1] = val2; + f->nvals++; + } + if (val3 != NA) { + f->val[2] = val2; + f->nvals++; + } + if (text) { + f->text = strdup(text); + } else { + f->text = strdup(""); + } + + f->pile = fp; + + return f; +} + +flagpile_t *addflagpile(void) { + flagpile_t *fp; + fp = malloc(sizeof(flagpile_t)); + fp->first = NULL; + fp->last = NULL; + + return fp; +} + +flag_t *hasflag(flagpile_t *fp, int id) { + flag_t *f; + for (f = fp->first ; f ; f = f->next) { + if (f->id == id) return f; + } + return NULL; +} + +flag_t *hasflagval(flagpile_t *fp, int id, int val1, int val2, int val3, char *text) { + flag_t *f; + for (f = fp->first ; f ; f = f->next) { + if (f->id == id) { + if ( ((val1 == -1) || (f->val[0] == val1)) && + ((val2 == -1) || (f->val[1] == val2)) && + ((val3 == -1) || (f->val[2] == val3)) && + ((text == NULL) || strstr(f->text, text))) { + return f; + } + } + } + return NULL; +} + +void killflag(flag_t *f) { + int i; + flag_t *nextone, *lastone; + + // free mem + + // remove from list + nextone = f->next; + if (nextone != NULL) { + nextone->prev = f->prev; + } else { /* last */ + f->pile->last = f->prev; + } + + if (f->prev == NULL) { + /* first */ + nextone = f->next; + f->pile->first = nextone; + free(f); + } else { + lastone = f->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + +void killflagpile(flagpile_t *fp) { + while (fp->first) { + killflag(fp->first); + } + free(fp); +} + diff --git a/flag.h b/flag.h new file mode 100644 index 0000000..d53b468 --- /dev/null +++ b/flag.h @@ -0,0 +1,10 @@ +#include "defs.h" + + +// functions +flag_t *addflag(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, char *text); +flagpile_t *addflagpile(void); +flag_t *hasflag(flagpile_t *fp, int id); +flag_t *hasflagval(flagpile_t *fp, int id, int val1, int val2, int val3, char *text); +void killflag(flag_t *f); +void killflagpile(flagpile_t *fp); diff --git a/io.c b/io.c new file mode 100644 index 0000000..a66ca40 --- /dev/null +++ b/io.c @@ -0,0 +1,921 @@ +#include +#include +#include +#include +#include "defs.h" +#include "io.h" +#include "map.h" +#include "move.h" +#include "nexus.h" +#include "objects.h" + +WINDOW *mainwin; +WINDOW *gamewin; +WINDOW *msgwin; +WINDOW *statwin; + +extern enum ERROR reason; + +extern FILE *logfile; +extern enum OBCLASS sortorder[]; + +extern int gamestarted; + +char msgbuf[HUGEBUFLEN]; + +extern lifeform_t *player; + +extern map_t *firstmap; + +int viewx = -9999,viewy = -9999; + +int vieww,viewh; + +void anim(cell_t *src, cell_t *dst, char ch) { + int deltax, deltay; + int numpixels; + int d; + int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2; + int xinc,yinc,dinc; + int i; + int x1,y1,dir; + int x; + int y; + int maxvisrange; + int modmaxvisrange; + int xray = B_FALSE; + int wentuphill = B_FALSE; + int origheight; + int shopwall; + + int x2,y2; + + // just in case + if (src->map != dst->map) return; + + x1 = src->x; + y1 = src->y; + x2 = dst->x; + y2 = dst->y; + + deltax = (x2 - x1); + if (deltax < 0) deltax = -deltax; + deltay = (y2 - y1); + if (deltay < 0) deltay = -deltay; + + // going nowhere + if ((deltax == 0) && (deltay == 0)) { + return; + } + + 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; + } + + x = x1; y = y1; + + for (i = 0; i < numpixels ; i++) { + cell_t *cell; + + // get current cell + cell = getcellat(src->map, x, y); + + // update screen + updateviewfor(cell); + drawlevelfor(player); + // draw char & cursor at its current pos... + mvwprintw(gamewin, cell->y - viewy, cell->x - viewx, "%c", ch); + wmove(gamewin, cell->y - viewy, cell->x - viewx); + wrefresh(gamewin); + usleep(ANIMDELAY); + + // move to next cell + if (d < 0) { + xinc = xinc1; + yinc = yinc1; + dinc = dinc1; + } else { + xinc = xinc2; + yinc = yinc2; + dinc = dinc2; + } + + d += dinc; + x += xinc; + y += yinc; + } +} + +cell_t *askcoords(char *prompt) { + int finished = B_FALSE; + cell_t *c,*newcell; + + c = player->cell; + + wclear(msgwin); + mvwprintw(msgwin, 0, 0, "%s", prompt); + wrefresh(msgwin); + + while (!finished) { + int dir; + char ch; + + drawstatus(); + updateviewfor(c); + drawlevelfor(player); + // move cursor selected position + wmove(gamewin, c->y - viewy, c->x - viewx); + redraw(); + + // get input + ch = getch(); + if (ch == '.') { + return c; + } else if (ch == 27) { // ESC - cancel + finished = B_TRUE; + } else { + dir = chartodir(ch); + if (dir != D_NONE) { + newcell = getcellindir(c, dir); + if (newcell) c = newcell; + } + } + } + return NULL; +} + +object_t *askobject(obpile_t *op, char *prompt, int *count) { + int c,i; + object_t *mylist[MAXPILEOBS+1]; + char myletters[MAXPILEOBS+1]; + char numstring[BUFLEN]; + int firstob = 0; + int nextpage = -1; + int lastline = SCREENH-2; + char buf[BUFLEN]; + int finished; + char nextlet = 'a'; + int useobletters; + + // if picking form a player's pack, use the object's letters. + // otherwise just label them a, b, c, etc. + if (op->owner && (op->owner->controller == C_PLAYER)) { + useobletters = B_TRUE; + } else { + useobletters = B_FALSE; + } + + // construct a list of objects + c = 0; + i = 0; + while (sortorder[c] != OC_NULL) { + object_t *o; + // add all objects of this class + for (o = op->first ; o ; o = o->next) { + if (o->type->obclass->id == sortorder[c]) { + mylist[i] = o; + myletters[i] = nextlet; + if (++nextlet > 'z') nextlet = 'A'; + + i++; + } + } + c++; + } + mylist[i] = NULL; + + // start displaying from the first one + firstob = 0; + nextpage = -1; + finished = B_FALSE; + strcpy(numstring, ""); + while (!finished) { + int lastclass = OC_NULL; + int y; + int ch; + + wclear(mainwin); + // list the objects + y = 2; + for (i = firstob ; (mylist[i] != NULL) && (y < lastline); i++) { + if (mylist[i]->type->obclass->id != lastclass) { + objectclass_t *oc; + // print class heading + mvwprintw(mainwin, y, 0, "%s", mylist[i]->type->obclass->name); + + lastclass = mylist[i]->type->obclass->id; + + y++; + } + getobname(mylist[i], buf,mylist[i]->amt); + mvwprintw(mainwin, y, 0, " %c - %s", + useobletters ? mylist[i]->letter : myletters[i], + buf); + y++; + } + if (mylist[i] == NULL) { + nextpage = -1; + } else { + nextpage = i; + } + // draw prompt + if (strlen(numstring) > 0) { + mvwprintw(mainwin, 0, 0, "%s (ESC to quit) [%s]: ",prompt, numstring); + } else { + mvwprintw(mainwin, 0, 0, "%s (ESC to quit): ", prompt); + } + if (nextpage != -1) { + mvwprintw(mainwin, y, 0, "-- More --"); + } + // update screen + wrefresh(mainwin); + // wait for keypess + ch = getch(); + if (ch == 27) { // ESCAPE + finished = B_TRUE; + break; + } + // otherwise look for shift key etc.. + ch = keycodetokey(ch); + // then handle input + if (ch == ' ') { // next page + if (nextpage == -1) { // go to first page + firstob = 0; + } else { + firstob = nextpage; + } + } else if (isalpha(ch)) { + object_t *o; + // describe that object + if (useobletters) { + o = findobl(op, ch); + } else { + o = NULL; + for (i = firstob ; (mylist[i] != NULL) && (y < lastline); i++) { + if (myletters[i] == ch) { + o = mylist[i]; + break; + } + } + } + if (o) { + // make sure count is okay... + if (count && *count > o->amt) { + *count = o->amt; + } + if (count && (*count == ALL)) { + *count = o->amt; + } + // display game windows again + drawscreen(); + return o; + } + } else if (isdigit(ch)) { + char temp[2]; + temp[0] = ch; + temp[1] = '\0'; + strcat(numstring, temp); + *count = atoi(numstring); + } else if (ch == 8) { // backspace + if (strlen(numstring) > 0) { + // remove last letter of number string + numstring[strlen(numstring)-1] = '\0'; + *count = atoi(numstring); + } + } + + // sanity check count... + if (count && (*count == 0)) { + strcpy(numstring, ""); + } + } + + // display game windows again + drawscreen(); + return NULL; +} + +void centre(WINDOW *win, int y, char *format, ... ) { + int w; + char buf[BUFLEN]; + va_list args; + + va_start(args, format); + vsprintf( buf, format, args ); + va_end(args); + + w = getmaxx(win); + mvwprintw(win, y, (w/2) - (strlen(buf)/2), buf); +} + + +int chartodir(char c) { + switch (tolower(c)) { + case 'h': return D_W; + case 'j': return D_S; + case 'k': return D_N; + case 'l': return D_E; + case 'y': return DC_NW; + case 'u': return DC_NE; + case 'b': return DC_SW; + case 'n': return DC_SE; + } + return D_NONE; +} + +void clearmsg(void) { + wclear(msgwin); + wrefresh(msgwin); +} + +int cleanupgfx(void) { + curs_set(1); + endwin(); + return B_FALSE; +} + + +void updateviewfor(cell_t *cell) { + // calculate viewport if required + if ((viewx == -9999) || (viewy == -9999)) { + // try to centre player + viewx = cell->x - (SCREENW/2); + viewy = cell->y - (SCREENH/2); + } + + while ((cell->x - viewx) >= ((SCREENW / 3)*2)) { + viewx++; + } + while ((cell->y - viewy) >= ((SCREENH / 3)*2)) { + viewy++; + } + while ((cell->x - viewx) <= (SCREENW/3)) { + viewx--; + } + while ((cell->y - viewy) <= (SCREENH/3)) { + viewy--; + } +} + + +void drawscreen(void) { + drawstatus(); + + updateviewfor(player->cell); + drawlevelfor(player); + drawcursor(); + redraw(); +} + + +void describeob(object_t *o) { + char buf[BUFLEN]; + int y; + material_t *m; + + wclear(mainwin); + + // title + getobname(o, buf,o->amt); + mvwprintw(mainwin, 0, 0, buf); + mvwprintw(mainwin, 2, 0, o->type->desc); + + // properties + y = 4; + mvwprintw(mainwin, y, 0, "%s made from %s.",(o->amt == 1) ? "It is" : "They are", o->material->name); y++; + + if (o->amt == 1) { + mvwprintw(mainwin, y, 0, "It weighs %0.1fkg.",o->weight); + } else { + mvwprintw(mainwin, y, 0, "They weigh %0.1fkg (%0.1f each).",(o->weight * o->amt), o->weight); + } + + wrefresh(mainwin); + + // wait for key + getch(); +} + +void dodrop(obpile_t *op) { + object_t *o; + char buf[BUFLEN]; + int count = ALL; + o = askobject(op, "Drop what", &count); + if (o) { + getobname(o, buf, count); + o = moveob(o, op->owner->cell->obpile, count); + if (o) { // if drop was successful... + if (op->owner) { + if (op->owner->controller == C_PLAYER) { + msg("You drop %s.",buf); + } + taketime(op->owner, (SPEED_DROP * count)); + } + } else { + // tell the player why! + if (op->owner->controller == C_PLAYER) { + switch (reason) { + case E_NOSPACE: + msg("There is no space here for any more objects!"); + break; + default: + msg("For some reason, you cannot drop %s!"); + break; + } + } + } + } +} + +void dopickup(lifeform_t *lf, obpile_t *op) { + int obcount; + object_t *o = NULL; + int howmany = ALL; + char buf[BUFLEN]; + + obcount = countobs(op); + // anything here? + if (obcount == 0) { + if (lf->controller == C_PLAYER) { + msg("There is nothing here to pick up!"); + } + return; + } else if (obcount == 1) { + // just get it + o = op->first; + howmany = ALL; + } else { + // prompt which one to pick up + o = askobject(op, "Pick up what", &howmany); + } + + if (!o) { + return; + } + + getobname(o, buf, howmany); + + // try to move whatever was selected + o = moveob(o, lf->pack, howmany); + if (o) { // if pickup was successful... + if (lf->controller == C_PLAYER) { + msg("You pick up %s.",buf); + } + taketime(lf, (SPEED_PICKUP * howmany)); + } else { + // tell the player why! + if (lf->controller == C_PLAYER) { + switch (reason) { + case E_NOSPACE: + msg("Your pack is too full to fit any more objects."); + break; + default: + msg("For some reason, you cannot pick up %s!"); + break; + } + } + } +} + +void doinventory(obpile_t *op) { + object_t *o; + o = askobject(op, "Select object to describe", NULL); + while (o) { + // describe it + describeob(o); + // ask for another one + o = askobject(op, "Select object to describe", NULL); + } +} + +void dothrow(obpile_t *op) { + object_t *o; + char buf[BUFLEN],buf2[BUFLEN]; + + // ask which object to throw + o = askobject(op, "Throw what", NULL); + if (o) { + cell_t *where; + getobname(o, buf, 1); + + // TODO: calculate throw range + + // ask where to throw it + sprintf(buf2, "Throw %s where?",buf); + where = askcoords(buf2); + + if (where) { + if (haslof(player, where)) { + throwat(player, o, where); + } else { + msg("You don't have a clear line of fire to there."); + } + } + } +} + +void drawcell(cell_t *cell, int x, int y) { + if (cell->lf) { // lifeform here? + // TODO: draw the lf's race glyph + mvwprintw(gamewin, y, x, "%c", cell->lf->race->glyph); + } else if (countobs(cell->obpile) > 0) { + object_t *o; + int c; + int drawn = B_FALSE; + // draw highest object in sort order + c = 0; + while ((sortorder[c] != OC_NULL) && (!drawn)) { + // check each object against this ob class + // count backwards so more recently dropped objects + // appear first. + for (o = cell->obpile->last ; o ; o = o->prev) { + if (o->type->obclass->id == sortorder[c]) { + // draw it + mvwprintw(gamewin, y, x, "%c", o->type->obclass->glyph); + drawn = B_TRUE; + break; + } + } + c++; + } + if (!drawn) { + // should never happen. if it does, just show the + // first object + dblog("Warn: sorted object glyph drawing matching nothing!"); + mvwprintw(gamewin, y, x, "%c", cell->obpile->first->type->obclass->glyph); + } + } else { + // draw ground + mvwprintw(gamewin, y, x, "%c", cell->type->glyph); + } +} + +void drawcursor(void) { + // move cursor to player position + wmove(gamewin, player->cell->y - viewy, player->cell->x - viewx); + wrefresh(gamewin); +} + +void drawlevelfor(lifeform_t *lf) { + int x,y; + int cx,cy; + cell_t *cell; + map_t *map; + map = lf->cell->map; + + wclear(gamewin); + for (y = viewy; y < viewy + viewh; y++) { + for (x = viewx; x < viewx + vieww; x++) { + cell = getcellat(map, x, y); + if (cell) { + if (haslos(lf, cell)) { + drawcell(cell, x-viewx, y-viewy); + } + } + } + } +} + +void initgfx(void) { + mainwin = initscr(); + if (!has_colors()) { + printf("Terminal does not support colour.\n"); + exit(1); + } + start_color(); + noecho(); + cbreak(); + nodelay(mainwin, FALSE); + + // determine window sizes + vieww = SCREENW; + viewh = SCREENH - 4; + + // create windows + msgwin = newwin(1, vieww, 0, 0); + gamewin = newwin(viewh, vieww, 1, 0); + statwin = newwin(2, vieww, 2 + viewh,0); + + redraw(); + refresh(); + + // init message buffer + strcpy(msgbuf, ""); +} + +int getkey(void) { + int key_code=0; + + key_code = getch(); + + return keycodetokey(key_code); +} + +void handleinput(void) { + int ch; + ch = getkey(); + + switch (ch) { + // movement + case 'h': + case 'j': + case 'k': + case 'l': + case 'y': + case 'u': + case 'b': + case 'n': + trymove(player, chartodir(ch)); + break; + case 'H': + case 'J': + case 'K': + case 'L': + case 'Y': + case 'U': + case 'B': + case 'N': + tryrun(player, chartodir(ch)); + break; + case '.': // wait + dowait(player); + break; + // testing + case '1': + msg("Something happens."); + msg("Something else happens."); + msg("Another thing is about to happen now."); + msg("Too many things are happening!"); + break; + // object functions + case 'd': // drop + dodrop(player->pack); + break; + case 'i': // inventory + doinventory(player->pack); + break; + case ',': // pickup + dopickup(player, player->cell->obpile); + break; + case 't': // throw + dothrow(player->pack); + break; + // GAME FUNCTIONS + case 'S': // save + quit + if (savegame()) { + msg("Save failed."); + drawmsg(); + } else { + msg("Saved successfully. See you later..."); + more(); + drawmsg(); + exit(0); + } + break; + } +} + + +int keycodetokey(int keycode) { + int keystroke = 0; + + if (keycode == -1) return -1; + + if (keycode == 27) { // an esc sequence + keycode=getch(); + keystroke=keycode; + keycode=getch(); + keystroke=keystroke | (keycode<<8); + keycode=getch(); + keystroke=keystroke | (keycode<<16); + } else { + // regular keypress + return (int)keycode; + } + + return keystroke; +} + +void dblog(char *format, ... ) { + char buf[HUGEBUFLEN]; + va_list args; + char *p; + + va_start(args, format); + vsprintf( buf, format, args ); + va_end(args); + + /* + if (gamestarted) { + fprintf(logfile, "%s\n", buf); + fflush(logfile); + } else { + printf("%s\n", buf); + fflush(stdout); + } + */ + fprintf(logfile, "%s\n", buf); + fflush(logfile); +} + +// force a '--more--' prompt +void more(void) { + msg("%100s"," "); +} + +void msg(char *format, ... ) { + char buf[BUFLEN]; + va_list args; + char *p; + int db = B_FALSE; + + va_start(args, format); + vsprintf( buf, format, args ); + va_end(args); + + if (db) dblog("adding to msgbuf: [%s]",buf); + + // Move to just after the last '^' in the message buffer... + p = strrchr(msgbuf, '^'); + if (p) { + p++; + } else { + p = msgbuf; + } + + // ie. can we fit the new text + '--more--' ? + if (strlen(p) + strlen(buf) + strlen(MORESTRING) >= SCREENW) { + strcat(msgbuf, "^"); + } else { + if (strlen(msgbuf) > 0) { + strcat(msgbuf, " "); + } + } + strcat(msgbuf, buf); + + if (db) dblog(" msgbuf is now: [%s]",msgbuf); + +} + +void drawstatus(void) { + char buf[BUFLEN]; + wclear(statwin); + sprintf(buf, "[%-12s] HP: %d/%d","Player", player->hp,player->maxhp); + mvwprintw(statwin, 0, 0, buf); + //redraw(); +} + + +void drawmsg(void) { + char *tok,*nexttok; + int db = B_FALSE; + + // no messages to display? + if (!strcmp(msgbuf, "")) { + return; + } + + // TODO: if it's a monster's turn, always do a more? + nexttok = strstr(msgbuf, "^"); + if (!nexttok) { + // just update msg with current text + wclear(msgwin); + mvwprintw(msgwin, 0, 0, "%s", msgbuf); + wrefresh(msgwin); + } else { + while (nexttok) { + char thistok[BUFLEN]; + int thistoklen; + // print up to just before the "^" current token + + // remember this token + + thistoklen = nexttok - msgbuf; + strncpy(thistok, msgbuf, thistoklen); + thistok[thistoklen] = '\0'; + if (db) dblog("drawmsg: thistok is [%s]",thistok); + + // is there another token? + nexttok = strstr(msgbuf, "^"); + + if (nexttok) { + char *p; + nexttok++; // go forward past the ^ + if (db) dblog("drawmsg: nexttok is [%s]",nexttok); + // print with '--more--' added to the end + if (db) dblog("drawmsg: displaying thistok [%s]",thistok); + wclear(msgwin); + mvwprintw(msgwin, 0, 0, "%s%s", thistok,MORESTRING); + wrefresh(msgwin); + + // ...wait for spacebar before showing next bit... + while (getch() != ' '); + if (db) dblog("prompting for --more--"); + + // now clear msgstring up to end of where + // 'thistok' appears! + strcpy(msgbuf, nexttok); + if (db) dblog(" reduced msgbuf to: [%s]",msgbuf); + } else { + if (db) dblog("drawmsg: no nexttok"); + // just print this bit + wclear(msgwin); + mvwprintw(msgwin, 0, 0, "%s", thistok); + wrefresh(msgwin); + } + } + } + + + // clear message buffer + if (isplayerturn()) { + strcpy(msgbuf, ""); + if (db) dblog("clearing msg buf"); + } + +} + +void redraw(void) { + //wrefresh(msgwin); + wrefresh(statwin); + wrefresh(gamewin); +} + +int savegame(void) { + map_t *m; + FILE *f; + char buf[BUFLEN]; + int rv; + for (m = firstmap; m ; m = m->next) { + // save world + rv = savemap(m); + if (rv) { + msg("Could not save map '%s'",m->name); + return B_TRUE; + } + // save player + their objects + sprintf(buf, "%s/game.sav",SAVEDIR); + f = fopen(buf, "wt"); + if (!f) { + msg("Could not open save file!"); + return B_TRUE; + } + savelf(f, player); + fclose(f); + } + + return B_FALSE; +} + +void tombstone(lifeform_t *lf) { + // clear screen + wclear(mainwin); + + centre(mainwin, 1, "R.I.P.\n"); + //printf("%s\n",lf->name); + centre(mainwin, 2, "Player\n");// TODO: use player name here + centre(mainwin, 3, "Killed by %s.\n",lf->lastdam); + + wrefresh(mainwin); + // wait for key... + getch(); + + // close down graphics ready to quit + // clear windows + delwin(gamewin); + delwin(statwin); + delwin(msgwin); + // clear screen + wclear(mainwin); + // close down curses + curs_set(1); + endwin(); + +} + + diff --git a/io.h b/io.h new file mode 100644 index 0000000..3e13f20 --- /dev/null +++ b/io.h @@ -0,0 +1,30 @@ +#include +#include "defs.h" + +void anim(cell_t *src, cell_t *dst, char ch); +object_t *askobject(obpile_t *op, char *title, int *count); +cell_t *askcoords(char *prompt); +void centre(WINDOW *win, int y, char *format, ... ); +int chartodir(char ch); +void clearmsg(void); +void describeob(object_t *o); +void dodrop(obpile_t *op); +void doinventory(obpile_t *op); +void dopickup(lifeform_t *lf, obpile_t *op); +void dothrow(obpile_t *op); +void drawcell(cell_t *cell, int x, int y); +void drawcursor(void); +void drawlevelfor(lifeform_t *lf); +void drawmsg(void); +void drawscreen(void); +void drawstatus(void); +int getkey(void); +void handleinput(void); +int keycodetokey(int keycode); +void more(void); +void msg(char *format, ... ); +void dblog(char *format, ... ); +void redraw(void); +int savequit(void); +void tombstone(lifeform_t *lf); +void updateviewfor(cell_t *cell); diff --git a/lf.c b/lf.c new file mode 100644 index 0000000..bfadba8 --- /dev/null +++ b/lf.c @@ -0,0 +1,639 @@ +#include +#include +#include +#include "defs.h" +#include "flag.h" +#include "lf.h" +#include "map.h" +#include "objects.h" + +extern race_t *firstrace, *lastrace; +extern lifeform_t *player; + + +lifeform_t *addlf(cell_t *cell, enum RACE rid) { + map_t *m; + lifeform_t *a; + int i; + flag_t *f; + + m = cell->map; + + // add to the end of the list + if (m->lf == NULL) { + m->lf = malloc(sizeof(lifeform_t)); + a = m->lf; + a->prev = NULL; + } else { + // go to end of list + a = m->lastlf; + a->next = malloc(sizeof(lifeform_t)); + a->next->prev = a; + a = a->next; + } + m->lastlf = a; + a->next = NULL; + + // props + a->id = m->nextlfid; m->nextlfid++; + a->controller = C_AI; + a->race = findrace(rid); + a->hp = 10; // TODO: fix + a->maxhp = 10; // TODO: fix + a->cell = cell; // TODO: fix + a->alive = B_TRUE; + a->lastdam = strdup("nothing"); + a->timespent = 0; + a->sorted = B_FALSE; + + a->pack = addobpile(a, NOLOC); + + // clear laoding variables + for (i = 0; i < MAXPILEOBS; i++) { + a->oblist[i] = -1; + } + a->x = -1; + a->y = -1; + + // inherit flags from race + a->flags = addflagpile(); + for (f = a->race->flags->first ; f ; f = f->next) { + addflag(a->flags, f->id, f->val[0], f->val[1], f->val[2], f->text); + } + + // update other things + cell->lf = a; +} + +race_t *addrace(enum RACE id, char *name, char glyph) { + race_t *a; + + // add to the end of the list + if (firstrace == NULL) { + firstrace = malloc(sizeof(race_t)); + a = firstrace; + a->prev = NULL; + } else { + // go to end of list + a = lastrace; + a->next = malloc(sizeof(race_t)); + a->next->prev = a; + a = a->next; + } + lastrace = a; + a->next = NULL; + + // props + a->id = id; + a->name = strdup(name); + a->glyph = glyph; + + a->flags = addflagpile(); +} + +void die(lifeform_t *lf) { + char buf[BUFLEN]; + object_t *o, *nexto; + flag_t *f; + + lf->alive = B_FALSE; + if (lf->controller == C_PLAYER) { + // forec screen redraw so you see your hp = 0 + drawscreen(); + msg("You die."); + more(); + // force msg redraw! + drawmsg(); + } else { + if (!hasflag(lf->flags, F_NODEATHANNOUNCE)) { + if (haslos(player, lf->cell)) { + getlfname(lf, buf); + capitalise(buf); + msg("%s dies.",buf); + } + } + } + + // drop all objects + while (lf->pack->first) { + if (!moveob(lf->pack->first, lf->cell->obpile, ALL)) { + killob(lf->pack->first); + } + } + + // drop corpse + f = hasflag(lf->flags, F_CORPSETYPE); + if (f) { + addob(lf->cell->obpile, f->text); + } + + if (lf->controller != C_PLAYER) { + // kill lifeform + killlf(lf); + } +} + +lifeform_t *findlf(map_t *m, int lfid) { + lifeform_t *lf; + for (lf = m->lf ; lf ; lf = lf->next) { + if (lf->id == lfid) return lf; + } + return NULL; +} + +race_t *findrace(enum RACE id) { + race_t *r; + for (r = firstrace; r ; r = r->next) { + if (r->id == id) { + return r; + } + } + + return NULL; +} + +int getattackspeed(lifeform_t *lf) { + int speed = SPEED_ATTACK; + flag_t *f; + f = hasflag(lf->flags, F_ATTACKSPEED); + if (f) { + speed = f->val[0]; + } + return speed; +} + +int getmovespeed(lifeform_t *lf) { + int speed = SPEED_MOVE; + flag_t *f; + f = hasflag(lf->flags, F_MOVESPEED); + if (f) { + speed = f->val[0]; + } + return speed; +} + + +char *getlfname(lifeform_t *lf, char *buf) { + if (lf == player) { + sprintf(buf, "you"); + } else { + sprintf(buf, "the %s",lf->race->name); + } + return buf; +} + +int haslof(lifeform_t *viewer, cell_t *dest) { + int deltax, deltay; + int numpixels; + int d; + int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2; + int xinc,yinc,dinc; + int i; + int x1,y1,dir; + int x; + int y; + int maxvisrange; + int modmaxvisrange; + int xray = B_FALSE; + int wentuphill = B_FALSE; + int origheight; + int shopwall; + + int x2,y2; + map_t *map; + + // must have line of sight to fire... + if (!haslos(viewer, dest)) return B_FALSE; + + if (viewer->cell->map != dest->map) return B_FALSE; + map = dest->map; + + + x1 = viewer->cell->x; + y1 = viewer->cell->y; + x2 = dest->x; + y2 = dest->y; + + deltax = (x2 - x1); + if (deltax < 0) deltax = -deltax; + deltay = (y2 - y1); + if (deltay < 0) deltay = -deltay; + + // can always fire at your own cell + if ((deltax == 0) && (deltay == 0)) { + return B_TRUE; + } + + 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; + } + + x = x1; y = y1; + + for (i = 0; i < numpixels ; i++) { + cell_t *cell; + + // don't need to move out of the last one + if ((x == x2) && (y == y2)) { + break; + } + + if (d < 0) { + xinc = xinc1; + yinc = yinc1; + dinc = dinc1; + } else { + xinc = xinc2; + yinc = yinc2; + dinc = dinc2; + } + + // solid cells stop lof + cell = getcellat(map, x, y); + if (cell->type->solid) { + return B_FALSE; + } + + // move to next cell + d += dinc; + x += xinc; + y += yinc; + } + + // made it to the target cell! + return B_TRUE; +} + +int haslos(lifeform_t *viewer, cell_t *dest) { + int deltax, deltay; + int numpixels; + int d; + int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2; + int xinc,yinc,dinc; + int i; + int x1,y1,dir; + int x; + int y; + int maxvisrange; + int modmaxvisrange; + int xray = B_FALSE; + int wentuphill = B_FALSE; + int origheight; + int shopwall; + + int x2,y2; + map_t *map; + + if (viewer->cell->map != dest->map) return B_FALSE; + map = dest->map; + + + x1 = viewer->cell->x; + y1 = viewer->cell->y; + x2 = dest->x; + y2 = dest->y; + + + /* + if (hasproplf(viewer, P_XRAYVISION)) { + xray = getproplf(viewer, P_XRAYVISION); + } + */ + + + deltax = (x2 - x1); + if (deltax < 0) deltax = -deltax; + deltay = (y2 - y1); + if (deltay < 0) deltay = -deltay; + + // can always see your own cell + if ((deltax == 0) && (deltay == 0)) { + //if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM; + return B_TRUE; + } + + /* + if (hasproplf(viewer, P_BLINDED)) { + return B_FALSE; + } + */ + + 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; + } + + x = x1; y = y1; + + //maxvisrange = getvisrange(viewer, B_LIGHTDOESNTMATTER); + maxvisrange = UNLIMITED; + + /* + if (hasproplf(viewer, P_SEEINDARK) || wreck->mazelev[viewer->z].lightlevel >= 100) { + modmaxvisrange = maxvisrange; + } else { + modmaxvisrange = getvisrange(viewer, B_LIGHTMATTERS); + } + */ + + //origheight = getheight(x1,y1,z); + + // too far away + if (maxvisrange != UNLIMITED) { + if (getcelldist(viewer->cell, dest) > maxvisrange) { + return B_FALSE; + } + } + + // outside our modified vision range, and not lit + /* + if (modmaxvisrange != UNLIMITED) { + if (getdistance(x1,y1,x2,y2) > modmaxvisrange) { + if (!xyislit(x2,y2,viewer->z)) { + return B_FALSE; + } + } + } + */ + + //shopwall = B_FALSE; + + for (i = 0; i < numpixels ; i++) { + cell_t *cell; + + // if we went uphill, stop here + /* + if (wentuphill) { + return B_FALSE; + } + */ + + // don't need to move out of the last one + if ((x == x2) && (y == y2)) { + break; + } + + if (d < 0) { + xinc = xinc1; + yinc = yinc1; + dinc = dinc1; + } else { + xinc = xinc2; + yinc = yinc2; + dinc = dinc2; + } + + /* + if (debug) { + printf("checking LOS at %d,%d going (%d,%d)\n",x,y,xinc,yinc); + fflush(stdout); + } + */ + + // solid cells stop los + cell = getcellat(map, x, y); + if (!cell->type->transparent) { + if (xray) xray--; + else return B_FALSE; + } + + /* + // check for smoke + if ((x != x1) || (y != y1)) { // if not in first cell + if (hasopaqueobject(viewer, x,y,z) && (getheight(x,y,z) >= origheight)) { + if (!hasproplf(viewer, P_SEEINSMOKE)) { + return B_FALSE; + } + } + } + */ + + // xray vision decreases by one + if (xray) xray--; + + // if you went uphill, can't see any further + /* + if (getheight(x,y,z) > origheight) { + wentuphill = B_TRUE; + } + */ + + // move to next cell + d += dinc; + x += xinc; + y += yinc; + } + + // made it to the target cell! + /* + if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM; + if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM; + */ + return B_TRUE; +} + + +void initrace(void) { + addrace(R_HUMAN, "human", '@'); + addflag(lastrace->flags, F_CORPSETYPE, -1, -1, -1, "human corpse"); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_RARE, -1, ""); + addrace(R_GOBLIN, "goblin", 'g'); + addflag(lastrace->flags, F_CORPSETYPE, -1, -1, -1, "goblin corpse"); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, -1, -1, ""); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_COMMON, -1, ""); + addrace(R_BAT, "bat", 'B'); + addflag(lastrace->flags, F_CORPSETYPE, -1, -1, -1, "bat corpse"); + addflag(lastrace->flags, F_ATTACKSPEED, 5, -1, -1, ""); + addflag(lastrace->flags, F_MOVESPEED, 5, -1, -1, ""); + addflag(lastrace->flags, F_FLYING, B_TRUE, -1, -1, ""); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_COMMON, -1, ""); + addrace(R_GIANTFLY, "giant fly", 'I'); + addflag(lastrace->flags, F_CORPSETYPE, -1, -1, -1, "giant fly corpse"); + addflag(lastrace->flags, F_MOVESPEED, 2, -1, -1, ""); + addflag(lastrace->flags, F_FLYING, B_TRUE, -1, -1, ""); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_COMMON, -1, ""); + addrace(R_GIANTBLOWFLY, "giant blowfly", 'I'); + addflag(lastrace->flags, F_CORPSETYPE, -1, -1, -1, "giant fly corpse"); + addflag(lastrace->flags, F_MOVESPEED, 2, -1, -1, ""); + addflag(lastrace->flags, F_FLYING, B_TRUE, -1, -1, ""); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, RR_UNCOMMON, -1, ""); +} + +int isdead(lifeform_t *lf) { + if (!lf->alive) return B_TRUE; + if (lf->hp <= 0) return B_TRUE; + return B_FALSE; +} + +void killlf(lifeform_t *lf) { + int i; + lifeform_t *nextone, *lastone; + map_t *m; + + m = lf->cell->map; + + // remove references + lf->cell->lf = NULL; + + // free mem + if (lf->lastdam) free(lf->lastdam); + + // kill any remaining obs + while (lf->pack->first) { + killob(lf->pack->first); + } + // kill object pile + free(lf->pack); + + // kill flags + while (lf->flags->first) { + killflag(lf->flags->first); + } + killflagpile(lf->flags); + + // remove from list + nextone = lf->next; + if (nextone != NULL) { + nextone->prev = lf->prev; + } else { /* last */ + m->lastlf = lf->prev; + } + + if (lf->prev == NULL) { + /* first */ + nextone = lf->next; + free(m->lf); + m->lf = nextone; + } else { + lastone = lf->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + + + +void killrace(race_t *race) { + int i; + race_t *nextone, *lastone; + + // free mem + free(race->name); + + // remove from list + nextone = race->next; + if (nextone != NULL) { + nextone->prev = race->prev; + } else { /* last */ + lastrace = race->prev; + } + + if (race->prev == NULL) { + /* first */ + nextone = race->next; + free(race); + race = nextone; + } else { + lastone = race->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + + +void losehp(lifeform_t *lf, int amt, lifeform_t *fromlf, char *damsrc) { + char buf[BUFLEN]; + lf->hp -= amt; + if (lf->lastdam) { + free(lf->lastdam); + } + + // replace 'the' at start of damsrc with 'a' + if (strstr(damsrc, "the ") == damsrc) { + sprintf(buf, "a %s", (damsrc+4)); + } else { + strcpy(buf, damsrc); + } + lf->lastdam = strdup(buf); + + // they will now fight back! + if (fromlf && !isdead(lf)) { + if (lf->controller != C_PLAYER) { + if (!hasflagval(lf->flags, F_TARGET, fromlf->id, -1, -1, "")) { + addflag(lf->flags, F_TARGET, fromlf->id, -1, -1, ""); + // announce + if (haslos(player, lf->cell)) { + char fromlfname[BUFLEN]; + char lfname[BUFLEN]; + getlfname(fromlf, fromlfname); + getlfname(lf, lfname); + capitalise(lfname); + msg("%s turns to attack %s!", lfname, + haslos(player, fromlf->cell) ? fromlfname : "something"); + } + } + } + } +} + +// give initial equiment to a lifeform +void outfitlf(lifeform_t *lf) { + if (lf->controller == C_PLAYER) { + addob(lf->pack, "10 gold"); + } +} + +void taketime(lifeform_t *lf, int howlong) { + // inc timespent + lf->timespent += howlong; + // TODO: decrement lifeform's (or their object's) temporary flags +} diff --git a/lf.h b/lf.h new file mode 100644 index 0000000..0d54b3d --- /dev/null +++ b/lf.h @@ -0,0 +1,19 @@ +#include "defs.h" + + +lifeform_t *addlf(cell_t *cell, enum RACE rid); +race_t *addrace(enum RACE id, char *name, char glyph); +void die(lifeform_t *lf); +lifeform_t *findlf(map_t *m, int lfid); +race_t *findrace(enum RACE id); +int getattackspeed(lifeform_t *lf); +int getmovespeed(lifeform_t *lf); +char *getlfname(lifeform_t *lf, char *buf); +int haslof(lifeform_t *viewer, cell_t *dest); +int haslos(lifeform_t *viewer, cell_t *dest); +void initrace(void); +int isdead(lifeform_t *lf); +void killlf(lifeform_t *lf); +void killrace(race_t *race); +void losehp(lifeform_t *lf, int amt, lifeform_t *fromlf, char *damsrc); +void taketime(lifeform_t *lf, int howlong); diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..bebaa48 --- /dev/null +++ b/log.txt @@ -0,0 +1,939 @@ + + + +====== NEW LOGFILE ==== +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 38,2 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 25,3 +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 51,4 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 21 gold coin to cell 5,5 +adding random object of rarity 1 + +got 2 possibilities. +adding 56 gold coin to cell 5,6 +adding random object of rarity 1 + +got 2 possibilities. +adding 79 gold coin to cell 23,6 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 2 stone to cell 50,6 +adding random object of rarity 1 + +got 2 possibilities. +adding 8 stone to cell 39,7 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 9,8 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 65 gold coin to cell 12,9 +adding random object of rarity 1 + +got 2 possibilities. +adding 16 gold coin to cell 12,10 +adding random object of rarity 1 + +got 2 possibilities. +adding 99 gold coin to cell 37,10 +adding random object of rarity 1 + +got 2 possibilities. +adding 17 gold coin to cell 49,11 +adding random object of rarity 1 + +got 2 possibilities. +adding 79 gold coin to cell 51,11 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 31 gold coin to cell 2,12 +adding random object of rarity 1 + +got 2 possibilities. +adding 8 stone to cell 31,12 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 35,15 +adding random object of rarity 1 + +got 2 possibilities. +adding 22 gold coin to cell 60,15 +adding random object of rarity 1 + +got 2 possibilities. +adding 42 gold coin to cell 39,17 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 10,18 +adding random object of rarity 1 + +got 2 possibilities. +adding 37 gold coin to cell 14,18 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 3 stone to cell 34,18 +adding random object of rarity 1 + +got 2 possibilities. +adding 95 gold coin to cell 55,18 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 2 stone to cell 23,20 +adding random object of rarity 1 + +got 2 possibilities. +adding 46 gold coin to cell 39,20 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 52,21 +adding random object of rarity 1 + +got 2 possibilities. +adding 92 gold coin to cell 21,24 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 10 stone to cell 20,25 +adding random object of rarity 1 + +got 2 possibilities. +adding 54 gold coin to cell 6,26 +adding random object of rarity 1 + +got 2 possibilities. +adding 29 gold coin to cell 44,26 +adding random object of rarity 1 + +got 2 possibilities. +adding 77 gold coin to cell 52,27 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 40 gold coin to cell 48,31 +adding random object of rarity 1 + +got 2 possibilities. +adding 44 gold coin to cell 24,33 +adding random object of rarity 1 + +got 2 possibilities. +adding 6 stone to cell 3,35 +adding random object of rarity 1 + +got 2 possibilities. +adding 62 gold coin to cell 24,35 +adding random object of rarity 1 + +got 2 possibilities. +adding 14 gold coin to cell 21,36 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 46,38 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 2,39 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 44,40 +adding random object of rarity 1 + +got 2 possibilities. +adding 71 gold coin to cell 11,41 +adding random object of rarity 1 + +got 2 possibilities. +adding 11 gold coin to cell 12,41 +adding random object of rarity 1 + +got 2 possibilities. +adding 5 stone to cell 28,41 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 10,42 +adding random object of rarity 1 + +got 2 possibilities. +adding 28 gold coin to cell 30,46 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 13 gold coin to cell 7,47 +adding random object of rarity 1 + +got 2 possibilities. +adding 44 gold coin to cell 15,47 +--> Will add 9 objects to room 0 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 21,17 +adding random object of rarity 1 + +got 2 possibilities. +adding 17 gold coin to cell 24,3 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 88 gold coin to cell 12,27 +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 25,21 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 33 gold coin to cell 25,26 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 86 gold coin to cell 20,9 +adding random object of rarity 1 + +got 2 possibilities. +adding 90 gold coin to cell 1,47 +adding random object of rarity 1 + +got 2 possibilities. +adding 31 gold coin to cell 48,9 +adding random object of rarity 1 + +got 2 possibilities. +adding 91 gold coin to cell 7,1 +--> Will add 12 objects to room 1 (of 23) +adding random object of rarity 4 + +no possible objects like this. trying again with rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 94 gold coin to cell 53,16 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 6 stone to cell 57,19 +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 54,16 +adding random object of rarity 1 + +got 2 possibilities. +adding 5 stone to cell 49,16 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 61,16 +adding random object of rarity 1 + +got 2 possibilities. +adding 8 stone to cell 52,20 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 4 stone to cell 50,20 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 5 stone to cell 55,19 +adding random object of rarity 1 + +got 2 possibilities. +adding 10 stone to cell 58,21 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 60,14 +adding random object of rarity 1 + +got 2 possibilities. +adding 6 stone to cell 59,20 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 50,16 +--> Will add 3 pillars +--> Will add 10 objects to room 2 (of 23) +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 7 stone to cell 6,7 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 9 stone to cell 3,5 +adding random object of rarity 1 + +got 2 possibilities. +adding 94 gold coin to cell 6,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 3,8 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 5 stone to cell 3,7 +adding random object of rarity 1 + +got 2 possibilities. +adding 5 stone to cell 4,2 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 71 gold coin to cell 2,4 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 31 gold coin to cell 1,9 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 4 stone to cell 5,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 10 stone to cell 6,2 +--> Will add 4 objects to room 3 (of 23) +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 53 gold coin to cell 24,29 +adding random object of rarity 1 + +got 2 possibilities. +adding 6 stone to cell 27,30 +adding random object of rarity 1 + +got 2 possibilities. +adding 58 gold coin to cell 30,28 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 35 gold coin to cell 35,29 +--> Will add 5 objects to room 4 (of 23) +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 7 stone to cell 4,37 +adding random object of rarity 1 + +got 2 possibilities. +adding 8 stone to cell 2,35 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 5 stone to cell 1,37 +adding random object of rarity 1 + +got 2 possibilities. +adding 35 gold coin to cell 5,38 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 10 stone to cell 3,38 +--> Will add 3 objects to room 5 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 50,41 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 21 gold coin to cell 50,39 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 1 stone to cell 42,40 +--> Will add 5 objects to room 6 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 50,29 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 6 stone to cell 43,25 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 10 stone to cell 45,32 +adding random object of rarity 1 + +got 2 possibilities. +adding 56 gold coin to cell 47,29 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 46,29 +--> Will add 1 objects to room 7 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 38,4 +--> Will add 9 objects to room 8 (of 23) +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 37 gold coin to cell 14,43 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 17,44 +adding random object of rarity 1 + +got 2 possibilities. +adding 73 gold coin to cell 11,42 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 18,47 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 20,46 +adding random object of rarity 1 + +got 2 possibilities. +adding 10 stone to cell 15,40 +adding random object of rarity 1 + +got 2 possibilities. +adding 84 gold coin to cell 11,46 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 19,48 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 9 stone to cell 9,44 +--> Will add 7 objects to room 9 (of 23) +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 3 stone to cell 42,46 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 5 stone to cell 37,47 +adding random object of rarity 1 + +got 2 possibilities. +adding 99 gold coin to cell 44,43 +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 45,43 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 1 gold coin to cell 37,45 +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 35,43 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 5 stone to cell 43,47 +--> Will add 2 objects to room 10 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 41,8 +adding random object of rarity 1 + +got 2 possibilities. +adding 8 gold coin to cell 40,9 +--> Will add 4 objects to room 11 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 3 stone to cell 18,7 +adding random object of rarity 1 + +got 2 possibilities. +adding 13 gold coin to cell 30,4 +adding random object of rarity 1 + +got 2 possibilities. +adding 6 stone to cell 32,5 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 1 gold coin to cell 29,6 +--> Will add 5 objects to room 12 (of 23) +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 66 gold coin to cell 18,36 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 17,39 +adding random object of rarity 1 + +got 2 possibilities. +adding 14 gold coin to cell 17,37 +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 14,37 +adding random object of rarity 4 + +no possible objects like this. trying again with rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 25 gold coin to cell 18,38 +--> Will add 7 objects to room 13 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 50 gold coin to cell 28,46 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 7 gold coin to cell 27,45 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 29,43 +adding random object of rarity 1 + +got 2 possibilities. +adding 26 gold coin to cell 26,46 +adding random object of rarity 1 + +got 2 possibilities. +adding 34 gold coin to cell 25,47 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 26,42 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 22,45 +--> Will add 5 objects to room 14 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 32 gold coin to cell 9,23 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 7 stone to cell 19,20 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 1 stone to cell 17,17 +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 17,18 +adding random object of rarity 1 + +got 2 possibilities. +adding 24 gold coin to cell 14,19 +--> Will add 11 objects to room 15 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 57 gold coin to cell 33,38 +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 39,38 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 30 gold coin to cell 40,41 +adding random object of rarity 1 + +got 2 possibilities. +adding 16 gold coin to cell 34,39 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 71 gold coin to cell 31,39 +adding random object of rarity 1 + +got 2 possibilities. +adding 10 stone to cell 41,39 +adding random object of rarity 1 + +got 2 possibilities. +adding 39 gold coin to cell 39,39 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 40,40 +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 30,41 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 2 stone to cell 31,40 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 3 stone to cell 34,41 +--> Will add 0 objects to room 16 (of 23) +--> Will add 2 pillars +--> Will add 10 objects to room 17 (of 23) +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 50 gold coin to cell 1,31 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 2,30 +adding random object of rarity 1 + +got 2 possibilities. +adding 74 gold coin to cell 4,24 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 96 gold coin to cell 4,25 +adding random object of rarity 1 + +got 2 possibilities. +adding 84 gold coin to cell 1,23 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 5 stone to cell 2,29 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 2,23 +adding random object of rarity 1 + +got 2 possibilities. +adding 52 gold coin to cell 4,31 +adding random object of rarity 1 + +got 2 possibilities. +adding 32 gold coin to cell 3,22 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 1,28 +--> Will add 7 objects to room 18 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 41 gold coin to cell 7,8 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 55 gold coin to cell 17,10 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 15,10 +adding random object of rarity 1 + +got 2 possibilities. +adding 2 stone to cell 15,13 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 87 gold coin to cell 17,7 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 9 stone to cell 14,6 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 46 gold coin to cell 8,12 +--> Will add 2 objects to room 19 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 28 gold coin to cell 23,12 +adding random object of rarity 1 + +got 2 possibilities. +adding 65 gold coin to cell 28,13 +--> Will add 7 objects to room 20 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 5 gold coin to cell 1,17 +adding random object of rarity 1 + +got 2 possibilities. +adding 57 gold coin to cell 3,16 +adding random object of rarity 1 + +got 2 possibilities. +adding 1 stone to cell 2,19 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 28 gold coin to cell 2,16 +adding random object of rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 32 gold coin to cell 4,14 +adding random object of rarity 1 + +got 2 possibilities. +adding 5 gold coin to cell 4,16 +adding random object of rarity 1 + +got 2 possibilities. +adding 51 gold coin to cell 3,15 +--> Will add 1 objects to room 21 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 56 gold coin to cell 43,21 +--> Will add 15 objects to room 22 (of 23) +adding random object of rarity 1 + +got 2 possibilities. +adding 44 gold coin to cell 22,3 +adding random object of rarity 1 + +got 2 possibilities. +adding 57 gold coin to cell 12,2 +adding random object of rarity 1 + +got 2 possibilities. +adding 86 gold coin to cell 18,3 +adding random object of rarity 3 + +no possible objects like this. trying again with rarity 2 + +no possible objects like this. trying again with rarity 1 + +got 2 possibilities. +adding 7 stone to cell 15,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 39 gold coin to cell 10,3 +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 18,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 60 gold coin to cell 14,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 39 gold coin to cell 20,2 +adding random object of rarity 1 + +got 2 possibilities. +adding 8 stone to cell 11,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 4 stone to cell 15,2 +adding random object of rarity 1 + +got 2 possibilities. +adding 93 gold coin to cell 17,3 +adding random object of rarity 1 + +got 2 possibilities. +adding 83 gold coin to cell 19,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 7 stone to cell 12,1 +adding random object of rarity 1 + +got 2 possibilities. +adding 9 stone to cell 23,2 +adding random object of rarity 1 + +got 2 possibilities. +adding 18 gold coin to cell 21,2 +Finished adding objects. +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat +AIMOVE: bat diff --git a/map.c b/map.c new file mode 100644 index 0000000..9f0cbbd --- /dev/null +++ b/map.c @@ -0,0 +1,1355 @@ +#include +#include +#include +#include +#include +#include "defs.h" +#include "nexus.h" +#include "map.h" +#include "objects.h" + +extern map_t *firstmap,*lastmap; + +cell_t *addcell(map_t *m, int x, int y) { + cell_t *cell; + m->cell[(y*m->w)+x] = malloc(sizeof(cell_t)); + cell = m->cell[(y*m->w)+x]; + cell->map = m; + cell->x = x; + cell->y = y; + setcelltype(cell, CT_WALL); + cell->visited = B_FALSE; + cell->obpile = addobpile(NOOWNER, cell); + cell->lf = NULL; + cell->roomid = -1; +} + +map_t *addmap(void) { + map_t *a; + int id; + int i; + // is there already a map? + if (lastmap) { + id = lastmap->id + 1; + } else { + id = 0; + } + + // add to the end of the list + if (firstmap == NULL) { + firstmap = malloc(sizeof(map_t)); + a = firstmap; + a->prev = NULL; + } else { + // go to end of list + a = lastmap; + a->next = malloc(sizeof(map_t)); + a->next->prev = a; + a = a->next; + } + lastmap = a; + a->next = NULL; + + // props + a->id = id; + a->lf = NULL; + a->nextlfid = 0; + for (i = 0; i < MAXDIR_ORTH; i++) { + a->nextmap[i] = -1; + } + return a; +} + +void addrandomthing(cell_t *c) { + char buf[BUFLEN]; + // TODO: monsters too + if (getrandomob(c->map, buf)) { + dblog("adding %s to cell %d,%d",buf,c->x,c->y); + addob(c->obpile, buf); + } +} + +cell_t *getcellat(map_t *map, int x, int y) { + if (!isonmap(map, x, y)) return NULL; + return map->cell[y*map->w + x]; +} + +int getcelldist(cell_t *src, cell_t *dst) { + double xd,yd; + // use pythag + xd = abs(dst->x - src->x); + yd = abs(dst->y - src->y); + return (int)sqrt(xd*xd + yd*yd); +} + +int calcroompos(map_t *map, int w, int h, int *bx, int *by) { + int x,y; + int bestx = -1, besty = -1; + int valid = B_FALSE; + int bestscore = 9999; + cell_t *cell; + + // try placing room at all positions + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + // would the room fit here? + if ( ((x + (w-1)) <= (map->w-1)) && + ((y + (h-1)) <= (map->h-1))) { + int score = 0; + int rx,ry; + int notpossible = B_FALSE; + valid = B_FALSE; + // calculate score based on cells in room + for (ry = y; (ry < y+h) && (!notpossible); ry++) { + for (rx = x; (rx < x+w) && (!notpossible); rx++) { + cell = getcellat(map, rx,ry); + // is this cell adjacent to an empty cell? + if (countcellexits(cell)) { + score++; + valid = B_TRUE; + } + // is this cell empty itself? + if (!cell->type->solid) score += 3; + // avoid being adjacent to other room walls + if (countcellexits(cell)) score++; + score += (countadjcellsoftype(cell, CT_ROOMWALL)*3); + // overlapping another room? + if (cell->type->id == CT_ROOM) { + valid = B_FALSE; + notpossible = B_TRUE; + } + if (cell->type->id == CT_ROOMWALL) { + valid = B_FALSE; + notpossible = B_TRUE; + } + } + } + if (valid && (score != 0) && (score < bestscore)) { + bestscore = score; + bestx = x; + besty = y; + } + } + } + } + + *bx = bestx; + *by = besty; + + if ((bestx == -1) || (besty == -1)) { + return B_TRUE; + } + + return B_FALSE; +} + +int countadjcellsoftype(cell_t *cell, int id) { + int d; + int count = 0; + cell_t *newcell; + for (d = D_N; d < MAXDIR_ORTH; d++) { + newcell = getcellindir(cell, d); + if (newcell && newcell->type->id == id) { + count++; + } + } + return count; +} + +int countcellexits(cell_t *cell) { + int d; + int exits = 0; + cell_t *newcell; + assert(cell); + for (d = D_N; d < MAXDIR_ORTH; d++) { + newcell = getcellindir(cell, d); + if (newcell && !newcell->type->solid) { + exits++; + } + } + return exits; +} + +/* + seed = random number seed + turnpct = percentage change of a corridor turning + sparseness = how many times to chop off dead ends + looppct = percentage change of turning dead-end into loop + maxrooms = max # of rooms +*/ +void createmap(map_t *map, int habitat) { + lifeform_t *lf; + char buf[BUFLEN]; + int wantrooms = B_TRUE; + int x,y,newx,newy; + int d; + int i; + int done,unused; + int dir; + int lastdir; + int numrooms = 0; + int roomw[MAXROOMS],roomh[MAXROOMS]; + //int roomspecial[MAX_MAPROOMS]; + int minroomw = MIN_ROOMW; + int minroomh = MIN_ROOMH; + int maxroomw = MAX_ROOMW; + int maxroomh = MAX_ROOMH; + int bestx,besty; + int w,h; + int startdir,forcex,forcey,ntries; + cell_t *cell; + //object_t *o; + + // parameters + int turnpct = DEF_TURNPCT; + int sparseness = DEF_SPARSENESS; + int looppct = DEF_LOOPPCT; + int minrooms = MINROOMS; + int maxrooms = MAXROOMS; + + int moved = 0; + + int db = B_TRUE; + + sprintf(buf, "Map %d",map->id); + map->name = strdup(buf); + map->habitat = habitat; + + for (i = 0; i < MAXDIR_ORTH; i++) { + map->nextmap[i] = -1; + } + + map->w = MAX_MAPW; + map->h = MAX_MAPH; + + // rememebr seed + map->seed = rand() % 65535; + + // fill entire maze with walls + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + addcell(map, x, y); + } + } + + // pick initial random spot + cell = getrandomcell(map); + setcelltype(cell, CT_CORRIDOR); + cell->visited = B_TRUE; + //if (db) printf("- Starting (%d,%d)\n",cell->x, cell->y); + + lastdir = D_UNKNOWN; + done = B_FALSE; + + dir = D_NONE; + + while (!done) { + // get random direction based on turnpct + dir = D_NONE; + while (dir == D_NONE) { + int badcount; + //if (db) printf("- At (%d,%d), moved %d, finding new direction...\n",cell->x, cell->y, moved); + + dir = getnewdigdir(cell, lastdir, (moved < 2) ? 0 : turnpct, &moved); + + badcount = 0; + while (dir == D_NONE) { + badcount++; + if (badcount > 10) { + // finish! + done = B_TRUE; + break; + } + + // pick new EMPTY random spot + cell = getrandomcell(map); + while (cell->type->solid) { + cell = getrandomcell(map); + } + //if (db) printf("--- Couldn't find a valid direction. Jumped to (%d,%d).\n",cell->x, cell->y); + // pick a new random dir + dir = getnewdigdir(cell, lastdir, turnpct, &moved); + } + if (!done) { + //if (db) printf("- Digging %s from (%d,%d).\n",getdirname(dir),cell->x, cell->y); + } + } + + if (!done) { + // move to adjacent cell in the given direction + cell = getcellindir(cell, dir); + //if (db) printf("- Now at (%d,%d)\n",cell->x, cell->y); + moved++; + + // blank it + setcelltype(cell,CT_CORRIDOR); + cell->visited = B_TRUE; + // mark surrounding cells as visited + for (d = DC_N; d < MAXDIR_COMPASS; d++) { + cell_t *thiscell; + thiscell = getcellindir(cell, d); + if (thiscell) { + //if (db) printf("* Marking surrounding cell in dir %d (%d,%d) as visited.\n",d, thiscell->x, thiscell->y); + thiscell->visited = B_TRUE; + } + } + // remember last direction + lastdir = dir; + + // check if we have visited all valid cells + unused = 0; + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + cell_t *thiscell; + thiscell = getcellat(map, x, y); + if (!thiscell->visited) { + unused++; + } + } + } + + if (!unused) { + done = B_TRUE; + } + } + + //printf("%d unused cell(s)\n",unused); + //dumpmap(map); + //getchar(); + + } + + // use sparseness to cut down dead ends + for (i = 0; i < sparseness; i++) { + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + cell = getcellat(map, x,y); + if (countcellexits(cell) == 1) { + // erase this cell + setcelltype(cell, CT_WALL); + } + } + } + } + + + + // introduce loops + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + cell = getcellat(map, x, y); + if (!cell->type->solid && countcellexits(cell) == 1) { + // dead end - maybe make loop from here + if (rnd(1,100) <= looppct) { + int connected = B_FALSE; + int loopok = B_TRUE; + int dir; + + // pick a random directory + dir = getnewdigdir(cell, D_UNKNOWN,100, &moved); + + if (dir == D_NONE) { + // can't make a loop from here. + loopok = B_FALSE; + } else { + int tries = 0; + // if we go this dir, will we hit a + // corridor? + while (!isloopdirok(cell, dir)) { + tries++; + // tried every direction? + if (tries >= MAXDIR_ORTH) { + loopok = B_FALSE; + break; + } + // turn... + if (++dir >= MAXDIR_ORTH) { + dir = 0; + } + } + } + + if (loopok) { + // keep digging until we hit another corridor + while (!connected) { + cell_t *newcell; + // find adjacent cell in the given direction + newcell = getcellindir(cell, dir); + if (!newcell) { + connected = B_TRUE; + } else { + // have we hit another corridor yet? + if (!newcell->type->solid) { + connected = B_TRUE; + } else { + // blank adjacent cell + setcelltype(newcell, CT_CORRIDOR); + newcell->visited = B_TRUE; + } + cell = newcell; + } + } + } + } + } + } + } + + + // create rooms + if (wantrooms) { + numrooms = rnd(minrooms, maxrooms); + + //printf("using %d rooms\n",numrooms); + //dblog("Adding %d rooms...\n",numrooms); + for (i = 0; i < numrooms; i++) { + // select random width/height + w = rnd(minroomw, maxroomw); + h = rnd(minroomh, maxroomh); + + roomw[i] = w; + roomh[i] = h; + + if (calcroompos(map, w, h, &bestx, &besty)) { + //printf("** couldn't make room!\n"); + } else { + // we now have the room position - fill it in + createroom(map, bestx,besty, w,h, i); + + /* + // maybe make it a special room + if (getrand(1,100) <= CH_SPECIALROOM) { + int curpos; + int roomid; + roomid = getrandomspecialroom(wreck->mazelev[curz].type); + for (y = besty; y <= (besty + (h-1)); y++) { + for (x = bestx; x <= (bestx + (w-1)); x++) { + curpos = y*MAZEW+x; + wreck->mazelev[curz].maze[curpos].floorver = roomid; + } + } + roomspecial[i] = roomid; + } else { + roomspecial[i] = B_FALSE; + } + */ + } + } + } + + + + /* void around map + // N + if (wreck->mazelev[0].type == W_WRECK) { + for (x = 0; x < MAZEW; x++) { + y = 0; + while (getfloor(x,y,curz) == C_SOLID) { + // change to void + wreck->mazelev[curz].maze[y*MAZEW+x].floor = C_VOID; + wreck->mazelev[curz].maze[y*MAZEW+x].floorver = rand() % MAXVOIDVER; + // make exits breakable or remove them. + for (d = D_NORTH ; d <= D_WEST; d++) { + int extype = getexit(x,y,curz,d); + int newx,newy; + newx = x + dirtox(d); + newy = y + dirtoy(d); + if (isonmap(newx, newy, curz)) { + if (issolid(newx,newy,curz)) { + // remove it + setexit(x,y,curz,d,EX_CORRIDOR); + } else { + object_t *o; + // make it vulnerable + if (isbreakableexit(extype)) { + setexit(x,y,curz,d,extype); + } + // add warning sign + o = addobject(&wreck->mazelev[curz].maze[newy*MAZEW+newx].floorobs, O_SIGNWARNING); + o->dir = diropposite(d); + } + } + } + y++; + } + } + // S + for (x = 0; x < MAZEW; x++) { + y = MAZEH-1; + while (getfloor(x,y,curz) == C_SOLID) { + // change to void + wreck->mazelev[curz].maze[y*MAZEW+x].floor = C_VOID; + wreck->mazelev[curz].maze[y*MAZEW+x].floorver = rand() % MAXVOIDVER; + // make exits breakable. + for (d = D_NORTH ; d <= D_WEST; d++) { + int extype = getexit(x,y,curz,d); + int newx,newy; + newx = x + dirtox(d); + newy = y + dirtoy(d); + if (isonmap(newx, newy, curz)) { + if (issolid(newx,newy,curz)) { + // remove it + setexit(x,y,curz,d,EX_CORRIDOR); + } else { + object_t *o; + // make it vulnerable + if (isbreakableexit(extype)) { + setexit(x,y,curz,d,extype); + } + // add warning sign + o = addobject(&wreck->mazelev[curz].maze[newy*MAZEW+newx].floorobs, O_SIGNWARNING); + o->dir = diropposite(d); + } + } + } + y--; + } + } + // E + for (y = 0; y < MAZEH; y++) { + x = 0; + while (getfloor(x,y,curz) == C_SOLID) { + // change to void + wreck->mazelev[curz].maze[y*MAZEW+x].floor = C_VOID; + wreck->mazelev[curz].maze[y*MAZEW+x].floorver = rand() % MAXVOIDVER; + // make exits breakable. + for (d = D_NORTH ; d <= D_WEST; d++) { + int extype = getexit(x,y,curz,d); + int newx,newy; + newx = x + dirtox(d); + newy = y + dirtoy(d); + if (isonmap(newx, newy, curz)) { + if (issolid(newx,newy,curz)) { + // remove it + setexit(x,y,curz,d,EX_CORRIDOR); + } else { + object_t *o; + // make it vulnerable + if (isbreakableexit(extype)) { + setexit(x,y,curz,d,extype); + } + // add warning sign + o = addobject(&wreck->mazelev[curz].maze[newy*MAZEW+newx].floorobs, O_SIGNWARNING); + o->dir = diropposite(d); + } + } + } + x++; + } + } + // W + for (y = 0; y < MAZEH; y++) { + x = MAZEW-1; + while (getfloor(x,y,curz) == C_SOLID) { + // change to void + wreck->mazelev[curz].maze[y*MAZEW+x].floor = C_VOID; + wreck->mazelev[curz].maze[y*MAZEW+x].floorver = rand() % MAXVOIDVER; + // make exits breakable. + for (d = D_NORTH ; d <= D_WEST; d++) { + int extype = getexit(x,y,curz,d); + int newx,newy; + newx = x + dirtox(d); + newy = y + dirtoy(d); + if (isonmap(newx, newy, curz)) { + if (issolid(newx,newy,curz)) { + // remove it + setexit(x,y,curz,d,EX_CORRIDOR); + } else { + object_t *o; + // make it vulnerable + if (isbreakableexit(extype)) { + setexit(x,y,curz,d,extype); + } + // add warning sign + o = addobject(&wreck->mazelev[curz].maze[newy*MAZEW+newx].floorobs, O_SIGNWARNING); + o->dir = diropposite(d); + } + } + } + x--; + } + } + } + */ + + /* + // add windows to map + for (y = 0; y < MAZEH; y++) { + for (x = 0; x < MAZEW; x++) { + int dir,chance = windowchance; + // increase chance for adjacent windows + for (dir = D_NORTH ; dir <= D_WEST ; dir++) { + int adjx,adjy; + adjx = x + dirtox(dir); + adjy = y + dirtoy(dir); + if (isonmap(adjx,adjy,curz)) { + // extra chance per adjacent window + chance += (hasexitoftype(adjx,adjy,curz,EX_WINDOW)*10); + } + } + for (dir = D_NORTH ; dir <= D_WEST ; dir++) { + int etype,newx,newy,thisfloor,newfloor; + etype = getexit(x,y,curz,dir); + newx = x + dirtox(dir); + newy = y + dirtoy(dir); + if (isonmap(newx,newy,curz)) { + thisfloor = wreck->mazelev[curz].maze[y*MAZEW+x].floor; + newfloor = wreck->mazelev[curz].maze[newy*MAZEW+newx].floor; + if ((etype == EX_WALL) && canhavewindow(thisfloor) && canhavewindow(newfloor)) { + if (getrand(1,100) <= chance) { + setexit(x,y,curz,dir,EX_WINDOW); + } + + } + } + } + } + } + */ + + // add objects and monsters to dead ends + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + cell_t *c; + int obchance; + c = getcellat(map, x, y); + if (c && !c->type->solid) { + if (rnd(1,100) <= getobchance(map->habitat)) { + addrandomthing(c); + } + } + } + } + + if (wantrooms && (numrooms > 0)) { + // add pillars & objects & monsters to rooms + for (i = 0; i < numrooms; i++) { + int numobsmin,numobsmax,numobs,n; + int x,y; + int maxpillars; + + //dblog("Adding obs to room %d/%d",i+1,numrooms); + maxpillars = (roomw[i] / 4) + (roomh[i] / 4); + // add pillars first + if ((maxpillars > 0) && (rnd(1,100) <= CH_PILLAR)) { + int n; + int numpillars; + numpillars = rnd(1,maxpillars); + dblog("--> Will add %d pillars",numpillars); + for (n = 0; n < numpillars;n++ ) { + //dblog("----> Adding pillar %d/%d",n+1,numpillars); + cell_t *c; + c = getrandomroomcell(map, i); + + if (c && !c->type->solid && !countobs(c->obpile)) { + setcelltype(cell, CT_WALL); + } + } + } + + /* + if (roomspecial[i]) { + // chance is increased + numobsmin = (roomw[i]*roomh[i]) / 4 ; + numobsmax = (roomw[i]*roomh[i]) / 2 ; + } else { + */ + numobsmin = 0; + numobsmax = MAXOF(roomw[i],roomh[i]); + + //} + + // then objects/monsters + if (numobsmax <= numobsmin) { + numobs = numobsmin; + } else { + numobs = rnd(numobsmin,numobsmax); + } + dblog("--> Will add %d objects to room %d (of %d)",numobs,i,numrooms); + for (n = 0 ; n < numobs; n++) { + int ntries = 0; + cell_t *c; + done = B_FALSE; + while (!done) { + c = getrandomroomcell(map, i); + // if nothing there + if (c && !countobs(c->obpile)) { + /* + if (roomspecial[i]) { + if (getrand(1,4) == 1) { // less chance of monster + addlifeform(x, y, curz,getrandommonster(curz), + getrand(1,curz+1), C_AI, B_FALSE, B_TRUE); + } else { + //add objects based on room type + if (roomspecial[i] == SP_MEDLAB) { + addspecialroomob(x, y, curz, SP_MEDLAB, + OR, 3, OP_AI_HEALS, OP_REVIVES); + } else if (roomspecial[i] == SP_COMPLAB) { + addspecialroomob(x, y, curz, SP_COMPLAB, + OR, 0); + } else if (roomspecial[i] == SP_ALIENNEST) { + addspecialroomob(x, y, curz, SP_ALIENNEST, + OR, 0); + } + } + } else { + */ + addrandomthing(c); + done = B_TRUE; + //dblog("----> Success ob at (%d,%d).",c->x,c->y); + } else { + ntries++; + //dblog("----> Failed, now at retry #%d/%d",ntries, numobs); + } + + if (ntries >= numobs) { + break; + } + } + } + + } + } + + dblog("Finished adding objects."); + + /* + // some extra monsters in corridors + for (i = 0; i < getrand(3,8); i++) { + int x,y; + lifeform_t *lf; + lf = addlifeform(rand() % MAZEW, rand() % MAZEH,curz,getrandommonster(curz), getrand(1,curz+1), C_AI, B_FALSE, B_TRUE); + done = B_FALSE; + while (!done) { + getrandomcell(&x, &y, curz,C_EMPTY ); + if (isempty(x,y,curz)) { + lf->x = x; + lf->y = y; + lf->z = curz; + done = B_TRUE; + } + } + } + */ + + + /* + + // add a fixed revival booth + done = B_FALSE; + while (!done) { + getrandomcell(&x,&y, curz,C_ROOM); // get a random cell in a room + if (isempty(x,y,curz)) { + if (!cellhasobject(x,y,curz)) { + done = B_TRUE; + } + } + } + addobject(&wreck->mazelev[curz].maze[y*MAZEW+x].floorobs, O_REVIVAL); + + // emergency lighting + for (y = 0; y < MAZEH; y++) { + for (x = 0; x < MAZEW; x++) { + if (getfloor(x,y,curz) == C_EMPTY) { + if (isempty(x,y,curz) && !hasdangerousobject(NULL,x,y,curz)) { + // chance of a light... + int lightchance = 5; + int roll = getrand(1,100); + if (roll <= lightchance) { + addobject(&wreck->mazelev[curz].maze[y*MAZEW+x].floorobs, O_EMERGLIGHT); + } + } + } + } + } + + // add the reactor + done = B_FALSE; + while (!done) { + getrandomcell(&x,&y, curz,C_ROOM); // get a random cell in a room + if (getdistance(x,y,wreck->dockpos.x,wreck->dockpos.y) >= 20) { + if (isempty(x,y,curz)) { + if (!cellhasobject(x,y,curz)) { + done = B_TRUE; + } + } + } + } + wreck->mazelev[curz].reactorpos.x = x; + wreck->mazelev[curz].reactorpos.y = y; + addobject(&wreck->mazelev[curz].maze[y*MAZEW+x].floorobs, O_REACTOR); + wreck->mazelev[curz].powered = B_TRUE; + wreck->mazelev[curz].lightlevel = getrand(50,100); + +//printf("reactor at %d,%d,%d\n",x,y,curz); + + // all empty cells around it change + for (cury = y-2 ; cury <= y+2; cury++) { + for (curx = x-2 ; curx <= x+2; curx++) { + if (isonmap(curx,cury,curz)) { + if (!issolid(curx,cury,curz)) { + wreck->mazelev[curz].maze[cury*MAZEW+curx].floor = C_EMPTYHAZARD; + } + } + } + } + + + // calculate level difficulty + wreck->mazelev[curz].mondifficulty = 0; + wreck->mazelev[curz].mapdifficulty = 0; + + // monsters + for (lf = wreck->mazelev[curz].lifeform ; lf ; lf = lf->next) { + if (lf->controller != C_HUMAN) { + wreck->mazelev[curz].mondifficulty += (getrdifficulty(lf->race)); + } + } + // maze layout & objects + for (y = 0; y < MAZEH; y++) { + for (x = 0; x < MAZEW; x++) { + switch (numexits(x,y,curz)) { + case 2: // corner + wreck->mazelev[curz].mapdifficulty += 0.5; + break; + case 1: // dead end + wreck->mazelev[curz].mapdifficulty += 1; + break; + } + + if (containerhasobject(&wreck->mazelev[curz].maze[y*MAZEW+x].floorobs, O_SPAWNPIT)) { + wreck->mazelev[curz].mondifficulty += DF_SPAWNPIT; + } + } + } + + // fix exithp + for (y = 0; y < MAZEH; y++) { + for (x = 0; x < MAZEW; x++) { + int d; + for (d = D_NORTH ; d <= D_WEST; d++) { + int extype = getexit(x,y,curz,d); + if (isbreakableexit(getexit(x,y,curz,d))) { + // using setexit will make sure the hp is right + setexit(x,y,curz,d,extype); + } + } + } + } + */ + + //printf("*** Level difficulty is %0.2f\n", getmazedifficulty(curz)); + + +} + +void createroom(map_t *map, int minx, int miny, int w, int h, int roomid) { + int x,y; + int poss[MAXOF(MAX_MAPW,MAX_MAPH)]; + int npossible; + cell_t *cell, *newcell; + int maxx,maxy; + +//printf("trying to create room at (%d,%d) w=%d,h=%d\n", minx, miny, w, h); + + maxx = minx + (w-1); + maxy = miny + (h-1); + + for (y = miny; y <= maxy; y++) { + for (x = minx; x <= maxx; x++) { + cell = getcellat(map, x, y); + + // make it a border or room + if ((y == miny) || (y == maxy) || + (x == minx) || (x == maxx)) { + setcelltype(cell, CT_ROOMWALL); + } else { + setcelltype(cell, CT_ROOM); + } + cell->roomid = roomid; + } + } + + + // for each side, make list of all possible door locations + // then pick one randomly. + // BUT if the nearby corridor only has one exit, always + // place a door. + + // N + npossible = 0; + y = miny; + for (x = minx; x <= maxx; x++) { + cell = getcellat(map, x, y); + newcell = getcellindir(cell, D_N); + if (newcell && !newcell->type->solid) { + int doorcount; + + doorcount = countadjcellsoftype(cell, CT_DOOROPEN) + + countadjcellsoftype(cell, CT_DOORCLOSED); + + if (doorcount == 0) { + if ((countcellexits(newcell) == 1) && + (iswallindir(newcell,D_E)) && + (iswallindir(newcell,D_W))) { // always add door + makedoor(cell); + } else { + poss[npossible] = x; + npossible++; + } + } + } + } + + if (npossible > 0) { + int sel = rand() % npossible; + //printf("adding N door at %d\n",poss[sel]); + cell = getcellat(map, poss[sel], y); + makedoor(cell); + } + + + // S + npossible = 0; + y = maxy; + for (x = minx; x <= maxx; x++) { + cell = getcellat(map, x, y); + newcell = getcellindir(cell, D_S); + if (newcell && !newcell->type->solid) { + int doorcount; + + doorcount = countadjcellsoftype(cell, CT_DOOROPEN) + + countadjcellsoftype(cell, CT_DOORCLOSED); + + if (doorcount == 0) { + if ((countcellexits(newcell) == 1) && + (iswallindir(newcell,D_E)) && + (iswallindir(newcell,D_W))) { // always add door + makedoor(cell); + } else { + poss[npossible] = x; + npossible++; + } + } + } + } + + if (npossible > 0) { + int sel = rand() % npossible; + //printf("adding S door at %d\n",poss[sel]); + cell = getcellat(map, poss[sel], y); + makedoor(cell); + } + + + // W + npossible = 0; + x = minx; + for (y = miny; y <= maxy; y++) { + cell = getcellat(map, x, y); + newcell = getcellindir(cell, D_W); + if (newcell && !newcell->type->solid) { + int doorcount; + doorcount = countadjcellsoftype(cell, CT_DOOROPEN) + + countadjcellsoftype(cell, CT_DOORCLOSED); + if (doorcount == 0) { + if ((countcellexits(newcell) == 1) && + (iswallindir(newcell,D_N)) && + (iswallindir(newcell,D_S))) { // always add door + makedoor(cell); + } else { + poss[npossible] = y; + npossible++; + } + } + } + } + + if (npossible > 0) { + int sel = rand() % npossible; + //printf("adding W door at %d\n",poss[sel]); + cell = getcellat(map, x, poss[sel]); + makedoor(cell); + } + + + // E + npossible = 0; + x = maxx; + for (y = miny; y <= maxy; y++) { + cell = getcellat(map, x, y); + newcell = getcellindir(cell, D_E); + if (newcell && !newcell->type->solid) { + int doorcount; + doorcount = countadjcellsoftype(cell, CT_DOOROPEN) + + countadjcellsoftype(cell, CT_DOORCLOSED); + if (doorcount == 0) { + if ((countcellexits(newcell) == 1) && + (iswallindir(newcell,D_N)) && + (iswallindir(newcell,D_S))) { // always add door + makedoor(cell); + } else { + poss[npossible] = y; + npossible++; + } + } + } + } + + if (npossible > 0) { + int sel = rand() % npossible; + //printf("adding E door at %d\n",poss[sel]); + cell = getcellat(map, x, poss[sel]); + makedoor(cell); + } + +} + +int dirtox(int dt, int dir) { + if (dt == DT_ORTH) { + switch (dir) { + case D_E: + return 1; + case D_W: + return -1; + default: + return 0; + } + } else if (dt == DT_COMPASS) { + switch (dir) { + case DC_NE: + case DC_E: + case DC_SE: + return 1; + case DC_NW: + case DC_W: + case DC_SW: + return -1; + default: + return 0; + } + } + return 0; +} + +int dirtoy(int dt, int dir) { + if (dt == DT_ORTH) { + switch (dir) { + case D_S: + return 1; + case D_N: + return -1; + default: + return 0; + } + } else if (dt == DT_COMPASS) { + switch (dir) { + case DC_SW: + case DC_S: + case DC_SE: + return 1; + case DC_NE: + case DC_N: + case DC_NW: + return -1; + default: + return 0; + } + } +} + + +void dumpmap(map_t *map) { + int x,y; + cell_t *cell; +printf("dump of map '%s' (%d x %d):\n",map->name, map->w, map->h); + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + cell = getcellat(map, x, y); + printf("%c",cell->type->glyph); + } + printf("\n"); + } +} + +map_t *findmap(int mid) { + map_t *m; + for (m = firstmap ; m ; m = m->next) { + if (m->id == mid) return m; + } + return NULL; +} + +cell_t *getcellindir(cell_t *cell, int dir) { + cell_t *newcell; + int newx,newy; + int dt; + switch (dir) { + case D_N: + case D_S: + case D_E: + case D_W: + dt = DT_ORTH; + break; + case DC_N: + case DC_E: + case DC_S: + case DC_W: + case DC_NE: + case DC_SE: + case DC_SW: + case DC_NW: + dt = DT_COMPASS; + break; + } + newx = cell->x + dirtox(dt, dir); + newy = cell->y + dirtoy(dt, dir); + newcell = getcellat(cell->map, newx,newy); + + return newcell; +} + +// select a new direction (random chance of turnung) +int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved) { + int foundvaliddir = B_FALSE; + int dir; + int tried[4], numtries; + int i; + int turned = B_FALSE; + cell_t *newcell; + int db = B_FALSE; + char err[BUFLEN]; + + // haven't tried any dirs yet + numtries = 0; + for (i = 0; i < MAXDIR_ORTH; i++) { + tried[i] = B_FALSE; + } + + while (!foundvaliddir) { // keep going until we get a valid direction + int newx,newy; + + if (numtries >= MAXDIR_ORTH) { // no valid dirs + return D_NONE; // (pick a new random spot and refresh tried dirs and current dir) + } + + if (lastdir == D_UNKNOWN) { + // just pick a random dir + dir = rnd(0, MAXDIR_ORTH-1); + turned = B_TRUE; + } else { + // chance of changing dir + if (rnd(1,100) <= turnpct) { + dir = rnd(0, MAXDIR_ORTH-1); + turned = B_TRUE; + } else { + dir = lastdir; + } + } + + // now validate the direction + if (db) printf("--- Trying %s...\n",getdirname(dir)); + if (tried[dir] == B_TRUE) { // already found this dir to be invalid + lastdir = D_UNKNOWN; + if (db) printf("--- Already know %s is invalid.\n",getdirname(dir)); + } else { + // check 1 cell ahead + newcell = getcellindir(cell, dir); + if (isnewcellok(newcell, err)) { + cell_t *newcell1, *newcell2; + // check 2 cells ahead and sidewars + newcell = getcellindir(newcell, dir); + if (newcell) { + switch (dir) { + case D_N: + case D_S: + newcell1 = getcellindir(newcell,D_E); + newcell2 = getcellindir(newcell,D_W); + break; + case D_E: + case D_W: + newcell1 = getcellindir(newcell,D_N); + newcell2 = getcellindir(newcell,D_S); + break; + } + } else { + newcell1 = NULL; + newcell2 = NULL; + } + + if (!isnewcellok(newcell, err)) { + if (db) printf("--- %s %s!\n",getdirname(dir), err); + tried[dir] = B_TRUE; + lastdir = D_UNKNOWN; + numtries++; + } else if (!isnewcellok(newcell1, err)) { + if (db) printf("--- %s %s!\n",getdirname(dir), err); + tried[dir] = B_TRUE; + lastdir = D_UNKNOWN; + numtries++; + } else if (!isnewcellok(newcell2, err)) { + if (db) printf("--- %s %s!\n",getdirname(dir), err); + tried[dir] = B_TRUE; + lastdir = D_UNKNOWN; + numtries++; + } else { // ok + if (db) printf("--- %s %s!\n",getdirname(dir), err); + foundvaliddir = B_TRUE; + } + } else { + if (db) printf("--- %s %s!\n",getdirname(dir), err); + tried[dir] = B_TRUE; + lastdir = D_UNKNOWN; + numtries++; + } + } + } + //newcell = getcellindir(cell, dir); + //printf("getrandomdigdir() - on cell %d,%d, returning dir %d (-> %d,%d)\n", + // cell->x, cell->y, dir, newcell->x, newcell->y); + if (turned) *moved = 0; + return dir; +} + +// chance of each empty cell in a map has of getting an object +int getobchance(int habitat) { + switch (habitat) { + case H_DUNGEON: + return 3; + } + // default of no objects + return 0; +} + +cell_t *getrandomcell(map_t *map) { + int x,y; + cell_t *cell; + x = (rand() % map->w); + y = (rand() % map->h); + cell = getcellat(map, x, y); + assert(cell); + return cell; +} + +cell_t *getrandomcelloftype(map_t *map, int id) { + cell_t *cell; + cell = getrandomcell(map); + while (cell->type->id != id) { + cell = getrandomcell(map); + } + return cell; +} + + +int getrandomdir(int dirtype) { + if (dirtype == DT_ORTH) { + return rnd(D_N, D_W); + } else { // ie. DT_COMPASS + return rnd(DC_N, DC_NW); + } + +} + +cell_t *getrandomroomcell(map_t *map, int roomid) { + int npossible = 0; + int selidx; + int x,y; + cell_t *c, **poss; + + poss = malloc((map->w*map->h) * sizeof(cell_t)); + + npossible = 0; + // avoid room borders! + for (y = 1; y < (map->h-1); y++) { + for (x = 1; x < (map->w-1); x++) { + c = getcellat(map, x, y); + // is this cell in the correct room? + if (c && c->roomid == roomid) { + poss[npossible] = c; + npossible++; + } + } + } + if (npossible <= 0) { + return NULL; + } + + selidx = rnd(0, npossible-1); + c = poss[selidx]; + free(poss); + return c; +} + +int isloopdirok(cell_t *cell, int dir) { + int dirok = B_FALSE; + cell_t *newcell; + // is there a corridor in this direction? + newcell = getcellindir(cell, dir); + while (newcell) { + // got a corridor? + if (!newcell->type->solid) { + dirok = B_TRUE; + break; + } + // keep going + newcell = getcellindir(newcell, dir); + } + + // we've either gone off the map or + // hit a corridor + return dirok; +} + +int isnewcellok(cell_t *cell, char *err) { + if ( !cell) { // can't go that way + if (err) sprintf(err,"goes off the map."); + return B_FALSE; + } else if ( !cell->type->solid) { // already an empty space there + if (err) sprintf(err,"goes to an empty space (%d,%d)",cell->x,cell->y); + return B_FALSE; + } + + // ok! + if (err) sprintf(err, "OK!"); + return B_TRUE; +} + +int isonmap(map_t *map, int x, int y) { + if ((x < 0) || (y < 0)) { + return B_FALSE; + } + if ((x >= map->w) || (y >= map->h)) { + return B_FALSE; + } + + return B_TRUE; +} + +int iswallindir(cell_t *cell, int dir) { + cell_t *newcell; + newcell = getcellindir(cell, dir); + if (!newcell) { + return B_TRUE; + } + + if (newcell->type->solid) { + return B_TRUE; + } + + return B_FALSE; +} + +void makedoor(cell_t *cell) { + setcelltype(cell, CT_DOORCLOSED); +} + +void setcelltype(cell_t *cell, int id) { + int i; + + assert(cell); + cell->type = findcelltype(id); + assert(cell->type); + cell->roomid = 0; +} + + diff --git a/map.h b/map.h new file mode 100644 index 0000000..acd4b06 --- /dev/null +++ b/map.h @@ -0,0 +1,29 @@ +#include "defs.h" + +cell_t *addcell(map_t *map, int x, int y); +map_t *addmap(void); +void addrandomthing(cell_t *c); +cell_t *getcellat(map_t *map, int x, int y); +int getcelldist(cell_t *src, cell_t *dst); +int calcroompos(map_t *map, int w, int h, int *bx, int *by); +int countadjcellsoftype(cell_t *cell, int id); +int countcellexits(cell_t *cell); +void createmap(map_t *map, int habitat); +void createroom(map_t *map, int minx, int miny, int w, int h, int roomid); +int dirtox(int dt, int dir); +int dirtoy(int dt, int dir); +void dumpmap(map_t *map); +map_t *findmap(int mid); +cell_t *getcellindir(cell_t *cell, int dir); +int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved); +int getobchance(int habitat); +cell_t *getrandomcell(map_t *map); +cell_t *getrandomcelloftype(map_t *map, int id); +int getrandomdir(int dirtype); +cell_t *getrandomroomcell(map_t *map, int roomid); +int isloopdirok(cell_t *cell, int dir); +int isnewcellok(cell_t *cell, char *err); +int isonmap(map_t *map, int x, int y); +int iswallindir(cell_t *cell, int dir); +void makedoor(cell_t *cell); +void setcelltype(cell_t *cell, int id); diff --git a/mod_ob.txt b/mod_ob.txt new file mode 100644 index 0000000..9d8d5a1 --- /dev/null +++ b/mod_ob.txt @@ -0,0 +1,5 @@ +When modifying the object_t structure: + +- update obpropsmatch() +- update isplainob() +- update addobject() diff --git a/move.c b/move.c new file mode 100644 index 0000000..8a3c141 --- /dev/null +++ b/move.c @@ -0,0 +1,146 @@ +#include +#include "attack.h" +#include "defs.h" +#include "map.h" + +extern lifeform_t *player; + +int canmove(lifeform_t *lf, int dir, enum ERROR *error) { + cell_t *cell; + + cell = getcellindir(lf->cell, dir); + + if (cell->type->solid) { + if (error) *error = E_WALLINWAY; + return B_FALSE; + } + if (cell->lf) { + if (error) *error = E_LFINWAY; + return B_FALSE; + } + + return B_TRUE; +} + +void dorandommove(lifeform_t *lf) { + int dir; + int tries = 0; + dir = getrandomdir(DT_COMPASS); + while (trymove(lf, dir)) { + if (++dir > DC_NW) dir = DC_N; + if (++tries >= MAXDIR_COMPASS) { + dowait(lf); + return; + } + } +} + +int dowait(lifeform_t *lf) { + taketime(lf, SPEED_WAIT); + return B_FALSE; +} + +void movelf(lifeform_t *lf, cell_t *newcell) { + // update current cell + lf->cell->lf = NULL; + + // update lifeform + lf->cell = newcell; + + // update new cell + newcell->lf = lf; + +} + +// basically this is a warpper for 'movelf' which +// does other game things like telling the player +// what is here. +int moveto(lifeform_t *lf, cell_t *newcell) { + + // actually do the move + movelf(lf, newcell); + + // tell player about things + if (lf->controller == C_PLAYER) { + int numobs; + char buf[BUFLEN]; + numobs = countobs(newcell->obpile); + if (numobs == 1) { + getobname(newcell->obpile->first, buf, newcell->obpile->first->amt); + msg("You see %s here.", buf); + } else if ((numobs > 1) && (numobs <= 3)) { + msg("You see a few objects here."); + } else if ((numobs > 3) && (numobs <= 6)) { + msg("You see some objects here."); + } else if (numobs > 6) { + msg("You see many objects here."); + } else { + // just clear the message buffer + clearmsg(); + } + } +} + +int opendoor(lifeform_t *lf, cell_t *cell) { + char buf[BUFLEN]; + if (cell->type->id == CT_DOORCLOSED) { + // open it + setcelltype(cell, CT_DOOROPEN); + if (lf->controller == C_PLAYER) { + msg("You open a door."); + } else { + // TODO: only announce if player can see it + if (haslos(player, cell)) { + getlfname(lf, buf); + capitalise(buf); + msg("%s opens a door.",buf); + } + } + taketime(lf, getmovespeed(lf)); + } else { + return B_TRUE; + } + + return B_FALSE; +} + +int tryrun(lifeform_t *lf, int dir) { + // continue moving until we fail + while (canmove(lf, dir, NULL)) { + trymove(lf,dir); + } +} + +int trymove(lifeform_t *lf, int dir) { + cell_t *cell; + enum ERROR errcode; + char buf[BUFLEN], buf2[BUFLEN]; + + cell = getcellindir(lf->cell, dir); + if (canmove(lf, dir, &errcode)) { + moveto(lf, cell); + taketime(lf, getmovespeed(lf)); + } else { + switch (errcode) { + case E_WALLINWAY: + // is it a door in the way? + if (cell->type->id == CT_DOORCLOSED) { + // try to open it + opendoor(lf, cell); + } else { + if (lf->controller == C_PLAYER) { + msg("Ouch! You walk into a wall."); + } + } + break; + case E_LFINWAY: + // attack! + doattack(lf, cell->lf); + break; + } + return B_TRUE; + } + return B_FALSE; +} + + diff --git a/move.h b/move.h new file mode 100644 index 0000000..10609d1 --- /dev/null +++ b/move.h @@ -0,0 +1,8 @@ +#include "defs.h" + +int canmove(lifeform_t *lf, int dir, enum ERROR *error); +void dorandommove(lifeform_t *lf); +int dowait(lifeform_t *lf); +void movelf(lifeform_t *lf, cell_t *newcell); +int moveto(lifeform_t *lf, cell_t *newcell); +int trymove(lifeform_t *lf, int dir); diff --git a/nexus.c b/nexus.c new file mode 100644 index 0000000..7e79212 --- /dev/null +++ b/nexus.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include "io.h" +#include "lf.h" +#include "map.h" +#include "nexus.h" +#include "objects.h" +#include "save.h" + +material_t *material = NULL,*lastmaterial = NULL; +objectclass_t *objectclass = NULL,*lastobjectclass = NULL; +objecttype_t *objecttype = NULL,*lastobjecttype = NULL; +celltype_t *firstcelltype = NULL,*lastcelltype = NULL; +race_t *firstrace = NULL,*lastrace = NULL; +map_t *firstmap = NULL,*lastmap = NULL; + +FILE *logfile; + +enum ERROR reason; // global for returning errors + +lifeform_t *player = NULL; +int gameover; + +int gamestarted = B_FALSE; + +int main(char **argv, int argc) { + int newworld = B_FALSE; + + atexit(cleanup); + + // init params + init(); + + // load whatever maps are available + loadall(); + + // init graphics + initgfx(); + + // if no maps, make the initial level + if (!firstmap) { + newworld = B_TRUE; + addmap(); + createmap(firstmap, H_DUNGEON); + } + + // if no player, add them + if (!player) { + msg("Welcome to %snexus!", newworld ? "the new " : ""); + more(); + player = addlf(getrandomcelloftype(firstmap, CT_ROOM), R_HUMAN); + player->controller = C_PLAYER; + outfitlf(player); + addlf(getcellindir(player->cell, D_N), R_BAT); + } else { + msg("Welcome back!"); + more(); + } + + + // start game - this will cause debug messages to now + // go to the log file instead of stdout. + gamestarted = B_TRUE; + + + // show level + drawscreen(); + + // MAIN LOOP + gameover = B_FALSE; + while (!gameover) { + + // someone has a turn + donextturn(player->cell->map); + // update lifeform structue to figure out who goes next + sortlf(player->cell->map); + + // TODO: monsters move etc + + // check for death etc + doeffects(); + + // show level + drawscreen(); + + // check end of game + checkendgame(); + + } + + // print tombstone + tombstone(player); +} + +celltype_t *addcelltype(int id, char glyph, int solid, int transparent) { + 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->glyph = glyph; + a->solid = solid; + a->transparent = transparent; +} + +void checkendgame(void) { + if (!player->alive) { + gameover = B_TRUE; + } +} + +void cleanup(void) { + fclose(logfile); + cleanupgfx(); +} + + +void doeffects(void) { + lifeform_t *lf; + // check for death + for (lf = player->cell->map->lf; lf ; lf = lf->next) { + if (lf->hp <= 0) { + // die! + die(lf); + } + } + +} + +void donextturn(map_t *map) { + lifeform_t *who; + + // show messages to plaeyr + drawmsg(); + + who = map->lf; + if (who->controller == C_PLAYER) { + drawcursor(); + // find out what player wants to do + handleinput(); + } else { + // TODO: do ai move + aimove(who); + } + + // everyone else's time goes down by 1 + for (who = map->lf->next ; who ; who = who->next ){ + if (who->timespent > 0) who->timespent--; + } +} + +celltype_t *findcelltype(int id) { + celltype_t *ct; + for (ct = firstcelltype; ct ; ct = ct->next) { + if (ct->id == id) { + return ct; + } + } + + return NULL; +} + +char *getdirname(int dir) { + switch (dir) { + case D_N: + return "North"; + case D_E: + return "East"; + case D_S: + return "South"; + case D_W: + return "West"; + case D_UNKNOWN: + return "D_UNKNOWN"; + case D_NONE: + return "D_NONE"; + } + return "?errordir?"; +} + +int init(void) { + // random numbers + srand(time(NULL)); + + // open log file + logfile = fopen("log.txt","wt"); + fprintf(logfile, "\n\n\n====== NEW LOGFILE ====\n"); + + // cell types + addcelltype(CT_WALL, '#', B_SOLID, B_OPAQUE); + addcelltype(CT_ROOMWALL, '#', B_SOLID, B_OPAQUE); + addcelltype(CT_CORRIDOR, '.', B_EMPTY, B_TRANS); + addcelltype(CT_LOOPCORRIDOR, 'L', B_EMPTY, B_TRANS); + addcelltype(CT_ROOM, '.', B_EMPTY, B_TRANS); + addcelltype(CT_DOOROPEN, '-', B_EMPTY, B_TRANS); + addcelltype(CT_DOORCLOSED, '+', B_SOLID, B_OPAQUE); + + initobjects(); + + initrace(); + + return B_FALSE; +} + +int isplayerturn(void) { + if (player->cell->map->lf->controller == C_PLAYER) { + return B_TRUE; + } + return B_FALSE; +} + +// get a random number +int rnd(int min, int max) { + int res; + res = (rand() % (max - min + 1)) + min; + return res; +} + +// sort map's lifeform list by time spent +void sortlf(map_t *map) { + int donesomething; + lifeform_t *l; + int adjustby; + int db = B_FALSE; + + // bubblesort + if (db) { + dblog("BEFORE sortlf():"); + for (l = map->lf ; l ; l = l->next) { + dblog("- %s (timespent= %d) (sorted=%d)", (l == player) ? "player" : l->race->name, l->timespent,l->sorted); + } + } + + donesomething = B_TRUE; + while (donesomething) { + donesomething = B_FALSE; + for (l = map->lf ; l ; l = l->next) { + if (!l->sorted && l->next && (l->timespent > l->next->timespent) ) { + lifeform_t *temp; + // remember next element + temp = l->next; + + // remove this element from list + if (l->prev == NULL) { + // first + map->lf = l->next; + } else { + // not first + l->prev->next = l->next; + } + // don't bother checking for next - we know ther eis one. + l->next->prev = l->prev; + + // re-add element afterwards + l->next = temp->next; + l->prev = temp; + temp->next = l; + if (l->next == NULL) map->lastlf = l; + + donesomething = B_TRUE; + } else { + l->sorted = B_TRUE; + } + } + } + + if (db) { + dblog("AFTER SORT:"); + for (l = map->lf ; l ; l = l->next) { + dblog("- %s (timespent= %d) (sorted=%d)", (l == player) ? "player" : l->race->name, l->timespent,l->sorted); + } + } + + // now go through the list and make the first element be 0 + adjustby = map->lf->timespent; + for (l = map->lf ; l ; l = l->next) { + l->timespent -= adjustby; + l->sorted = B_FALSE; + } + +} diff --git a/nexus.h b/nexus.h new file mode 100644 index 0000000..0642ec6 --- /dev/null +++ b/nexus.h @@ -0,0 +1,13 @@ +#include "defs.h" + +celltype_t *addcelltype(int id, char glyph, int solid, int transparent); +void checkendgame(void); +void cleanup(void); +void doeffects(void); +void donextturn(map_t *map); +celltype_t *findcelltype(int id); +char *getdirname(int dir); +int init(void); +int isplayerturn(void); +int rnd(int min, int max); +void sortlf(map_t *map); diff --git a/object.h b/object.h new file mode 100644 index 0000000..e69de29 diff --git a/objects.c b/objects.c new file mode 100644 index 0000000..afe47ad --- /dev/null +++ b/objects.c @@ -0,0 +1,940 @@ +#include +#include +#include +#include +#include "defs.h" +#include "flag.h" +#include "objects.h" +#include "text.h" + +extern objecttype_t *objecttype,*lastobjecttype; +extern objectclass_t *objectclass,*lastobjectclass; +extern material_t *material,*lastmaterial; + +extern lifeform_t *player; + +extern int reason; + +char letorder[MAXPILEOBS] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' +}; + +objecttype_t *lastot = NULL; + +enum OBCLASS sortorder[] = { + OC_MONEY, + OC_WEAPON, + OC_ARMOUR, + OC_SCROLL, + OC_POTION, + OC_FOOD, + OC_ROCK, + OC_MISC, + OC_NULL +}; + +long nextoid = 0; + + +material_t *addmaterial(enum MATERIAL id, char *name) { + material_t *a; + + // add to the end of the list + if (material == NULL) { + material = malloc(sizeof(material_t)); + a = material; + a->prev = NULL; + } else { + // go to end of list + a = lastmaterial; + a->next = malloc(sizeof(material_t)); + a->next->prev = a; + a = a->next; + } + lastmaterial = a; + a->next = NULL; + + // props + a->id = id; + a->name = strdup(name); +} + +objectclass_t *addoc(enum OBCLASS id, char *name, char glyph) { + objectclass_t *a; + + // add to the end of the list + if (objectclass == NULL) { + objectclass = malloc(sizeof(objectclass_t)); + a = objectclass; + a->prev = NULL; + } else { + // go to end of list + a = lastobjectclass; + a->next = malloc(sizeof(objectclass_t)); + a->next->prev = a; + a = a->next; + } + lastobjectclass = a; + a->next = NULL; + + // props + a->id = id; + a->name = strdup(name); + a->glyph = glyph; +} + + + +// create a new obkejct, stacking ok +object_t *addob(obpile_t *where, char *name) { + addobject(where, name, B_TRUE); +} + +// create a new object +// if canstack is true, we are allwoed +// to join similar objects together instead of +// creating new obejct entries. +object_t *addobject(obpile_t *where, char *name, int canstack) { + objecttype_t *ot; + object_t *o; + char *p,*nsp; + char numstring[BUFLEN]; + int howmany = 1; + int i; + int db = B_FALSE; + flag_t *f; + + // parse name string + nsp = numstring; + for (p = name ; isdigit(*p) ; p++) { + *nsp = *p; + nsp++; + } + *nsp = '\0'; + + + // are we giving multiple objects? + if (strlen(numstring) > 0) { + // first increment name string pointer + // past any spaces + while (!isalpha(*p)) p++; + // now figure out how many + howmany = atoi(numstring); + } else { + howmany = 1; + } + + + // make sure we can find the requested object type + ot = findotn(p); + if (!ot) { + if (db) dblog("DB: No match for object name '%s'", p ); + return NULL; + } + if (db) dblog("DB: '%s' -> adding %d x %s",name, howmany, ot->name); + + for (i = 0; i < howmany; i++) { + int added = B_FALSE; + if (canstack) { + object_t *existob; + if (db) dblog("DB: Looking for stacks..."); + // TODO: if object is stackable + // TODO: if (hasflag(ob,stackable) && hasobject(where, ot)) { + // does the pile already contain one? + existob = canstacknewot(where, ot); + if (existob) { + if (db) dblog("DB: STACK FOUND. Adding %d obs to existing stack.",howmany); + existob->amt++; + added = B_TRUE; + } + if (db) dblog("DB: No stacks found."); + } + + if (!added) { + if (db) dblog("DB: Creating new object."); + + // otherwise add to list + if (where->first == NULL) { + where->first = malloc(sizeof(object_t)); + o = where->first; + o->prev = NULL; + } else { + // go to end of list + o = where->last; + o->next = malloc(sizeof(object_t)); + o->next->prev = o; + o = o->next; + } + where->last = o; + + o->next = NULL; + + // fill in props + o->id = nextoid++; // increment next ob id + o->type = ot; + o->pile = where; + // inherit from objecttype + o->material = ot->material; + o->weight = ot->weight; + o->flags = addflagpile(); + for (f = ot->flags->first ; f ; f = f->next) { + addflag(o->flags, f->id, f->val[0], f->val[1], f->val[2], f->text); + } + + // non-inherited: + o->inscription = NULL; + // if adding to a player... + if (where->owner) { + o->letter = getnextletter(where, NULL); + } else { + // new object - has no letter yet. + o->letter = ' '; + } + o->amt = 1; + o->blessed = B_UNCURSED; + o->blessknown = B_FALSE; // TODO: change to false + } + } + + // return the last object given + return o; +} + +obpile_t *addobpile(lifeform_t *owner, cell_t *where) { + obpile_t *op; + op = malloc(sizeof(obpile_t)); + op->first = NULL; + op->last = NULL; + op->owner = owner; + op->where = where; + + return op; +} + + +objecttype_t *addot(int id, char *name, char *description, int material, float weight, int obclassid) { + objecttype_t *a; + + // add to the end of the list + if (objecttype == NULL) { + objecttype = malloc(sizeof(objecttype_t)); + a = objecttype; + a->prev = NULL; + } else { + // go to end of list + a = lastobjecttype; + a->next = malloc(sizeof(objecttype_t)); + a->next->prev = a; + a = a->next; + } + lastobjecttype = a; + a->next = NULL; + + // props + a->id = id; + a->name = strdup(name); + a->desc = strdup(description); + a->material = findmaterial(material); + a->weight = weight; + a->obclass = findoc(obclassid); + a->flags = addflagpile(); + + // for easy addition of flags + lastot = a; +} + +// does the pile "op" have an object we can +// stack "o" with +object_t *canstackob(obpile_t *op, object_t *match) { + object_t *o; + if (!hasflag(match->flags, F_STACKABLE)) { + return NULL; + } + for (o = op->first ; o ; o = o->next) { + if (obpropsmatch(o, match)) return o; + } + return NULL; +} + +// does the pile "op" have an object we can +// stack a new object of type "ot" with +object_t *canstacknewot(obpile_t *op, objecttype_t *match) { + object_t *o; + if (!hasflag(match->flags, F_STACKABLE)) { + return NULL; + } + for (o = op->first ; o ; o = o->next) { + if (o->type == match) { + if (isplainob(o)) return o; + } + } + return NULL; +} + + +void copyobprops(object_t *dst, object_t *src) { + dst->material = src->material; + dst->weight = src->weight; + // TODO: copy flags + if (src->inscription) { + dst->inscription = strdup(src->inscription); + } +} + +int countobs(obpile_t *op) { + object_t *o; + int count = 0; + for (o = op->first ; o ; o = o->next) { + count++; + } + return count; +} + +material_t *findmaterial(int id) { + material_t *m; + for (m = material ; m ; m = m->next) { + if (m->id == id) return m; + } + return NULL; +} + +objectclass_t *findoc(int id) { + objectclass_t *oc; + for (oc = objectclass ; oc ; oc = oc->next) { + if (oc->id == id) return oc; + } + return NULL; +} + +object_t *findobl(obpile_t *op, char let) { + object_t *o; + for (o = op->first; o ; o = o->next) { + if (o->letter == let) return o; + } + return NULL; +} + +objecttype_t *findot(int id) { + objecttype_t *ot; + for (ot = objecttype ; ot ; ot = ot->next) { + if (ot->id == id) return ot; + } + return NULL; +} + +objecttype_t *findotn(char *name) { + objecttype_t *ot; + char *searchfor; + searchfor = strdup(name); + for (ot = objecttype ; ot ; ot = ot->next) { + if (strstr(ot->name, searchfor)) { + free(searchfor); + return ot; + } + } + + // haven't found it yet - check for plurals + // search for words ending in "es" (eg. tomatoes) + if ((searchfor[strlen(searchfor)-1] == 's') && + (searchfor[strlen(searchfor)-2] == 'e') ) { + // remove trailing 'es' + searchfor[strlen(searchfor)-1] = '\0'; + searchfor[strlen(searchfor)-2] = '\0'; + // search again + for (ot = objecttype ; ot ; ot = ot->next) { + if (strstr(ot->name, searchfor)) { + free(searchfor); + return ot; + } + } + } + // reset back to inital word + free(searchfor); + searchfor = strdup(name); + + // search for words ending in "s" (eg. "swords") + if (searchfor[strlen(searchfor)-1] == 's') { + // remove trailing 's' + searchfor[strlen(searchfor)-1] = '\0'; + searchfor[strlen(searchfor)-2] = '\0'; + // search again + for (ot = objecttype ; ot ; ot = ot->next) { + if (strstr(ot->name, searchfor)) { + free(searchfor); + return ot; + } + } + } + return NULL; +} + + +int getletidx(char let) { + int i; + for (i = 0; i < MAXPILEOBS; i++) { + if (letorder[i] == let) return i; + } + return -1; +} + +// select lowest possible letter +char getnextletter(obpile_t *op, char *wantletter) { + object_t *o; + int curidx = -1, thisidx; + char let; + int db = B_FALSE; + + // try 'wantletter' first + if (wantletter) { + if (!pilehasletter(op, *wantletter)) { + return *wantletter; + } + } + + curidx = 0; // ie 'a' + + for (curidx = 0; curidx < MAXPILEOBS; curidx++) { + // does any other object in the pile have this letter? + let = letorder[curidx]; + if (!pilehasletter(op, let)) { + // if we didn't find it, this letter is okay. + return let; + } + } + + return '-'; +} + +// buf must already be allocated +char *getobname(object_t *o, char *buf, int count) { + char *pluralname; + char prefix[BUFLEN]; + + // handle ALL + if (count == ALL) { + count = o->amt; + } + + // figure out prefix + if ((count == 1) && !hasflag(o->flags, F_NO_A)) { + pluralname = strdup(o->type->name); + + if (o->blessknown) { + if (o->blessed == B_UNCURSED) { + strcpy(prefix, "an"); + } else { + strcpy(prefix, "a"); + } + } else { + if (isvowel(o->type->name[0])) { + strcpy(prefix, "an"); + } else { + strcpy(prefix, "a"); + } + } + } else { + // multiple objects? + if (hasflag(o->flags, F_NO_PLURAL)) { + pluralname = strdup(o->type->name); + } else { + pluralname = makeplural(o->type->name); + } + sprintf(prefix, "%d",count); + } + + sprintf(buf, "%s ", prefix); + + + // blessed status + if (o->blessknown) { + switch (o->blessed) { + case B_BLESSED: + strcat(buf, "blessed "); + break; + case B_UNCURSED: + strcat(buf, "uncursed "); + break; + case B_CURSED: + strcat(buf, "cursed "); + break; + } + } + + // object name + strcat(buf, pluralname); + free(pluralname); + + return buf; +} + + +char *getrandomob(map_t *map, char *buf) { + enum RARITY rarity; + objecttype_t *ot; + int roll; + objecttype_t *poss[MAXRANDOMOBCANDIDATES]; + int nposs = 0; + int selidx; + int amt; + flag_t *f; + int db = B_FALSE; + + // determine rarity of object to generate + rarity = RR_FREQUENT; + + // start with 'frequent'ly appearing items + // roll a die. 30% chance of getting rarer. + // stop when we fail the die roll. + roll = rnd(1,100); + while (roll < 30) { + rarity++; + if (rarity == RR_VERYRARE) break; + roll = rnd(1,100); + } + if (db) dblog("adding random object of rarity %d\n",rarity); + + + // try to find an object of this type which will + // fit in the map's habitat + nposs = 0; + while (nposs == 0) { + for (ot = objecttype ; ot ; ot = ot->next) { + if (hasflagval(ot->flags, F_RARITY, map->habitat, rarity, -1, "") || + hasflagval(ot->flags, F_RARITY, H_ALL, rarity, -1, "") ) { + poss[nposs] = ot; + nposs++; + if (nposs >= MAXRANDOMOBCANDIDATES) break; + } + } + + // nothing found? + if (nposs == 0) { + // already at lowest rarity? + if (rarity == RR_FREQUENT) { + // give up + sprintf(buf, ""); + if (db) dblog("no possible objects at all! giving up."); + return NULL; + } else { + // lower rarity and try again + rarity--; + if (db) dblog("no possible objects like this. trying again with rarity %d\n",rarity); + } + } + } + + if (db) dblog("got %d possibilities.",nposs); + // pick a random object from our possiblities + selidx = rnd(0,nposs-1); + ot = poss[selidx]; + // handle objects which appear in multiples (ie. rocks) + f = hasflag(ot->flags, F_NUMAPPEAR); + if (f) { + amt = rnd(f->val[0], f->val[1]); + } else { + amt = 1; + } + if (db) sprintf(buf, "%d %s", amt, ot->name); + return buf; +} + +object_t *hasob(obpile_t *op, enum OBTYPE oid) { + object_t *o; + for (o = op->first ; o ; o = o->next) { + if (o->type->id == oid) return o; + } + return NULL; +} + +void initobjects(void) { + // materials + addmaterial(MT_STONE, "stone"); + addmaterial(MT_FIRE, "fire"); + addmaterial(MT_PLASTIC, "plastic"); + addmaterial(MT_METAL, "metal"); + addmaterial(MT_WOOD, "wood"); + addmaterial(MT_FLESH, "flesh"); + addmaterial(MT_GOLD, "gold"); + + // object classes + addoc(OC_MONEY, "Money", '$'); + addoc(OC_SCROLL, "Scrolls", '?'); + addoc(OC_POTION, "Potions", '!'); + addoc(OC_WEAPON, "Weapons", '\\'); + addoc(OC_ARMOUR, "Armour", ']'); + addoc(OC_ROCK, "Rocks/Gems", '*'); + addoc(OC_FOOD, "Food", '%'); + addoc(OC_MISC, "Miscellaneous", '"'); + + // object types + addot(OT_GOLD, "gold coin", "Sparkling nuggets of gold, the standard currency of Nexus.", MT_GOLD, 0.1, OC_MONEY); + addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_RARITY, H_ALL, RR_COMMON, -1, ""); + addflag(lastot->flags, F_NUMAPPEAR, 1, 100, -1, ""); + addot(OT_STONE, "stone", "A medium-sized roundish stone.", MT_STONE, 0.5, OC_ROCK); + addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_RARITY, H_DUNGEON, RR_FREQUENT, -1, ""); + addflag(lastot->flags, F_NUMAPPEAR, 1, 10, -1, ""); + + // corpses + addot(OT_CORPSEHUMAN, "human corpse", "The dead body of a human.", MT_FLESH, 50, OC_FOOD); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 5, NA, ""); + addot(OT_CORPSEGOBLIN, "goblin corpse", "The dead body of a goblin.", MT_FLESH, 20, OC_FOOD); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 5, NA, ""); + addot(OT_CORPSEBAT, "bat corpse", "The dead body of a bat.", MT_FLESH, 5, OC_FOOD); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 5, NA, ""); + addot(OT_CORPSEBAT, "fly corpse", "The dead body of a giant flying insect.", MT_FLESH, 5, OC_FOOD); + addflag(lastot->flags, F_EDIBLE, B_TRUE, 5, NA, ""); +} + +// has this object changed proerties from its +// parent objecttype? +int isplainob(object_t *o) { + if (o->material != o->type->material) return B_FALSE; + if (o->weight != o->type->weight) return B_FALSE; + if (o->inscription) return B_FALSE; + if (o->blessed != B_UNCURSED) return B_FALSE; + if (o->blessknown) return B_FALSE; + + return B_TRUE; +} + + +void killmaterial(material_t *m) { + int i; + material_t *nextone, *lastone; + + // free mem + free(m->name); + + // remove from list + nextone = m->next; + if (nextone != NULL) { + nextone->prev = m->prev; + } else { /* last */ + lastmaterial = m->prev; + } + + if (m->prev == NULL) { + /* first */ + nextone = m->next; + free(material); + material = nextone; + } else { + lastone = m->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + +void killob(object_t *o) { + int i; + object_t *nextone, *lastone; + + // free mem + if (o->inscription) free(o->inscription); + + // remove from list + nextone = o->next; + if (nextone != NULL) { + nextone->prev = o->prev; + } else { /* last */ + o->pile->last = o->prev; + } + + if (o->prev == NULL) { + /* first */ + nextone = o->next; + o->pile->first = nextone; + free(o); + } else { + lastone = o->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + +void killoc(objectclass_t *oc) { + int i; + objectclass_t *nextone, *lastone; + + // free mem + free(oc->name); + + // remove from list + nextone = oc->next; + if (nextone != NULL) { + nextone->prev = oc->prev; + } else { /* last */ + lastobjectclass = oc->prev; + } + + if (oc->prev == NULL) { + /* first */ + nextone = oc->next; + free(objectclass); + objectclass = nextone; + } else { + lastone = oc->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + +void killot(objecttype_t *ot) { + int i; + objecttype_t *nextone, *lastone; + + // free mem + free(ot->name); + free(ot->desc); + if (ot->flags) killflagpile(ot->flags); + + // remove from list + nextone = ot->next; + if (nextone != NULL) { + nextone->prev = ot->prev; + } else { /* last */ + lastobjecttype = ot->prev; + } + + if (ot->prev == NULL) { + /* first */ + nextone = ot->next; + free(objecttype); + objecttype = nextone; + } else { + lastone = ot->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + + +object_t *moveob(object_t *src, obpile_t *dst, int howmany) { + object_t *o, *oo, *existob; + int i; + int db = B_FALSE; + + reason = E_OK; + + if (db) dblog("DB: moveob() - moving %d x %s",howmany, src->type->name); + existob = canstackob(dst, src); + if (existob) { + if (db) dblog("DB: moveob() - found stack to join"); + if (howmany == ALL) howmany = src->amt; + existob->amt += howmany; + src->amt -= howmany; + if (src->amt == 0) { + killob(src); + } + return existob; + } else { + if (db) dblog("DB: moveob() - no stack to join"); + + // space in destination pile? + if (getnextletter(dst, NULL) == '-') { + reason = E_NOSPACE; + return NULL; + } + + // no similar objects in the dest pile. + if ((howmany == ALL) || (howmany == src->amt)) { + if (db) dblog("DB: dropping ALL - relinking object to new pile"); + // just move the whole object to the new pile + o = relinkob(src, dst); + } else { + obpile_t *tempop; + if (db) dblog("DB: moving only some of a stack."); + + + // create temporary object pile + tempop = addobpile(NULL, NULL); + // add new object to the temporary pile + o = addob(tempop, src->type->name); + // copy props from original object + copyobprops(o, src); + + // just move some of them + for (i = 0; i < howmany; i++ ) { + // the first one is already there! + if (i != 0) { + // inc counter in temp pile + o->amt++; + } + // dec counter on original + src->amt--; + } + if (src->amt <= 0) { + killob(src); + } + // now move the object entry from the temporary pile to the + // real destination pile. + while (tempop->first) { + o = relinkob(tempop->first, dst); + } + free(tempop); + + } + } + + return o; +} + +int obfits(object_t *o, obpile_t *op) { + if (countobs(op) >= MAXPILEOBS) { + return B_FALSE; + } + return B_TRUE; +} + +// has this object changed proerties from its +// parent objecttype? +int obpropsmatch(object_t *a, object_t *b) { + if (a->type != b->type) return B_FALSE; + if (a->material != b->material) return B_FALSE; + if (a->weight != b->weight) return B_FALSE; + if (a->inscription || b->inscription) return B_FALSE; + if (a->blessed != b->blessed) return B_FALSE; + if (a->blessknown != b->blessknown) return B_FALSE; + return B_TRUE; +} + + +int pilehasletter(obpile_t *op, char let) { + object_t *o; + int found = B_FALSE; + + for (o = op->first ; o ; o = o->next) { + if (o->letter == let) { + found = B_TRUE; + break; + } + } + return found; +} + +object_t *relinkob(object_t *src, obpile_t *dst) { + if (!obfits(src, dst)) return NULL; + + // unlink this object from the current list + if (src->prev == NULL) { + // first + src->pile->first = src->next; + } else { + // not first + src->prev->next = src->next; + } + if (src->next == NULL) { + // last + src->pile->last = src->prev; + } else { + // not last + src->next->prev = src->prev; + } + + // add this object to the end of the list for the new pile + if (dst->first == NULL) { + // first element in new list + dst->first = src; + src->prev = NULL; + } else { + object_t *aa; + // go to end of list + aa = dst->last; + // not first in new list + src->prev = aa; + aa->next = src; + } + + + dst->last = src; + src->pile = dst; + src->next = NULL; + + // adjust letter if going to player + if (dst->owner && (dst->owner->controller == C_PLAYER)) { + src->letter = getnextletter(dst, &src->letter); + } + + return src; +} + +void throwat(lifeform_t *thrower, object_t *o, cell_t *where) { + char throwername[BUFLEN]; + char obname[BUFLEN]; + char targetname[BUFLEN]; + char buf[BUFLEN]; + lifeform_t *target; + + + getlfname(thrower, throwername); + + target = where->lf; + if (target) { + getlfname(target, targetname); + } + + // announce it ("xx throws xx" "at yy") + getobname(o, obname, 1); + if (thrower->controller == C_PLAYER) { + if (target) { + msg("You throw %s at %s.", obname, targetname); + } else { + msg("You throw %s.",obname); + } + } else { + } + + if (haslos(player, thrower->cell)) { + char throwstring[BUFLEN]; + char *throwercaps; + throwercaps = strdup(throwername); + capitalise(throwercaps); + sprintf(throwstring, "%s throw%s %s",throwercaps, + (thrower->controller == C_PLAYER) ? "" : "s", + obname); + + if (target && haslos(player, where)) { + strcat(throwstring, " at"); + strcat(throwstring, targetname); + } + strcat(throwstring, "."); + + free(throwercaps); + } + + taketime(thrower, SPEED_THROW); + + // TODO: figure out if you miss or not, based on distance + + // do throw animation + anim(thrower->cell, where, o->type->obclass->glyph); + + // if someone is there, they take damage and the object might die + if (target) { + int dam; + char damstring[BUFLEN]; + // damage is kilograms / 5. + // ie. 5 kg object does 1 damage + // ie. 100 kg object does 20 damage (person) + // ie. 1 tonne object does 200 damage (car) + dam = (int)ceil((double)o->weight / 5.0); + + // announce + if (haslos(player, where)) { + char *obcaps; + obcaps = strdup(obname); + capitalise(obcaps); + + msg("%s hits %s.",obcaps,targetname); + free(obcaps); + } + sprintf(damstring, "%s (thrown by %s)",obname,throwername); + losehp(target, dam, thrower, damstring); + } + + // either remove or move the object + moveob(o, where->obpile, 1); +} diff --git a/objects.h b/objects.h new file mode 100644 index 0000000..89286f7 --- /dev/null +++ b/objects.h @@ -0,0 +1,37 @@ +#ifndef __OBJECTS_H +#define __OBJECTS_H +#include "defs.h" + +material_t *addmaterial(enum MATERIAL id, char *name); +objectclass_t *addoc(enum OBCLASS id, char *name, char glyph); +object_t *addob(obpile_t *where, char *name); +object_t *addobject(obpile_t *where, char *name, int canstack); +obpile_t *addobpile(lifeform_t *owner, cell_t *where); +objecttype_t *addot(int id, char *name, char *description, int material, float weight, int obclassid); +object_t *canstackob(obpile_t *op, object_t *match); +object_t *canstacknewot(obpile_t *op, objecttype_t *match); +void copyobprops(object_t *dst, object_t *src); +int countobs(obpile_t *op); +material_t *findmaterial(int id); +objectclass_t *findoc(int id); +object_t *findobl(obpile_t *op, char let); // find object by letter +objecttype_t *findot(int id); +objecttype_t *findotn(char *name); // find objecttype by name +int getletindex(char let); +char getnextletter(obpile_t *op, char *wantletter); +char *getobname(object_t *o, char *buf, int count); +char *getrandomob(map_t *map, char *buf); +object_t *hasob(obpile_t *op, enum OBTYPE oid); +void initobjects(void); +int isplainob(object_t *o); +void killmaterial(material_t *m); +void killob(object_t *o); +void killoc(objectclass_t *oc); +void killot(objecttype_t *ot); +object_t *moveob(object_t *src, obpile_t *dst, int howmany); +int obfits(object_t *o, obpile_t *op); +int obpropsmatch(object_t *a, object_t *b); +int pilehasletter(obpile_t *op, char let); +object_t *relinkob(object_t *src, obpile_t *dst); +void throwat(lifeform_t *thrower, object_t *o, cell_t *where); +#endif diff --git a/save.c b/save.c new file mode 100644 index 0000000..b6d07db --- /dev/null +++ b/save.c @@ -0,0 +1,594 @@ +#include +#include +#include +#include +#include "defs.h" +#include "lf.h" +#include "map.h" +#include "move.h" +#include "nexus.h" +#include "objects.h" +#include "save.h" + +extern lifeform_t *player; +extern map_t *firstmap; + +int loadall(void) { + DIR *dir; + struct dirent *ent; + char *filename; + FILE *f; + + dir = opendir(MAPDIR); + if (!dir) { + dblog("Could not open map directory '%s'",MAPDIR); + return B_TRUE; + } + // for each map file in directory + while ((ent = readdir(dir)) != NULL) { + char *p; + // ie. start of 4 char prefix + p = ent->d_name + strlen(ent->d_name) - 4; + // load this map + if (!strcmp(p, ".map") ) { + if (!loadmap(ent->d_name)) { + printf("Error loading map from file '%s'",ent->d_name); + exit(1); + } + } + } + closedir(dir); + + loadsavegame(); + + return B_FALSE; +} + + + +// load and allocate one lifeform from given file +lifeform_t *loadlf(FILE *f, cell_t *where) { + lifeform_t *l; + int lfid, lfraceid; + long obid; + int rv; + int i; + char buf[BUFLEN]; + int obcount; + int mapid; + map_t *m; + int x,y; + int db = B_TRUE; + + if (db) dblog("--> Loading lifeform...\n"); + + fscanf(f, "startlf\n"); + fscanf(f, "lfid: %d\n",&lfid); + fscanf(f, "race: %d\n",&lfraceid); + fscanf(f, "map: %d\n",&mapid); + fscanf(f, "pos: %d,%d\n",&x, &y); + + // find the map + m = findmap(mapid); + if (!m) { + dblog("ERROR loading lf id %d: can't find map id %d\n",lfid,mapid); + exit(1); + } + if (where == NULL) { + where = getcellat(m, x, y); + } + if (!m) { + dblog("ERROR loading lf id %d: can't find cell at %d,%d on map id %d\n",lfid,x,y,mapid); + exit(1); + } + + l = addlf(where, lfraceid); + l->id = lfid; + l->x = x; + l->y = y; + + // load rest of this lf + fscanf(f, "contr: %d\n",&l->controller); + fscanf(f, "hp: %d/%d\n",&l->hp, &l->maxhp); + fscanf(f, "alive: %d\n",&l->alive); + + fgets(buf, BUFLEN, f); // lastdam + buf[strlen(buf)-1] = '\0'; // strip newline + l->lastdam = strdup(buf + 9); // after 'lastdam: ' + + fscanf(f, "timespent: %d\n",&l->timespent); + + fscanf(f, "sorted: %d\n",&l->sorted); + + if (db) dblog("--> Got hp=%d/%d. timespend=%d. sorted=%d. Now loading ob list.",l->hp,l->maxhp,l->timespent,l->sorted); + + // load object list + obcount = 0; + obid = 9999; // for testing + rv = fscanf(f, "ob:%ld\n",&obid); + while (obid != -1) { + if (db) dblog("--> Load ob id %d into list...",obid); + l->pack->oblist[obcount] = obid; + obcount++; + fscanf(f, "ob:%ld\n",&obid); + } + // terminate with -1s! + for (i = obcount ; i < MAXPILEOBS; i++) { + l->pack->oblist[i] = -1; + } + if (db) dblog("--> Finished oblist. Found %d objects.",obcount); + + // now load load object defs! + fscanf(f, "obdefs\n"); + for (i = 0; i < obcount; i++) { + //if (db) dblog("-----> ob %d/%d...\n",i+1,obcount); + if (loadob(f, l->pack)) { + dblog("Error - can't create object %d/%d!\n",i+1,obcount); + exit(1); + } + } + + // is this the player? + if (l->controller == C_PLAYER) { + player = l; + } +} + + + +map_t *loadmap(char *basefile) { + FILE *f; + char filename[BUFLEN]; + char buf[BUFLEN]; + char buf2[BUFLEN]; + int obcount; + int i; + int x,y; + int db = B_TRUE; + lifeform_t *l; + object_t *o,*nexto; + map_t *m; + cell_t *dummycell; + + if (db) dblog("Loading map from %s...",basefile); + sprintf(filename, "%s/%s",MAPDIR,basefile); + f = fopen(filename, "rt"); + + + + // create map + m = addmap(); + dummycell = malloc(sizeof(cell_t)); + dummycell->obpile = addobpile(NULL, dummycell); + dummycell->map = m; + dummycell->type = (celltype_t *)0xabcde; // for debugging + + if (!m) { + dblog("Error creating map while loading file '%s'",filename); + return NULL; + } + + // load map info + if (db) dblog("--> Loading map info...\n"); + fscanf(f, "id:%d\n",&m->id); // map id + fgets(buf, BUFLEN, f); // map name + buf[strlen(buf)-1] = '\0'; // strip newline + m->name = strdup(buf + 5); // after 'name:' + fscanf(f, "habitat:%d\n",(int *)&m->habitat); // habitat + fscanf(f, "seed:%d\n",&m->seed); // seed + fscanf(f, "dims:%d,%d\n",&m->w, &m->h); // map dimensons + fscanf(f, "nextlfid:%ld\n",&m->nextlfid); + fscanf(f, "nextmaps:\n"); + for (i = 0; i < MAXDIR_ORTH; i++) { + fscanf(f, "%d\n",&m->nextmap[i]); // map dimensons + } + if (db) dblog("--> Finished map info. name='%s', dims=%d x %d\n",m->name, m->w, m->h); + fscanf(f, "id:%d\n",&m->id); // map id + + // load lifeforms + if (db) dblog("--> Loading lifeforms...\n"); + fscanf(f, "lifeforms:\n"); + + fscanf(f, "%s\n",buf); + while (!strcmp(buf ,"startlf")) { + loadlf(f, dummycell); + + // check for more lifeforms... + fscanf(f, "%s\n",buf); + } + + // load cells + if (db) dblog("--> Loading map cells...\n"); + fscanf(f, "cells:\n"); + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + cell_t *c; + celltype_t *ct; + int celltypeid; + long obid; + + //if (db) dblog("cell %d,%d...",x,y); + + // allocate this cell + c = addcell(m, x, y); + /* + c = m->cell[y * m->w + x]; + c->map = m; + c->obpile = addobpile(NULL, c); + c->lf = NULL; + c->x = x; + c->y = y; + c->roomid = -1; + */ + + // cell info + fscanf(f, "%d,%d,%d,%d\n", + &c->roomid, &celltypeid, &c->known, &c->visited); + + + ct = findcelltype(celltypeid); + if (ct) { + c->type = ct; + } else { + dblog("ERROR loading map cells - can't find celltype id %ld\n",celltypeid); + exit(1); + } + + // cell objects + obcount = 0; + fscanf(f, "ob:%ld\n",&obid); + while (obid != -1) { + c->obpile->oblist[obcount] = obid; + obcount++; + fscanf(f, "ob:%ld\n",&obid); + } + // terminate with -1s! + for (i = obcount ; i < MAXPILEOBS; i++) { + c->obpile->oblist[i] = -1; + } + } + } + + // load object definitions + if (db) dblog("--> Loading object definitions for map obs...\n"); + fscanf(f, "MAPOBS:%d\n",&obcount); // how many obs? + + // create all objects in a dummy cell + for (i = 0; i < obcount; i++) { + if (db) dblog("-----> ob %d/%d...\n",i+1,obcount); + if (loadob(f, dummycell->obpile)) { + dblog("Error - can't create object %d/%d!\n",i+1,obcount); + exit(1); + } + } + + // hand out the objects to lifeforms... + for (l = m->lf ; l ; l = l->next) { + int curob = 0; + long obid; + obid = l->pack->oblist[curob]; + while (obid != -1) { + int found = B_FALSE; + // find this ob id in the dummycell + for (o = dummycell->obpile->first ; o ; o = nexto) { + nexto = o->next; + if (o->id == obid) { + relinkob(o, l->pack); + found = B_TRUE; + break; + } + } + if (!found) { + dblog("Error loading obs - lf %d should have obid %ld but can't find it.\n",l->id, obid); + exit(1); + } + // next one + curob++; + obid = l->pack->oblist[curob]; + + } + // clear the oblist + for (i = 0; i < MAXPILEOBS; i++) { + l->pack->oblist[i] = -1; + } + + } + + // hand out objects to cells + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + cell_t *c; + long obid; + int curob = 0; + c = m->cell[y * m->w + x]; + + obid = c->obpile->oblist[curob]; + while (obid != -1) { + int found = B_FALSE; + // find this ob id in the dummycell + for (o = dummycell->obpile->first ; o ; o = nexto) { + nexto = o->next; + if (o->id == obid) { + relinkob(o, c->obpile); + found = B_TRUE; + break; + } + } + if (!found) { + dblog("Error loading obs - cell %d,%d should have obid %ld but can't find it.\n",x,y, obid); + exit(1); + } + // next one + curob++; + } + // clear the oblist + for (i = 0; i < MAXPILEOBS; i++) { + c->obpile->oblist[i] = -1; + } + + } + } + fclose(f); + + // move lifeforms to their proper locations + for (l = m->lf ; l ; l = l->next) { + cell_t *c; + c = getcellat(m, l->x, l->y); + if (!c) { + dblog("Error loading map - Can't find cell at %d,%d to place lifeform id %d.\n", + l->x, l->y, l->id); + exit(1); + } + movelf(l, c); + //dblog("Moving lf %d to %d,%d\n",l->id, l->x, l->y); + } + + free(dummycell); + + + return m; +} + +int loadob(FILE *f, obpile_t *op) { + objecttype_t *ot; + object_t *o; + material_t *mat; + long obid; + int otid,matid; + char buf[BUFLEN]; + int flagid; + flag_t tempflag; + int rv; + + fscanf(f, "id:%ld\n",&obid); + fscanf(f, "type:%d\n",&otid); + + ot = findot(otid); + if (!ot) { + dblog("ERROR loading objects - can't find obtype id %ld\n",obid); + return B_TRUE; + } + // create the object + o = addob(op, ot->name); + + // overwrite ob parameters + o->id = obid; + fscanf(f, "material:%d\n",&matid); + mat = findmaterial(matid); + if (!mat) { + dblog("ERROR loading objects - can't find material %d\n",matid); + return B_TRUE; + } + o->material = mat; + + fscanf(f, "weight:%f\n",&o->weight); + + fgets(buf, BUFLEN, f); // inscription + buf[strlen(buf)-1] = '\0'; // strip newline + o->inscription = strdup(buf + 6); // after 'inscr:' + if (!strcmp(o->inscription, "^^^")) { // ie. if equal to ^^^... + free(o->inscription); + o->inscription = NULL; + } + fscanf(f, "letter:%c\n",&o->letter); + fscanf(f, "bless:%d\n",&o->blessed); + fscanf(f, "blessknown:%d\n",&o->blessknown); + fscanf(f, "amt:%d\n",&o->amt); + + fscanf(f, "flags:\n"); + + rv = fscanf(f, "%d,%d,%d,%d,%d\n", + &tempflag.id, &tempflag.nvals, &tempflag.val[0], &tempflag.val[1], &tempflag.val[2]); + + // get flag text + fgets(buf, BUFLEN, f); + buf[strlen(buf)-1] = '\0'; // strip newline + + while (tempflag.id != -1) { + dblog("got flag id=%d\n",tempflag.id); + + addflag(o->flags, tempflag.id, + tempflag.val[0], + tempflag.val[1], + tempflag.val[2], strcmp(buf, "^^^") ? buf : NULL); + // load next one + rv = fscanf(f, "%d,%d,%d,%d,%d\n", + &tempflag.id, &tempflag.nvals, &tempflag.val[0], &tempflag.val[1], &tempflag.val[2]); + //dblog("fscanf returned %d\n",rv); + } + fscanf(f, "endob\n"); + return B_FALSE; +} + +int loadsavegame(void) { + DIR *dir; + struct dirent *ent; + char filename[BUFLEN]; + FILE *f; + + // now see if there is a savegame... + dir = opendir(SAVEDIR); + if (!dir) { + dblog("Could not open savegame directory '%s'",SAVEDIR); + return B_TRUE; + } + + ent = readdir(dir); + while ((ent = readdir(dir)) != NULL) { + char *p; + // ie. start of 4 char prefix + p = ent->d_name + strlen(ent->d_name) - 4; + // load this savegame + if (!strcmp(p, ".sav") ) { + sprintf(filename, "%s/%s",SAVEDIR,ent->d_name); + dblog("Trying to load from %s\n",filename); + f = fopen(filename, "rt"); + if (!f) { + printf("Error opening savegame file '%s'",ent->d_name); + exit(1); + } + if (!loadlf(f, NULL)) { + printf("Error loading savegame from file '%s'",ent->d_name); + exit(1); + } + fclose(f); + + // successful load - kill the savegame now + unlink(filename); + break; + } + } + closedir(dir); + return B_FALSE; +} + +int savelf(FILE *f, lifeform_t *l) { + object_t *o; + int obcount = 0; + // save this lf + fprintf(f, "startlf\n"); + fprintf(f, "lfid: %d\n",l->id); + fprintf(f, "race: %d\n",l->race->id); + fprintf(f, "map: %d\n",l->cell->map->id); + fprintf(f, "pos: %d,%d\n",l->cell->x, l->cell->y); + fprintf(f, "contr: %d\n",l->controller); + fprintf(f, "hp: %d/%d\n",l->hp, l->maxhp); + fprintf(f, "alive: %d\n",l->alive); + fprintf(f, "lastdam: %s\n",l->lastdam); + fprintf(f, "timespent: %d\n",l->timespent); + fprintf(f, "sorted: %d\n",l->sorted); + // lifeform objects + obcount = 0; + for (o = l->pack->first ; o ; o = o->next) { + fprintf(f, "ob:%ld\n",o->id); + obcount++; + } + fprintf(f, "ob:-1\n"); + + fprintf(f, "obdefs\n"); + + // now save our object definitions + for (o = l->pack->first ; o ; o = o->next) { + saveob(f, o); + } + + return B_FALSE; +} + + +int savemap(map_t *m) { + FILE *f; + char filename[BUFLEN]; + int i; + object_t *o; + lifeform_t *l; + int x,y; + int obcount = 0; + + // TODO: check that map dir exists + sprintf(filename, "%s/map%d.map",MAPDIR, m->id); + f = fopen(filename, "wt"); + + // save map info + fprintf(f, "id:%d\n",m->id); // map id + fprintf(f, "name:%s\n",m->name); // map name + fprintf(f, "habitat:%d\n",m->habitat); // habitat + fprintf(f, "seed:%d\n",m->seed); // seed + fprintf(f, "dims:%d,%d\n",m->w, m->h); // map dimensons + fprintf(f, "nextlfid:%ld\n",m->nextlfid); + fprintf(f, "nextmaps:\n"); + for (i = 0; i < MAXDIR_ORTH; i++) { + fprintf(f, "%d\n",m->nextmap[i] ); // map dimensons + } + + // save all non-player lifeforms (includes their objects) + fprintf(f, "lifeforms:\n"); + for (l = m->lf ; l ; l = l->next) { + if (l->controller != C_PLAYER) { // don't save the player! + savelf(f, l); + } + } + fprintf(f, "endlifeforms\n"); + + // cells + fprintf(f, "cells:\n"); + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + cell_t *c; + c = getcellat(m, x, y); + // cell info + fprintf(f, "%d,%d,%d,%d\n", + c->roomid, c->type->id, c->known, c->visited); + // cell objects + for (o = c->obpile->first ; o ; o = o->next) { + fprintf(f, "ob:%ld\n",o->id); + obcount++; + } + fprintf(f, "ob:-1\n"); + } + } + + // save object definitions from map cells + fprintf(f, "MAPOBS:%d\n",obcount); + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + cell_t *c; + c = getcellat(m, x, y); + // cell objects + for (o = c->obpile->first ; o ; o = o->next) { + saveob(f, o); + } + } + } + + fclose(f); + + return B_FALSE; +} + + +int saveob(FILE *f, object_t *o) { + flag_t *fl; + fprintf(f, "id:%ld\n",o->id); + fprintf(f, "type:%d\n",o->type->id); + fprintf(f, "material:%d\n",o->material->id); + fprintf(f, "weight:%f\n",o->weight); + fprintf(f, "inscr:%s\n",o->inscription ? o->inscription : "^^^"); + fprintf(f, "letter:%c\n",o->letter); + fprintf(f, "bless:%d\n",o->blessed); + fprintf(f, "blessknown:%d\n",o->blessknown); + fprintf(f, "amt:%d\n",o->amt); + fprintf(f, "flags:\n"); + for (fl = o->flags->first ; fl ; fl = fl->next) { + fprintf(f, "%d,%d,%d,%d,%d\n", + fl->id, fl->nvals, fl->val[0], fl->val[1], fl->val[2]); + if (!fl->text || !strcmp(fl->text, "")) { + fprintf(f, "%s\n","^^^"); + } else { + fprintf(f, "%s\n",fl->text); + } + } + fprintf(f, "-1,-1,-1,-1,-1\n"); + fprintf(f, "endob\n"); + return B_FALSE; +} diff --git a/save.h b/save.h new file mode 100644 index 0000000..5f211f0 --- /dev/null +++ b/save.h @@ -0,0 +1,10 @@ +#include "defs.h" + +int loadall(void); +lifeform_t *loadlf(FILE *f, cell_t *where); +map_t *loadmap(char *basefile); +int loadob(FILE *f, obpile_t *op); +int loadsavegame(void); +int savelf(FILE *f, lifeform_t *l); +int savemap(map_t *m); +int saveob(FILE *f, object_t *o); diff --git a/svn-commit.tmp b/svn-commit.tmp new file mode 100644 index 0000000..af28af7 --- /dev/null +++ b/svn-commit.tmp @@ -0,0 +1,37 @@ +Initial checkin + +--This line, and those below, will be ignored-- + +A objects.h +A ai.h +A save.c +A lf.c +A nexus.c +A save.h +A move.c +A lf.h +A attack.c +A io.c +A nexus.h +A flag.c +A object.h +A move.h +A io.h +A attack.h +A map.c +A log.txt +A flag.h +A map.h +A doc +A doc/add_race.txt +A doc/add_obclass.txt +A text.c +A defs.h +A data +A data/maps +A data/save +A ai.c +A objects.c +A text.h +A Makefile +A mod_ob.txt diff --git a/text.c b/text.c new file mode 100644 index 0000000..9a4b51e --- /dev/null +++ b/text.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include "defs.h" +#include "objects.h" + +char *capitalise(char *text) { + text[0] = toupper(text[0]); + return text; +} + +int isvowel (char c) { + switch (c) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return B_TRUE; + } + return B_FALSE; +} + +// allocates and returns new string +char *makeplural(char *text) { + int newlen; + char *newtext; + char lastlet; + + lastlet = text[strlen(text)-1]; + switch (lastlet) { + case 's': + case 'o': // append "es" + asprintf(&newtext, "%ses",text); + break; + default: // append "s" + asprintf(&newtext, "%ss",text); + break; + } + return newtext; +} diff --git a/text.h b/text.h new file mode 100644 index 0000000..0d97da4 --- /dev/null +++ b/text.h @@ -0,0 +1,6 @@ +#include "defs.h" + +char *capitalise(char *text); +int isvowel(char c); +char *makeplural(char *text); +