/* * Implementation of maps. */ #include #include #include #include #include "obj.h" #include "vars.h" #include "pline.h" #include "structs.h" #include "scrsyms.h" #include "display.h" #include "objtypes.h" #include "obj-map.h" /* * Return the MAP for an OBJ. */ #define OBJMAP(o) ((MAP *)(o->private)) /* * Auto-map-switch link type: UP and DN for stairs, GATE for gates. */ typedef enum { AMT_UP = 1, AMT_DN, AMT_GATE, } AMT; typedef struct map MAP; typedef struct automap AUTOMAP; /* * Auto-map-switch links. Each of these is kept in two linked lists, * the autos list for the map it links from and the autoback list for * the map it links to. link and backlink are the links for these * lists. (These do not form a doubly-linked list.) Beyond that, * there's the link type, gate location, linked-to map, and offset * deltas. */ struct automap { AUTOMAP *link; AUTOMAP *backlink; AMT t; int gatex; int gatey; OBJ *to; int odx; int ody; } ; /* * Private data for a map. A map has a label, min and max coordinates, * the mapped text, and auto-map-switch lists, autos for the list of * links with this as the source map, and autoback for the list of * links withthis as the destination map. */ struct map { char *label; int minx; int maxx; int miny; int maxy; char **map; AUTOMAP *autos; AUTOMAP *autoback; } ; /* * Return the MAP for an OBJ. This is just a function version of * MAPOBJ with more checking. */ static MAP *mapforobj(OBJ *o) { if (o->type != OBJ_MAP) panic("non-map where map needed"); return(o->private); } /* * Return true if o is a blank map. */ int map_blank(OBJ *o) { MAP *m; m = mapforobj(o); return(!m->map && !m->label); } /* * Return the label string for a map object. */ const char *map_label(OBJ *o) { MAP *m; m = mapforobj(o); return(m->label); } /* * Set the label string for a map. */ void map_setlabel(OBJ *o, const char *newlabel) { MAP *m; m = mapforobj(o); free(m->label); m->label = newlabel ? strdup(newlabel) : 0; } /* * Return a map's min and max coordinates. */ #define FOO(x) int map_##x(OBJ *o) { MAP *m; m = mapforobj(o); return(m->x); } FOO(minx) FOO(miny) FOO(maxx) FOO(maxy) #undef FOO /* * Return the character on a given map at a given location. */ char map_charat(OBJ *o, int x, int y) { MAP *m; m = mapforobj(o); if ( !m->map || (x < m->minx) || (x > m->maxx) || !m->map[x-m->minx] || (y < m->miny) || (y > m->maxy) ) return(SYM_OOS); return(m->map[x-m->minx][y-m->miny]); } /* * Set the character on a given map at a given location. This handles * expanding the map as necessary. */ int map_setcharat(OBJ *o, int x, int y, char c) { MAP *m; int new; int i; int j; char *col; char oc; m = mapforobj(o); if (m->map == 0) { m->map = malloc(17*sizeof(char *)); for (i=0;i<17;i++) m->map[i] = 0; m->minx = x - 8; m->maxx = x + 8; m->miny = y - 5; m->maxy = y + 5; } if (x < m->minx) { new = x - 8; m->map = realloc(m->map,(m->maxx+1-new)*sizeof(char *)); bcopy(m->map,m->map+(m->minx-new),(m->maxx+1-m->minx)*sizeof(char *)); for (i=m->minx-new-1;i>=0;i--) m->map[i] = 0; m->minx = new; } else if (x > m->maxx) { new = x + 8; m->map = realloc(m->map,(new+1-m->minx)*sizeof(char *)); j = new - m->minx; for (i=m->maxx+1-m->minx;i<=j;i++) m->map[i] = 0; m->maxx = new; } if (y < m->miny) { new = y - 5; for (i=m->maxx-m->minx;i>=0;i--) { col = m->map[i]; if (col) { col = realloc(col,m->maxy+1-new); bcopy(col,col+(m->miny-new),m->maxy+1-m->miny); memset(col,SYM_OOS,m->miny-new); m->map[i] = col; } } m->miny = new; } if (y > m->maxy) { new = y + 5; for (i=m->maxx-m->minx;i>=0;i--) { col = m->map[i]; if (col) { col = realloc(col,new+1-m->miny); memset(col+(m->maxy+1-m->miny),SYM_OOS,new-m->maxy); m->map[i] = col; } } m->maxy = new; } col = m->map[x-m->minx]; if (! col) { col = malloc(m->maxy+1-m->miny); memset(col,SYM_OOS,m->maxy+1-m->miny); m->map[x-m->minx] = col; } oc = col[y-m->miny]; if ( (oc == c) || ( (col[y-m->miny] == SYM_TWALL) && ((c == SYM_HWALL) || (c == SYM_VWALL)) ) ) return(0); col[y-m->miny] = c; return(1); } /* * Find a blank map in the player's inventory. If any are found, split * off one and return it. If not, return nil. */ static OBJ *get_blank_map(void) { INVOBJ *io; for (io=you->invent.inv;io;io=io->link) { if ((io->v[0]->type == OBJ_MAP) && map_blank(io->v[0])) { inventory_split_n(io,1,find_xwi(&you->invent)); return(io->v[0]); } } return(0); } /* * Find an AUTOMAP link of type kind out from the current map and * return it, or return nil if none is found. */ static AUTOMAP *find_auto(OBJ *o, AMT kind, ...) { MAP *m; int x; int y; va_list argp; AUTOMAP **ap; AUTOMAP *a; m = mapforobj(o); if (kind == AMT_GATE) { va_start(argp,kind); x = va_arg(argp,int); y = va_arg(argp,int); va_end(argp); } ap = &m->autos; while ((a = *ap)) { if (a->to == 0) { *ap = a->link; free(a); continue; } if (a->t == kind) { switch (kind) { case AMT_UP: case AMT_DN: return(a); break; case AMT_GATE: if ((a->gatex == x) && (a->gatey == y)) return(a); break; } } ap = &a->link; } return(0); } /* * Set up AMT_UP and AMT_DN autolinks between o1 and o2. d1 and d2 are * the types (which may be AMT_UP and AMT_DN or AMT_DN and AMT_UP * depending on which way the stairs are going); odx and ody are the * map shift offsets between the two levels. */ static void setup_updn_autos(OBJ *o1, AMT d1, OBJ *o2, AMT d2, int odx, int ody) { AUTOMAP *a; MAP *m1; MAP *m2; m1 = mapforobj(o1); m2 = mapforobj(o2); a = malloc(sizeof(AUTOMAP)); a->t = d1; a->to = o2; a->odx = odx; a->ody = ody; a->link = m1->autos; m1->autos = a; a->backlink = m2->autoback; m2->autoback = a; a = malloc(sizeof(AUTOMAP)); a->t = d2; a->to = o1; a->odx = - odx; a->ody = - ody; a->link = m2->autos; m2->autos = a; a->backlink = m1->autoback; m1->autoback = a; } /* * Set up AMT_GATE autolinks between o1 and o2. g1x/g1y/g2x/g2y are * the locatinos of the two gates; odx and ody are the map shift * offsets between the two levels. */ static void setup_gate_autos(OBJ *o1, int g1x, int g1y, OBJ *o2, int g2x, int g2y, int odx, int ody) { AUTOMAP *a; MAP *m1; MAP *m2; m1 = mapforobj(o1); m2 = mapforobj(o2); a = malloc(sizeof(AUTOMAP)); a->t = AMT_GATE; a->gatex = g1x; a->gatey = g1y; a->to = o2; a->odx = odx; a->ody = ody; a->link = m1->autos; m1->autos = a; a->backlink = m2->autoback; m2->autoback = a; a = malloc(sizeof(AUTOMAP)); a->t = AMT_GATE; a->gatex = g2x; a->gatey = g2y; a->to = o1; a->odx = - odx; a->ody = - ody; a->link = m2->autos; m2->autos = a; a->backlink = m1->autoback; m1->autoback = a; } /* * Try to auto-switch maps for stairs, given the types of the two * directions. tag is the label to be applied to the new map, if a * blank map is allocated as the new map for the switch. */ static int map_auto_switch_updn(AMT out, AMT back, const char *tag) { AUTOMAP *a; OBJ *m; a = find_auto(map,out); if (a && !inv_present(&you->invent,a->to)) a = 0; if (! a) { m = get_blank_map(); if (! m) { pline("[Auto-switch failed - no blank map available]"); return(0); } setup_updn_autos(map,out,m,back,-mapox,-mapoy); map = m; mapox = 0; mapoy = 0; OBJMAP(m)->label = strdup(tag); pline("[Map auto-switched to a new map]"); return(1); } map = a->to; mapox += a->odx; mapoy += a->ody; pline("[Map auto-switched]"); return(1); } /* * Try to auto-switch maps for a gate. tag is the label to be applied * to the new map, if a blank map is allocated as the new map for the * switch. */ int map_auto_switch_gate(LOC *lc, const char *tag) { AUTOMAP *a; OBJ *m; a = find_auto(map,AMT_GATE,lc->x,lc->y); if (! a) { m = get_blank_map(); if (! m) { pline("[Map auto-switch failed - no blank map available]"); return(0); } setup_gate_autos(map,lc->x,lc->y,m,lc->to->x,lc->to->y,-mapox,-mapoy); map = m; mapox = 0; mapoy = 0; OBJMAP(m)->label = strdup(tag); pline("[Map auto-switched to a new map]"); return(1); } map = a->to; mapox += a->odx; mapoy += a->ody; pline("[Map auto-switched]"); return(1); } /* * Tru to auto-switch maps for taking stairs up. */ int map_auto_switch_up(const char *tag) { return(map_auto_switch_updn(AMT_UP,AMT_DN,tag)); } /* * Tru to auto-switch maps for taking stairs down. */ int map_auto_switch_dn(const char *tag) { return(map_auto_switch_updn(AMT_DN,AMT_UP,tag)); } /* * Find the map that's an auto-link up from the argument map. If one * is found, set *oxp and *oyp to the deltas between the two and * return the linked-to map OBJ; if not, return nil. */ OBJ *map_auto_link_up(OBJ *m, int *oxp, int *oyp) { AUTOMAP *a; a = find_auto(m,AMT_UP); if (! a) return(0); *oxp += a->odx; *oyp += a->ody; return(a->to); } /* * Find the map that's an auto-link down from the argument map. If one * is found, set *oxp and *oyp to the deltas between the two and * return the linked-to map OBJ; if not, return nil. */ OBJ *map_auto_link_dn(OBJ *m, int *oxp, int *oyp) { AUTOMAP *a; a = find_auto(m,AMT_DN); if (! a) return(0); *oxp += a->odx; *oyp += a->ody; return(a->to); } /* * Remove rem from m's autoback list. */ static void remove_autoback(OBJ *m, AUTOMAP *rem) { AUTOMAP **ap; AUTOMAP *a; ap = &OBJMAP(m)->autoback; while ((a = *ap)) { if (a == rem) { *ap = a->backlink; return; } ap = &a->backlink; } } /* * Complete map o, making it an accurate map of lv. This, basically, * implements the scroll of magic mapping. */ void map_complete(OBJ *o, LEVEL *lv) { int x; int y; LOC *l; for (x=0;xcells[x][0]; for (y=0;ylabel = 0; m->minx = LEV_X; m->maxx = 0; m->miny = LEV_Y; m->maxy = 0; m->map = 0; m->autos = 0; m->autoback = 0; o->private = m; return(o); } /* * The old method for maps. */ static void old(OBJ *o) { MAP *m; int x; AUTOMAP *a; m = mapforobj(o); for (x=m->minx;x<=m->maxx;x++) free(m->map[x]); for (a=m->autoback;a;a=a->backlink) a->to = 0; while (m->autos) { a = m->autos; m->autos = a->link; remove_autoback(a->to,a); free(a); } free(m->map); free(m); } /* * The format method for maps. Maps use a completely custom format * function rather than relying on custom format strings and * conditionals and the standard format function. */ static void format(FILE *f, INVOBJ *io) { OBJ *o; MAP *m; if (io->dispn > 1) { fprintf(f,"%d blank maps",io->dispn); return; } o = io->v[0]; m = mapforobj(o); if (map_blank(o)) { fprintf(f,"a blank map"); } else { fprintf(f,"a map ("); if (m->label) fprintf(f,"label=%s",m->label); else fprintf(f,"no label"); if (m->map) { fprintf(f,", x=[%d..%d], y=[%d..%d])",m->minx,m->maxx,m->miny,m->maxy); } else { fprintf(f,", no content)"); } } if (o == map) fprintf(f," (in use)"); } /* * The identical method for maps. */ static int identical(OBJ *o1, OBJ *o2) { return((o1!=map)&&(o2!=map)&&map_blank(o1)&&map_blank(o2)); } /* * The collapsible method for maps. */ static int collapsible(OBJ *o1, OBJ *o2) { return(identical(o1,o2)); } /* * The identified method for maps. */ static int identified(INVOBJ *io __attribute__((__unused__))) { return(1); } /* * The identify method for maps. */ static INVOBJ *identify(INVOBJ *io) { return(io); } /* * The OBJOPS vector for maps. */ OBJOPS objops_map = { &new, &old, &format, 0, 0, &collapsible, &identical, &std_split, &identified, &identify };