From 19d30819ae9a9e41d252496645bd83cea81ea881 Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Thu, 2 Dec 2010 01:17:54 +0000 Subject: [PATCH] initial checkin --- Makefile | 2 + ai.c | 124 ++++ ai.h | 5 + attack.c | 50 ++ attack.h | 3 + defs.h | 374 ++++++++++++ doc/add_obclass.txt | 8 + doc/add_race.txt | 11 + flag.c | 120 ++++ flag.h | 10 + io.c | 921 +++++++++++++++++++++++++++++ io.h | 30 + lf.c | 639 ++++++++++++++++++++ lf.h | 19 + log.txt | 939 ++++++++++++++++++++++++++++++ map.c | 1355 +++++++++++++++++++++++++++++++++++++++++++ map.h | 29 + mod_ob.txt | 5 + move.c | 146 +++++ move.h | 8 + nexus.c | 297 ++++++++++ nexus.h | 13 + object.h | 0 objects.c | 940 ++++++++++++++++++++++++++++++ objects.h | 37 ++ save.c | 594 +++++++++++++++++++ save.h | 10 + svn-commit.tmp | 37 ++ text.c | 41 ++ text.h | 6 + 30 files changed, 6773 insertions(+) create mode 100644 Makefile create mode 100644 ai.c create mode 100644 ai.h create mode 100644 attack.c create mode 100644 attack.h create mode 100644 defs.h create mode 100644 doc/add_obclass.txt create mode 100644 doc/add_race.txt create mode 100644 flag.c create mode 100644 flag.h create mode 100644 io.c create mode 100644 io.h create mode 100644 lf.c create mode 100644 lf.h create mode 100644 log.txt create mode 100644 map.c create mode 100644 map.h create mode 100644 mod_ob.txt create mode 100644 move.c create mode 100644 move.h create mode 100644 nexus.c create mode 100644 nexus.h create mode 100644 object.h create mode 100644 objects.c create mode 100644 objects.h create mode 100644 save.c create mode 100644 save.h create mode 100644 svn-commit.tmp create mode 100644 text.c create mode 100644 text.h 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); +