don't ask me why wintty.c was actually compiling before. also, make studio shut up about all the long-to-short stuff.
5231 lines
129 KiB
C
5231 lines
129 KiB
C
/* NetHack 3.5 sp_lev.c $NHDT-Date: 1427934549 2015/04/02 00:29:09 $ $NHDT-Branch: master $:$NHDT-Revision: 1.40 $ */
|
|
/* Copyright (c) 1989 by Jean-Christophe Collet */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/*
|
|
* This file contains the various functions that are related to the special
|
|
* levels.
|
|
* It contains also the special level loader.
|
|
*
|
|
*/
|
|
|
|
#include "hack.h"
|
|
#include "dlb.h"
|
|
|
|
#include "sp_lev.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4244)
|
|
#endif
|
|
|
|
typedef void (*select_iter_func)(int, int, genericptr_t);
|
|
|
|
extern void FDECL(mkmap, (lev_init *));
|
|
|
|
STATIC_DCL void FDECL(get_room_loc, (schar *, schar *, struct mkroom *));
|
|
STATIC_DCL void FDECL(get_free_room_loc, (schar *, schar *, struct mkroom *, packed_coord));
|
|
STATIC_DCL void FDECL(create_trap, (trap *, struct mkroom *));
|
|
STATIC_DCL int FDECL(noncoalignment, (ALIGNTYP_P));
|
|
STATIC_DCL boolean FDECL(m_bad_boulder_spot, (int,int));
|
|
STATIC_DCL void FDECL(create_monster, (monster *, struct mkroom *));
|
|
STATIC_DCL void FDECL(create_object, (object *, struct mkroom *));
|
|
STATIC_DCL void FDECL(create_altar, (altar *, struct mkroom *));
|
|
STATIC_DCL boolean FDECL(search_door, (struct mkroom *, xchar *, xchar *,
|
|
XCHAR_P, int));
|
|
STATIC_DCL void NDECL(fix_stair_rooms);
|
|
STATIC_DCL void FDECL(create_corridor, (corridor *));
|
|
STATIC_DCL void NDECL(count_features);
|
|
|
|
STATIC_DCL boolean FDECL(create_subroom, (struct mkroom *, XCHAR_P, XCHAR_P,
|
|
XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P));
|
|
|
|
long FDECL(opvar_array_length, (struct sp_coder *));
|
|
STATIC_DCL void NDECL(solidify_map);
|
|
STATIC_DCL void FDECL(splev_stack_init, (struct splevstack *));
|
|
STATIC_DCL void FDECL(splev_stack_done, (struct splevstack *));
|
|
STATIC_DCL void FDECL(splev_stack_push, (struct splevstack *, struct opvar *));
|
|
STATIC_DCL struct opvar * FDECL(splev_stack_pop, (struct splevstack *));
|
|
STATIC_DCL struct splevstack * FDECL(splev_stack_reverse, (struct splevstack *));
|
|
STATIC_DCL struct opvar * FDECL(opvar_new_str, (char *));
|
|
STATIC_DCL struct opvar * FDECL(opvar_new_int, (long));
|
|
STATIC_DCL struct opvar * FDECL(opvar_new_coord, (int, int));
|
|
STATIC_DCL struct opvar * FDECL(opvar_new_region, (int,int, int,int));
|
|
STATIC_DCL void FDECL(opvar_free_x, (struct opvar *));
|
|
STATIC_DCL struct opvar * FDECL(opvar_clone, (struct opvar *));
|
|
STATIC_DCL struct opvar * FDECL(opvar_var_conversion, (struct sp_coder *, struct opvar *));
|
|
STATIC_DCL struct splev_var * FDECL(opvar_var_defined, (struct sp_coder *, char *));
|
|
STATIC_DCL struct opvar * FDECL(splev_stack_getdat, (struct sp_coder *, XCHAR_P));
|
|
STATIC_DCL struct opvar * FDECL(splev_stack_getdat_any, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(variable_list_del, (struct splev_var *));
|
|
STATIC_DCL void FDECL(lvlfill_maze_grid, (int,int, int,int, SCHAR_P));
|
|
STATIC_DCL void FDECL(lvlfill_solid, (SCHAR_P, SCHAR_P));
|
|
STATIC_DCL void NDECL(remove_boundary_syms);
|
|
STATIC_DCL void FDECL(maybe_add_door, (int,int, struct mkroom *));
|
|
STATIC_DCL void NDECL(link_doors_rooms);
|
|
STATIC_DCL void NDECL(fill_rooms);
|
|
STATIC_DCL unpacked_coord FDECL(get_unpacked_coord, (long, int));
|
|
STATIC_DCL void FDECL(replace_terrain, (replaceterrain *, struct mkroom *));
|
|
STATIC_DCL void FDECL(wallify_map, (int,int, int,int));
|
|
STATIC_DCL void FDECL(splev_initlev, (lev_init *));
|
|
STATIC_DCL struct sp_frame * FDECL(frame_new, (long));
|
|
STATIC_DCL void FDECL(frame_del, (struct sp_frame *));
|
|
STATIC_DCL void FDECL(spo_frame_push, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_frame_pop, (struct sp_coder *));
|
|
STATIC_DCL long FDECL(sp_code_jmpaddr, (long, long));
|
|
STATIC_DCL void FDECL(spo_call, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_return, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_end_moninvent, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_pop_container, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_message, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_monster, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_object, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_level_flags, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_initlevel, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_engraving, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_mineralize, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_room, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_endroom, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_stair, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_ladder, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_grave, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_altar, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_trap, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_gold, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_corridor, (struct sp_coder *));
|
|
STATIC_DCL struct opvar * FDECL(selection_opvar, (char *));
|
|
STATIC_DCL xchar FDECL(selection_getpoint, (int,int, struct opvar *));
|
|
STATIC_DCL void FDECL(selection_setpoint, (int,int, struct opvar *, XCHAR_P));
|
|
STATIC_DCL struct opvar * FDECL(selection_not, (struct opvar *));
|
|
STATIC_DCL struct opvar * FDECL(selection_logical_oper, (struct opvar *, struct opvar *, CHAR_P));
|
|
STATIC_DCL struct opvar * FDECL(selection_filter_mapchar, (struct opvar *, struct opvar *));
|
|
STATIC_DCL void FDECL(selection_filter_percent, (struct opvar *, int));
|
|
STATIC_DCL int FDECL(selection_rndcoord, (struct opvar *, schar *, schar *));
|
|
STATIC_DCL void FDECL(selection_do_grow, (struct opvar *, int));
|
|
STATIC_DCL void FDECL(selection_floodfill, (struct opvar *, int,int));
|
|
STATIC_DCL void FDECL(selection_do_ellipse, (struct opvar *, int,int, int,int, int));
|
|
STATIC_DCL long FDECL(line_dist_coord, (long,long, long,long, long,long));
|
|
STATIC_DCL void FDECL(selection_do_gradient, (struct opvar *, long,long, long,long, long,long,long,long));
|
|
STATIC_DCL void FDECL(selection_do_line, (SCHAR_P,SCHAR_P, SCHAR_P,SCHAR_P, struct opvar *));
|
|
STATIC_DCL void FDECL(selection_do_randline, (SCHAR_P,SCHAR_P, SCHAR_P,SCHAR_P, SCHAR_P,SCHAR_P, struct opvar *));
|
|
STATIC_DCL void FDECL(selection_iterate, (struct opvar *, select_iter_func, genericptr_t));
|
|
STATIC_DCL void FDECL(sel_set_ter, (int,int, genericptr_t));
|
|
STATIC_DCL void FDECL(sel_set_feature, (int,int, genericptr_t));
|
|
STATIC_DCL void FDECL(sel_set_door, (int,int, genericptr_t));
|
|
STATIC_DCL void FDECL(spo_door, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_feature, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_terrain, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_replace_terrain, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_levregion, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_region, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_drawbridge, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_mazewalk, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_wall_property, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_room_door, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(sel_set_wallify, (int,int, genericptr_t));
|
|
STATIC_DCL void FDECL(spo_wallify, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_map, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_jmp, (struct sp_coder *, sp_lev *));
|
|
STATIC_DCL void FDECL(spo_conditional_jump, (struct sp_coder *, sp_lev *));
|
|
STATIC_DCL void FDECL(spo_var_init, (struct sp_coder *));
|
|
STATIC_DCL void FDECL(spo_shuffle_array, (struct sp_coder *));
|
|
|
|
#define LEFT 1
|
|
#define H_LEFT 2
|
|
#define CENTER 3
|
|
#define H_RIGHT 4
|
|
#define RIGHT 5
|
|
|
|
#define TOP 1
|
|
#define BOTTOM 5
|
|
|
|
#define sq(x) ((x)*(x))
|
|
|
|
#define XLIM 4
|
|
#define YLIM 3
|
|
|
|
#define Fread (void)dlb_fread
|
|
#define Fgetc (schar)dlb_fgetc
|
|
#define New(type) (type *) alloc(sizeof(type))
|
|
#define NewTab(type, size) (type **) alloc(sizeof(type *) * (unsigned)size)
|
|
#define Free(ptr) if(ptr) free((genericptr_t) (ptr))
|
|
|
|
extern struct engr *head_engr;
|
|
|
|
extern int min_rx, max_rx, min_ry, max_ry; /* from mkmap.c */
|
|
|
|
/* positions touched by level elements explicitly defined in the des-file */
|
|
static char SpLev_Map[COLNO][ROWNO];
|
|
|
|
static aligntyp ralign[3] = { AM_CHAOTIC, AM_NEUTRAL, AM_LAWFUL };
|
|
static NEARDATA xchar xstart, ystart;
|
|
static NEARDATA char xsize, ysize;
|
|
|
|
STATIC_DCL void FDECL(set_wall_property, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,int));
|
|
STATIC_DCL int NDECL(rnddoor);
|
|
STATIC_DCL int NDECL(rndtrap);
|
|
STATIC_DCL void FDECL(get_location, (schar *,schar *,int, struct mkroom *));
|
|
STATIC_DCL void FDECL(light_region, (region *));
|
|
STATIC_DCL void FDECL(maze1xy, (coord *,int));
|
|
STATIC_DCL boolean FDECL(sp_level_loader, (dlb *, sp_lev *));
|
|
STATIC_DCL void FDECL(create_door, (room_door *, struct mkroom *));
|
|
struct mkroom * FDECL(build_room, (room *, struct mkroom *));
|
|
|
|
char *lev_message = 0;
|
|
lev_region *lregions = 0;
|
|
int num_lregions = 0;
|
|
boolean splev_init_present = FALSE;
|
|
boolean icedpools = FALSE;
|
|
|
|
struct obj *container_obj[MAX_CONTAINMENT];
|
|
int container_idx = 0;
|
|
|
|
struct monst *invent_carrying_monster = NULL;
|
|
|
|
#define SPLEV_STACK_RESERVE 128
|
|
|
|
void
|
|
solidify_map()
|
|
{
|
|
xchar x, y;
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
if (IS_STWALL(levl[x][y].typ) && !SpLev_Map[x][y])
|
|
levl[x][y].wall_info |= (W_NONDIGGABLE|W_NONPASSWALL);
|
|
}
|
|
|
|
void
|
|
splev_stack_init(st)
|
|
struct splevstack *st;
|
|
{
|
|
if (st) {
|
|
st->depth = 0;
|
|
st->depth_alloc = SPLEV_STACK_RESERVE;
|
|
st->stackdata = (struct opvar **)alloc(st->depth_alloc * sizeof(struct opvar *));
|
|
if (!st->stackdata) panic("stack init alloc");
|
|
}
|
|
}
|
|
|
|
void
|
|
splev_stack_done(st)
|
|
struct splevstack *st;
|
|
{
|
|
if (st) {
|
|
int i;
|
|
|
|
if (st->stackdata && st->depth)
|
|
for (i = 0; i < st->depth; i++) {
|
|
switch (st->stackdata[i]->spovartyp) {
|
|
default:
|
|
case SPOVAR_NULL:
|
|
case SPOVAR_COORD:
|
|
case SPOVAR_REGION:
|
|
case SPOVAR_MAPCHAR:
|
|
case SPOVAR_MONST:
|
|
case SPOVAR_OBJ:
|
|
case SPOVAR_INT:
|
|
break;
|
|
case SPOVAR_VARIABLE:
|
|
case SPOVAR_STRING:
|
|
case SPOVAR_SEL:
|
|
if (st->stackdata[i]->vardata.str) Free(st->stackdata[i]->vardata.str);
|
|
st->stackdata[i]->vardata.str = NULL;
|
|
break;
|
|
}
|
|
Free(st->stackdata[i]);
|
|
st->stackdata[i] = NULL;
|
|
}
|
|
|
|
Free(st->stackdata);
|
|
st->stackdata = NULL;
|
|
st->depth = st->depth_alloc = 0;
|
|
Free(st);
|
|
}
|
|
}
|
|
|
|
void
|
|
splev_stack_push(st, v)
|
|
struct splevstack *st;
|
|
struct opvar *v;
|
|
{
|
|
if (!st || !v) return;
|
|
if (!st->stackdata) panic("splev_stack_push: no stackdata allocated?");
|
|
|
|
if (st->depth >= st->depth_alloc) {
|
|
struct opvar **tmp = (struct opvar **)alloc((st->depth_alloc + SPLEV_STACK_RESERVE) * sizeof(struct opvar *));
|
|
if (!tmp) panic("stack push alloc");
|
|
(void)memcpy(tmp, st->stackdata, st->depth_alloc * sizeof(struct opvar *));
|
|
Free(st->stackdata);
|
|
st->stackdata = tmp;
|
|
st->depth_alloc += SPLEV_STACK_RESERVE;
|
|
}
|
|
|
|
st->stackdata[st->depth] = v;
|
|
st->depth++;
|
|
}
|
|
|
|
struct opvar *
|
|
splev_stack_pop(st)
|
|
struct splevstack *st;
|
|
{
|
|
struct opvar *ret = NULL;
|
|
if (!st) return ret;
|
|
if (!st->stackdata) panic("splev_stack_pop: no stackdata allocated?");
|
|
|
|
if (st->depth) {
|
|
st->depth--;
|
|
ret = st->stackdata[st->depth];
|
|
st->stackdata[st->depth] = NULL;
|
|
return ret;
|
|
} else impossible("splev_stack_pop: empty stack?");
|
|
return ret;
|
|
}
|
|
|
|
struct splevstack *
|
|
splev_stack_reverse(st)
|
|
struct splevstack *st;
|
|
{
|
|
long i;
|
|
struct opvar *tmp;
|
|
if (!st) return NULL;
|
|
if (!st->stackdata) panic("splev_stack_reverse: no stackdata allocated?");
|
|
for (i = 0; i < (st->depth / 2); i++) {
|
|
tmp = st->stackdata[i];
|
|
st->stackdata[i] = st->stackdata[st->depth - i - 1];
|
|
st->stackdata[st->depth - i - 1] = tmp;
|
|
}
|
|
return st;
|
|
}
|
|
|
|
#define OV_typ(o) (o->spovartyp)
|
|
#define OV_i(o) (o->vardata.l)
|
|
#define OV_s(o) (o->vardata.str)
|
|
|
|
#define OV_pop_i(x) (x = splev_stack_getdat(coder, SPOVAR_INT))
|
|
#define OV_pop_c(x) (x = splev_stack_getdat(coder, SPOVAR_COORD))
|
|
#define OV_pop_r(x) (x = splev_stack_getdat(coder, SPOVAR_REGION))
|
|
#define OV_pop_s(x) (x = splev_stack_getdat(coder, SPOVAR_STRING))
|
|
#define OV_pop(x) (x = splev_stack_getdat_any(coder))
|
|
#define OV_pop_typ(x,typ) (x = splev_stack_getdat(coder, typ))
|
|
|
|
|
|
struct opvar *
|
|
opvar_new_str(s)
|
|
char *s;
|
|
{
|
|
struct opvar *tmpov = (struct opvar *)alloc(sizeof(struct opvar));
|
|
if (!tmpov) panic("could not alloc opvar struct");
|
|
tmpov->spovartyp = SPOVAR_STRING;
|
|
if (s) {
|
|
int len = strlen(s);
|
|
tmpov->vardata.str = (char *)alloc(len + 1);
|
|
if (!tmpov->vardata.str) panic("opvar new str alloc");
|
|
(void)memcpy((genericptr_t)tmpov->vardata.str,
|
|
(genericptr_t)s, len);
|
|
tmpov->vardata.str[len] = '\0';
|
|
} else
|
|
tmpov->vardata.str = NULL;
|
|
return tmpov;
|
|
}
|
|
|
|
struct opvar *
|
|
opvar_new_int(i)
|
|
long i;
|
|
{
|
|
struct opvar *tmpov = (struct opvar *)alloc(sizeof(struct opvar));
|
|
if (!tmpov) panic("could not alloc opvar struct");
|
|
tmpov->spovartyp = SPOVAR_INT;
|
|
tmpov->vardata.l = i;
|
|
return tmpov;
|
|
}
|
|
|
|
struct opvar *
|
|
opvar_new_coord(x,y)
|
|
int x,y;
|
|
{
|
|
struct opvar *tmpov = (struct opvar *)alloc(sizeof(struct opvar));
|
|
if (!tmpov) panic("could not alloc opvar struct");
|
|
tmpov->spovartyp = SPOVAR_COORD;
|
|
tmpov->vardata.l = SP_COORD_PACK(x,y);
|
|
return tmpov;
|
|
}
|
|
|
|
struct opvar *
|
|
opvar_new_region(x1,y1,x2,y2)
|
|
int x1,y1,x2,y2;
|
|
{
|
|
struct opvar *tmpov = (struct opvar *)alloc(sizeof(struct opvar));
|
|
if (!tmpov) panic("could not alloc opvar struct");
|
|
tmpov->spovartyp = SPOVAR_REGION;
|
|
tmpov->vardata.l = SP_REGION_PACK(x1,y1,x2,y2);
|
|
return tmpov;
|
|
}
|
|
|
|
void
|
|
opvar_free_x(ov)
|
|
struct opvar *ov;
|
|
{
|
|
if (!ov) return;
|
|
switch (ov->spovartyp) {
|
|
case SPOVAR_COORD:
|
|
case SPOVAR_REGION:
|
|
case SPOVAR_MAPCHAR:
|
|
case SPOVAR_MONST:
|
|
case SPOVAR_OBJ:
|
|
case SPOVAR_INT:
|
|
break;
|
|
case SPOVAR_VARIABLE:
|
|
case SPOVAR_STRING:
|
|
case SPOVAR_SEL:
|
|
Free(ov->vardata.str);
|
|
break;
|
|
default: impossible("Unknown opvar value type (%i)!", ov->spovartyp);
|
|
}
|
|
Free(ov);
|
|
}
|
|
|
|
#define opvar_free(ov) { if (ov) { opvar_free_x(ov); ov = NULL; } else impossible("opvar_free(), %s", __FUNCTION__); }
|
|
|
|
struct opvar *
|
|
opvar_clone(ov)
|
|
struct opvar *ov;
|
|
{
|
|
struct opvar *tmpov;
|
|
if (!ov) panic("no opvar to clone");
|
|
tmpov = (struct opvar *)alloc(sizeof(struct opvar));
|
|
if (!tmpov) panic("could not alloc opvar struct");
|
|
tmpov->spovartyp = ov->spovartyp;
|
|
switch (ov->spovartyp) {
|
|
case SPOVAR_COORD:
|
|
case SPOVAR_REGION:
|
|
case SPOVAR_MAPCHAR:
|
|
case SPOVAR_MONST:
|
|
case SPOVAR_OBJ:
|
|
case SPOVAR_INT:
|
|
tmpov->vardata.l = ov->vardata.l;
|
|
break;
|
|
case SPOVAR_VARIABLE:
|
|
case SPOVAR_STRING:
|
|
case SPOVAR_SEL:
|
|
tmpov->vardata.str = strdup(ov->vardata.str);
|
|
break;
|
|
default: impossible("Unknown push value type (%i)!", ov->spovartyp);
|
|
}
|
|
return tmpov;
|
|
}
|
|
|
|
|
|
struct opvar *
|
|
opvar_var_conversion(coder, ov)
|
|
struct sp_coder *coder;
|
|
struct opvar *ov;
|
|
{
|
|
struct splev_var *tmp;
|
|
struct opvar *tmpov;
|
|
struct opvar *array_idx = NULL;
|
|
if (!coder || !ov) return NULL;
|
|
if (ov->spovartyp != SPOVAR_VARIABLE) return ov;
|
|
tmp = coder->frame->variables;
|
|
while (tmp) {
|
|
if (!strcmp(tmp->name, OV_s(ov))) {
|
|
if ((tmp->svtyp & SPOVAR_ARRAY)) {
|
|
array_idx = opvar_var_conversion(coder, splev_stack_pop(coder->stack));
|
|
if (!array_idx || OV_typ(array_idx) != SPOVAR_INT)
|
|
panic("array idx not an int");
|
|
if (tmp->array_len < 1) panic("array len < 1");
|
|
OV_i(array_idx) = (OV_i(array_idx) % tmp->array_len);
|
|
tmpov = opvar_clone(tmp->data.arrayvalues[OV_i(array_idx)]);
|
|
return tmpov;
|
|
} else {
|
|
tmpov = opvar_clone(tmp->data.value);
|
|
return tmpov;
|
|
}
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct splev_var *
|
|
opvar_var_defined(coder, name)
|
|
struct sp_coder *coder;
|
|
char *name;
|
|
{
|
|
struct splev_var *tmp;
|
|
if (!coder) return NULL;
|
|
tmp = coder->frame->variables;
|
|
while (tmp) {
|
|
if (!strcmp(tmp->name, name)) return tmp;
|
|
tmp = tmp->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct opvar *
|
|
splev_stack_getdat(coder, typ)
|
|
struct sp_coder *coder;
|
|
xchar typ;
|
|
{
|
|
if (coder && coder->stack) {
|
|
struct opvar *tmp = splev_stack_pop(coder->stack);
|
|
if (!tmp) panic("no value type %i in stack.", typ);
|
|
if (tmp->spovartyp == SPOVAR_VARIABLE)
|
|
tmp = opvar_var_conversion(coder, tmp);
|
|
if (tmp->spovartyp == typ)
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct opvar *
|
|
splev_stack_getdat_any(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
if (coder && coder->stack) {
|
|
struct opvar *tmp = splev_stack_pop(coder->stack);
|
|
if (tmp && tmp->spovartyp == SPOVAR_VARIABLE)
|
|
tmp = opvar_var_conversion(coder, tmp);
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
variable_list_del(varlist)
|
|
struct splev_var *varlist;
|
|
{
|
|
struct splev_var *tmp = varlist;
|
|
if (!tmp) return;
|
|
while (tmp) {
|
|
Free(tmp->name);
|
|
if ((tmp->svtyp & SPOVAR_ARRAY)) {
|
|
long idx = tmp->array_len;
|
|
while (idx-- > 0) {
|
|
opvar_free(tmp->data.arrayvalues[idx]);
|
|
};
|
|
Free(tmp->data.arrayvalues);
|
|
} else {
|
|
opvar_free(tmp->data.value);
|
|
}
|
|
tmp = varlist->next;
|
|
Free(varlist);
|
|
varlist = tmp;
|
|
}
|
|
}
|
|
|
|
void
|
|
lvlfill_maze_grid(x1,y1,x2,y2,filling)
|
|
int x1,y1,x2,y2;
|
|
schar filling;
|
|
{
|
|
int x,y;
|
|
|
|
for (x = x1; x <= x2; x++)
|
|
for (y = y1; y <= y2; y++) {
|
|
#ifndef WALLIFIED_MAZE
|
|
levl[x][y].typ = STONE;
|
|
#else
|
|
levl[x][y].typ =
|
|
(y < 2 || ((x % 2) && (y % 2))) ? STONE : filling;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
lvlfill_solid(filling,lit)
|
|
schar filling;
|
|
schar lit;
|
|
{
|
|
int x,y;
|
|
for (x = 2; x <= x_maze_max; x++)
|
|
for (y = 0; y <= y_maze_max; y++) {
|
|
SET_TYPLIT(x,y,filling,lit);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Make walls of the area (x1, y1, x2, y2) non diggable/non passwall-able
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
set_wall_property(x1,y1,x2,y2, prop)
|
|
xchar x1, y1, x2, y2;
|
|
int prop;
|
|
{
|
|
register xchar x, y;
|
|
|
|
for(y = max(y1,0); y <= min(y2, ROWNO-1); y++)
|
|
for(x = max(x1,0); x <= min(x2,COLNO-1); x++)
|
|
if(IS_STWALL(levl[x][y].typ) || IS_TREE(levl[x][y].typ))
|
|
levl[x][y].wall_info |= prop;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
shuffle_alignments()
|
|
{
|
|
int i;
|
|
aligntyp atmp;
|
|
/* shuffle 3 alignments */
|
|
i = rn2(3); atmp=ralign[2]; ralign[2]=ralign[i]; ralign[i]=atmp;
|
|
if (rn2(2)) { atmp=ralign[1]; ralign[1]=ralign[0]; ralign[0]=atmp; }
|
|
}
|
|
|
|
/*
|
|
* Count the different features (sinks, fountains) in the level.
|
|
*/
|
|
STATIC_OVL void
|
|
count_features()
|
|
{
|
|
xchar x,y;
|
|
level.flags.nfountains = level.flags.nsinks = 0;
|
|
for (y = 0; y < ROWNO; y++)
|
|
for (x = 0; x < COLNO; x++) {
|
|
int typ = levl[x][y].typ;
|
|
if (typ == FOUNTAIN)
|
|
level.flags.nfountains++;
|
|
else if (typ == SINK)
|
|
level.flags.nsinks++;
|
|
}
|
|
}
|
|
|
|
void
|
|
remove_boundary_syms()
|
|
{
|
|
/*
|
|
* If any CROSSWALLs are found, must change to ROOM after REGION's
|
|
* are laid out. CROSSWALLS are used to specify "invisible"
|
|
* boundaries where DOOR syms look bad or aren't desirable.
|
|
*/
|
|
xchar x,y;
|
|
boolean has_bounds = FALSE;
|
|
for (x = 0; x < COLNO-1; x++)
|
|
for (y = 0; y < ROWNO-1; y++)
|
|
if (levl[x][y].typ == CROSSWALL) {
|
|
has_bounds = TRUE;
|
|
break;
|
|
}
|
|
if (has_bounds) {
|
|
for(x = 0; x < x_maze_max; x++)
|
|
for(y = 0; y < y_maze_max; y++)
|
|
if ((levl[x][y].typ == CROSSWALL) && SpLev_Map[x][y])
|
|
levl[x][y].typ = ROOM;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
maybe_add_door(x,y, droom)
|
|
int x,y;
|
|
struct mkroom *droom;
|
|
{
|
|
if (droom->hx >= 0 && doorindex < DOORMAX && inside_room(droom, x,y))
|
|
add_door(x, y, droom);
|
|
}
|
|
|
|
void
|
|
link_doors_rooms()
|
|
{
|
|
int x,y;
|
|
int tmpi, m;
|
|
for (y = 0; y < ROWNO; y++)
|
|
for (x = 0; x < COLNO; x++)
|
|
if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) {
|
|
for (tmpi = 0; tmpi < nroom; tmpi++) {
|
|
maybe_add_door(x,y, &rooms[tmpi]);
|
|
for (m = 0; m < rooms[tmpi].nsubrooms; m++) {
|
|
maybe_add_door(x,y, rooms[tmpi].sbrooms[m]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
fill_rooms()
|
|
{
|
|
int tmpi;
|
|
for (tmpi = 0; tmpi < nroom; tmpi++) {
|
|
int m;
|
|
if (rooms[tmpi].needfill)
|
|
fill_room(&rooms[tmpi], (rooms[tmpi].needfill == 2));
|
|
for (m = 0; m < rooms[tmpi].nsubrooms; m++)
|
|
if (rooms[tmpi].sbrooms[m]->needfill)
|
|
fill_room(rooms[tmpi].sbrooms[m], FALSE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Choose randomly the state (nodoor, open, closed or locked) for a door
|
|
*/
|
|
STATIC_OVL int
|
|
rnddoor()
|
|
{
|
|
int i = 1 << rn2(5);
|
|
i >>= 1;
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Select a random trap
|
|
*/
|
|
STATIC_OVL int
|
|
rndtrap()
|
|
{
|
|
int rtrap;
|
|
|
|
do {
|
|
rtrap = rnd(TRAPNUM-1);
|
|
switch (rtrap) {
|
|
case HOLE: /* no random holes on special levels */
|
|
case MAGIC_PORTAL: rtrap = NO_TRAP;
|
|
break;
|
|
case TRAPDOOR: if (!Can_dig_down(&u.uz)) rtrap = NO_TRAP;
|
|
break;
|
|
case LEVEL_TELEP:
|
|
case TELEP_TRAP: if (level.flags.noteleport) rtrap = NO_TRAP;
|
|
break;
|
|
case ROLLING_BOULDER_TRAP:
|
|
case ROCKTRAP: if (In_endgame(&u.uz)) rtrap = NO_TRAP;
|
|
break;
|
|
}
|
|
} while (rtrap == NO_TRAP);
|
|
return rtrap;
|
|
}
|
|
|
|
/*
|
|
* Coordinates in special level files are handled specially:
|
|
*
|
|
* if x or y is < 0, we generate a random coordinate.
|
|
* The "humidity" flag is used to insure that engravings aren't
|
|
* created underwater, or eels on dry land.
|
|
*/
|
|
STATIC_DCL boolean FDECL(is_ok_location, (SCHAR_P, SCHAR_P, int));
|
|
|
|
STATIC_OVL void
|
|
get_location(x, y, humidity, croom)
|
|
schar *x, *y;
|
|
int humidity;
|
|
struct mkroom *croom;
|
|
{
|
|
int cpt = 0;
|
|
int mx, my, sx, sy;
|
|
|
|
if (croom) {
|
|
mx = croom->lx;
|
|
my = croom->ly;
|
|
sx = croom->hx - mx + 1;
|
|
sy = croom->hy - my + 1;
|
|
} else {
|
|
mx = xstart;
|
|
my = ystart;
|
|
sx = xsize;
|
|
sy = ysize;
|
|
}
|
|
|
|
if (*x >= 0) { /* normal locations */
|
|
*x += mx;
|
|
*y += my;
|
|
} else { /* random location */
|
|
do {
|
|
if (croom) { /* handle irregular areas */
|
|
coord tmpc;
|
|
somexy(croom, &tmpc);
|
|
*x = tmpc.x;
|
|
*y = tmpc.y;
|
|
} else {
|
|
*x = mx + rn2((int)sx);
|
|
*y = my + rn2((int)sy);
|
|
}
|
|
if (is_ok_location(*x,*y,humidity)) break;
|
|
} while (++cpt < 100);
|
|
if (cpt >= 100) {
|
|
register int xx, yy;
|
|
/* last try */
|
|
for (xx = 0; xx < sx; xx++)
|
|
for (yy = 0; yy < sy; yy++) {
|
|
*x = mx + xx;
|
|
*y = my + yy;
|
|
if (is_ok_location(*x,*y,humidity)) goto found_it;
|
|
}
|
|
if (!(humidity & NO_LOC_WARN)) {
|
|
impossible("get_location: can't find a place!");
|
|
} else {
|
|
*x = *y = -1;
|
|
}
|
|
}
|
|
}
|
|
found_it:;
|
|
|
|
if (!(humidity & ANY_LOC) && !isok(*x,*y)) {
|
|
if (!(humidity & NO_LOC_WARN)) {
|
|
/*warning("get_location: (%d,%d) out of bounds", *x, *y);*/
|
|
*x = x_maze_max; *y = y_maze_max;
|
|
} else {
|
|
*x = *y = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC_OVL boolean
|
|
is_ok_location(x, y, humidity)
|
|
register schar x, y;
|
|
register int humidity;
|
|
{
|
|
register int typ;
|
|
|
|
if (Is_waterlevel(&u.uz)) return TRUE; /* accept any spot */
|
|
|
|
/* TODO: Should perhaps check if wall is diggable/passwall? */
|
|
if (humidity & ANY_LOC) return TRUE;
|
|
|
|
if ((humidity & SOLID) && IS_ROCK(levl[x][y].typ)) return TRUE;
|
|
|
|
if (humidity & DRY) {
|
|
typ = levl[x][y].typ;
|
|
if (typ == ROOM || typ == AIR ||
|
|
typ == CLOUD || typ == ICE || typ == CORR)
|
|
return TRUE;
|
|
}
|
|
if ((humidity & WET) && is_pool(x,y)) return TRUE;
|
|
if ((humidity & HOT) && is_lava(x,y)) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
unpacked_coord
|
|
get_unpacked_coord(loc, defhumidity)
|
|
long loc;
|
|
int defhumidity;
|
|
{
|
|
static unpacked_coord c;
|
|
|
|
if (loc & SP_COORD_IS_RANDOM) {
|
|
c.x = c.y = -1;
|
|
c.is_random = 1;
|
|
c.getloc_flags = (loc & ~SP_COORD_IS_RANDOM);
|
|
if (!c.getloc_flags) c.getloc_flags = defhumidity;
|
|
} else {
|
|
c.is_random = 0;
|
|
c.getloc_flags = defhumidity;
|
|
c.x = SP_COORD_X(loc);
|
|
c.y = SP_COORD_Y(loc);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
get_location_coord(x, y, humidity, croom, crd)
|
|
schar *x, *y;
|
|
int humidity;
|
|
struct mkroom *croom;
|
|
long crd;
|
|
{
|
|
unpacked_coord c;
|
|
c = get_unpacked_coord(crd, humidity);
|
|
*x = c.x;
|
|
*y = c.y;
|
|
get_location(x, y, c.getloc_flags | (c.is_random ? NO_LOC_WARN : 0), croom);
|
|
if (*x == -1 && *y == -1 && c.is_random)
|
|
get_location(x,y, humidity, croom);
|
|
}
|
|
|
|
/*
|
|
* Get a relative position inside a room.
|
|
* negative values for x or y means RANDOM!
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
get_room_loc(x,y, croom)
|
|
schar *x, *y;
|
|
struct mkroom *croom;
|
|
{
|
|
coord c;
|
|
|
|
if (*x <0 && *y <0) {
|
|
if (somexy(croom, &c)) {
|
|
*x = c.x;
|
|
*y = c.y;
|
|
} else
|
|
panic("get_room_loc : can't find a place!");
|
|
} else {
|
|
if (*x < 0)
|
|
*x = rn2(croom->hx - croom->lx + 1);
|
|
if (*y < 0)
|
|
*y = rn2(croom->hy - croom->ly + 1);
|
|
*x += croom->lx;
|
|
*y += croom->ly;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get a relative position inside a room.
|
|
* negative values for x or y means RANDOM!
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
get_free_room_loc(x,y, croom, pos)
|
|
schar *x, *y;
|
|
struct mkroom *croom;
|
|
packed_coord pos;
|
|
{
|
|
schar try_x, try_y;
|
|
register int trycnt = 0;
|
|
|
|
get_location_coord(&try_x, &try_y, DRY, croom, pos);
|
|
if (levl[try_x][try_y].typ != ROOM) {
|
|
do {
|
|
try_x = *x, try_y = *y;
|
|
get_room_loc(&try_x, &try_y, croom);
|
|
} while (levl[try_x][try_y].typ != ROOM && ++trycnt <= 100);
|
|
|
|
if (trycnt > 100)
|
|
panic("get_free_room_loc: can't find a place!");
|
|
}
|
|
*x = try_x, *y = try_y;
|
|
}
|
|
|
|
boolean
|
|
check_room(lowx, ddx, lowy, ddy, vault)
|
|
xchar *lowx, *ddx, *lowy, *ddy;
|
|
boolean vault;
|
|
{
|
|
register int x,y,hix = *lowx + *ddx, hiy = *lowy + *ddy;
|
|
register struct rm *lev;
|
|
int xlim, ylim, ymax;
|
|
|
|
xlim = XLIM + (vault ? 1 : 0);
|
|
ylim = YLIM + (vault ? 1 : 0);
|
|
|
|
if (*lowx < 3) *lowx = 3;
|
|
if (*lowy < 2) *lowy = 2;
|
|
if (hix > COLNO-3) hix = COLNO-3;
|
|
if (hiy > ROWNO-3) hiy = ROWNO-3;
|
|
chk:
|
|
if (hix <= *lowx || hiy <= *lowy) return FALSE;
|
|
|
|
/* check area around room (and make room smaller if necessary) */
|
|
for (x = *lowx - xlim; x<= hix + xlim; x++) {
|
|
if(x <= 0 || x >= COLNO) continue;
|
|
y = *lowy - ylim; ymax = hiy + ylim;
|
|
if(y < 0) y = 0;
|
|
if(ymax >= ROWNO) ymax = (ROWNO-1);
|
|
lev = &levl[x][y];
|
|
for (; y <= ymax; y++) {
|
|
if (lev++->typ) {
|
|
if (!vault)
|
|
debugpline2("strange area [%d,%d] in check_room.", x, y);
|
|
if (!rn2(3)) return FALSE;
|
|
if (x < *lowx)
|
|
*lowx = x + xlim + 1;
|
|
else
|
|
hix = x - xlim - 1;
|
|
if (y < *lowy)
|
|
*lowy = y + ylim + 1;
|
|
else
|
|
hiy = y - ylim - 1;
|
|
goto chk;
|
|
}
|
|
}
|
|
}
|
|
*ddx = hix - *lowx;
|
|
*ddy = hiy - *lowy;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Create a new room.
|
|
* This is still very incomplete...
|
|
*/
|
|
|
|
boolean
|
|
create_room(x,y,w,h,xal,yal,rtype,rlit)
|
|
xchar x,y;
|
|
xchar w,h;
|
|
xchar xal,yal;
|
|
xchar rtype, rlit;
|
|
{
|
|
xchar xabs, yabs;
|
|
int wtmp, htmp, xaltmp, yaltmp, xtmp, ytmp;
|
|
NhRect *r1 = 0, r2;
|
|
int trycnt = 0;
|
|
boolean vault = FALSE;
|
|
int xlim = XLIM, ylim = YLIM;
|
|
|
|
if (rtype == -1) /* Is the type random ? */
|
|
rtype = OROOM;
|
|
|
|
if (rtype == VAULT) {
|
|
vault = TRUE;
|
|
xlim++;
|
|
ylim++;
|
|
}
|
|
|
|
/* on low levels the room is lit (usually) */
|
|
/* some other rooms may require lighting */
|
|
|
|
/* is light state random ? */
|
|
if (rlit == -1)
|
|
rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? TRUE : FALSE;
|
|
|
|
/*
|
|
* Here we will try to create a room. If some parameters are
|
|
* random we are willing to make several try before we give
|
|
* it up.
|
|
*/
|
|
do {
|
|
xchar xborder, yborder;
|
|
wtmp = w; htmp = h;
|
|
xtmp = x; ytmp = y;
|
|
xaltmp = xal; yaltmp = yal;
|
|
|
|
/* First case : a totally random room */
|
|
|
|
if((xtmp < 0 && ytmp <0 && wtmp < 0 && xaltmp < 0 &&
|
|
yaltmp < 0) || vault) {
|
|
xchar hx, hy, lx, ly, dx, dy;
|
|
r1 = rnd_rect(); /* Get a random rectangle */
|
|
|
|
if (!r1) { /* No more free rectangles ! */
|
|
debugpline0("No more rects...");
|
|
return FALSE;
|
|
}
|
|
hx = r1->hx;
|
|
hy = r1->hy;
|
|
lx = r1->lx;
|
|
ly = r1->ly;
|
|
if (vault)
|
|
dx = dy = 1;
|
|
else {
|
|
dx = 2 + rn2((hx-lx > 28) ? 12 : 8);
|
|
dy = 2 + rn2(4);
|
|
if(dx*dy > 50)
|
|
dy = 50/dx;
|
|
}
|
|
xborder = (lx > 0 && hx < COLNO -1) ? 2*xlim : xlim+1;
|
|
yborder = (ly > 0 && hy < ROWNO -1) ? 2*ylim : ylim+1;
|
|
if(hx-lx < dx + 3 + xborder ||
|
|
hy-ly < dy + 3 + yborder) {
|
|
r1 = 0;
|
|
continue;
|
|
}
|
|
xabs = lx + (lx > 0 ? xlim : 3)
|
|
+ rn2(hx - (lx>0?lx : 3) - dx - xborder + 1);
|
|
yabs = ly + (ly > 0 ? ylim : 2)
|
|
+ rn2(hy - (ly>0?ly : 2) - dy - yborder + 1);
|
|
if (ly == 0 && hy >= (ROWNO-1) &&
|
|
(!nroom || !rn2(nroom)) && (yabs+dy > ROWNO/2)) {
|
|
yabs = rn1(3, 2);
|
|
if(nroom < 4 && dy>1) dy--;
|
|
}
|
|
if (!check_room(&xabs, &dx, &yabs, &dy, vault)) {
|
|
r1 = 0;
|
|
continue;
|
|
}
|
|
wtmp = dx+1;
|
|
htmp = dy+1;
|
|
r2.lx = xabs-1; r2.ly = yabs-1;
|
|
r2.hx = xabs + wtmp;
|
|
r2.hy = yabs + htmp;
|
|
} else { /* Only some parameters are random */
|
|
int rndpos = 0;
|
|
if (xtmp < 0 && ytmp < 0) { /* Position is RANDOM */
|
|
xtmp = rnd(5);
|
|
ytmp = rnd(5);
|
|
rndpos = 1;
|
|
}
|
|
if (wtmp < 0 || htmp < 0) { /* Size is RANDOM */
|
|
wtmp = rn1(15, 3);
|
|
htmp = rn1(8, 2);
|
|
}
|
|
if (xaltmp == -1) /* Horizontal alignment is RANDOM */
|
|
xaltmp = rnd(3);
|
|
if (yaltmp == -1) /* Vertical alignment is RANDOM */
|
|
yaltmp = rnd(3);
|
|
|
|
/* Try to generate real (absolute) coordinates here! */
|
|
|
|
xabs = (((xtmp-1) * COLNO) / 5) + 1;
|
|
yabs = (((ytmp-1) * ROWNO) / 5) + 1;
|
|
switch (xaltmp) {
|
|
case LEFT:
|
|
break;
|
|
case RIGHT:
|
|
xabs += (COLNO / 5) - wtmp;
|
|
break;
|
|
case CENTER:
|
|
xabs += ((COLNO / 5) - wtmp) / 2;
|
|
break;
|
|
}
|
|
switch (yaltmp) {
|
|
case TOP:
|
|
break;
|
|
case BOTTOM:
|
|
yabs += (ROWNO / 5) - htmp;
|
|
break;
|
|
case CENTER:
|
|
yabs += ((ROWNO / 5) - htmp) / 2;
|
|
break;
|
|
}
|
|
|
|
if (xabs + wtmp - 1 > COLNO - 2)
|
|
xabs = COLNO - wtmp - 3;
|
|
if (xabs < 2)
|
|
xabs = 2;
|
|
if (yabs + htmp - 1> ROWNO - 2)
|
|
yabs = ROWNO - htmp - 3;
|
|
if (yabs < 2)
|
|
yabs = 2;
|
|
|
|
/* Try to find a rectangle that fit our room ! */
|
|
|
|
r2.lx = xabs-1; r2.ly = yabs-1;
|
|
r2.hx = xabs + wtmp + rndpos;
|
|
r2.hy = yabs + htmp + rndpos;
|
|
r1 = get_rect(&r2);
|
|
}
|
|
} while (++trycnt <= 100 && !r1);
|
|
if (!r1) { /* creation of room failed ? */
|
|
return FALSE;
|
|
}
|
|
split_rects(r1, &r2);
|
|
|
|
if (!vault) {
|
|
smeq[nroom] = nroom;
|
|
add_room(xabs, yabs, xabs+wtmp-1, yabs+htmp-1,
|
|
rlit, rtype, FALSE);
|
|
} else {
|
|
rooms[nroom].lx = xabs;
|
|
rooms[nroom].ly = yabs;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Create a subroom in room proom at pos x,y with width w & height h.
|
|
* x & y are relative to the parent room.
|
|
*/
|
|
|
|
STATIC_OVL boolean
|
|
create_subroom(proom, x, y, w, h, rtype, rlit)
|
|
struct mkroom *proom;
|
|
xchar x,y;
|
|
xchar w,h;
|
|
xchar rtype, rlit;
|
|
{
|
|
xchar width, height;
|
|
|
|
width = proom->hx - proom->lx + 1;
|
|
height = proom->hy - proom->ly + 1;
|
|
|
|
/* There is a minimum size for the parent room */
|
|
if (width < 4 || height < 4)
|
|
return FALSE;
|
|
|
|
/* Check for random position, size, etc... */
|
|
|
|
if (w == -1)
|
|
w = rnd(width - 3);
|
|
if (h == -1)
|
|
h = rnd(height - 3);
|
|
if (x == -1)
|
|
x = rnd(width - w - 1) - 1;
|
|
if (y == -1)
|
|
y = rnd(height - h - 1) - 1;
|
|
if (x == 1)
|
|
x = 0;
|
|
if (y == 1)
|
|
y = 0;
|
|
if ((x + w + 1) == width)
|
|
x++;
|
|
if ((y + h + 1) == height)
|
|
y++;
|
|
if (rtype == -1)
|
|
rtype = OROOM;
|
|
if (rlit == -1)
|
|
rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? TRUE : FALSE;
|
|
add_subroom(proom, proom->lx + x, proom->ly + y,
|
|
proom->lx + x + w - 1, proom->ly + y + h - 1,
|
|
rlit, rtype, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Create a new door in a room.
|
|
* It's placed on a wall (north, south, east or west).
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
create_door(dd, broom)
|
|
room_door *dd;
|
|
struct mkroom *broom;
|
|
{
|
|
int x = 0, y = 0;
|
|
int trycnt = 0, wtry = 0;
|
|
|
|
if (dd->secret == -1)
|
|
dd->secret = rn2(2);
|
|
|
|
if (dd->mask == -1) {
|
|
/* is it a locked door, closed, or a doorway? */
|
|
if (!dd->secret) {
|
|
if(!rn2(3)) {
|
|
if(!rn2(5))
|
|
dd->mask = D_ISOPEN;
|
|
else if(!rn2(6))
|
|
dd->mask = D_LOCKED;
|
|
else
|
|
dd->mask = D_CLOSED;
|
|
if (dd->mask != D_ISOPEN && !rn2(25))
|
|
dd->mask |= D_TRAPPED;
|
|
} else
|
|
dd->mask = D_NODOOR;
|
|
} else {
|
|
if(!rn2(5)) dd->mask = D_LOCKED;
|
|
else dd->mask = D_CLOSED;
|
|
|
|
if(!rn2(20)) dd->mask |= D_TRAPPED;
|
|
}
|
|
}
|
|
|
|
do {
|
|
register int dwall, dpos;
|
|
|
|
dwall = dd->wall;
|
|
if (dwall == -1) /* The wall is RANDOM */
|
|
dwall = 1 << rn2(4);
|
|
|
|
dpos = dd->pos;
|
|
|
|
/* Convert wall and pos into an absolute coordinate! */
|
|
wtry = rn2(4);
|
|
switch (wtry) {
|
|
case 0:
|
|
if (!(dwall & W_NORTH)) goto redoloop;
|
|
y = broom->ly - 1;
|
|
x = broom->lx + ((dpos == -1) ? rn2(1+(broom->hx - broom->lx)) : dpos);
|
|
if (IS_ROCK(levl[x][y-1].typ)) goto redoloop;
|
|
goto outdirloop;
|
|
case 1:
|
|
if (!(dwall & W_SOUTH)) goto redoloop;
|
|
y = broom->hy + 1;
|
|
x = broom->lx + ((dpos == -1) ? rn2(1+(broom->hx - broom->lx)) : dpos);
|
|
if (IS_ROCK(levl[x][y+1].typ)) goto redoloop;
|
|
goto outdirloop;
|
|
case 2:
|
|
if (!(dwall & W_WEST)) goto redoloop;
|
|
x = broom->lx - 1;
|
|
y = broom->ly + ((dpos == -1) ? rn2(1+(broom->hy - broom->ly)) : dpos);
|
|
if (IS_ROCK(levl[x-1][y].typ)) goto redoloop;
|
|
goto outdirloop;
|
|
case 3:
|
|
if (!(dwall & W_EAST)) goto redoloop;
|
|
x = broom->hx + 1;
|
|
y = broom->ly + ((dpos == -1) ? rn2(1+(broom->hy - broom->ly)) : dpos);
|
|
if (IS_ROCK(levl[x+1][y].typ)) goto redoloop;
|
|
goto outdirloop;
|
|
default:
|
|
x = y = 0;
|
|
panic("create_door: No wall for door!");
|
|
goto outdirloop;
|
|
}
|
|
outdirloop:
|
|
if (okdoor(x,y))
|
|
break;
|
|
redoloop: ;
|
|
} while (++trycnt <= 100);
|
|
if (trycnt > 100) {
|
|
impossible("create_door: Can't find a proper place!");
|
|
return;
|
|
}
|
|
levl[x][y].typ = (dd->secret ? SDOOR : DOOR);
|
|
levl[x][y].doormask = dd->mask;
|
|
}
|
|
|
|
/*
|
|
* Create a secret door in croom on any one of the specified walls.
|
|
*/
|
|
void
|
|
create_secret_door(croom, walls)
|
|
struct mkroom *croom;
|
|
xchar walls; /* any of W_NORTH | W_SOUTH | W_EAST | W_WEST (or W_ANY) */
|
|
{
|
|
xchar sx, sy; /* location of the secret door */
|
|
int count;
|
|
|
|
for(count = 0; count < 100; count++) {
|
|
sx = rn1(croom->hx - croom->lx + 1, croom->lx);
|
|
sy = rn1(croom->hy - croom->ly + 1, croom->ly);
|
|
|
|
switch(rn2(4)) {
|
|
case 0: /* top */
|
|
if(!(walls & W_NORTH)) continue;
|
|
sy = croom->ly-1; break;
|
|
case 1: /* bottom */
|
|
if(!(walls & W_SOUTH)) continue;
|
|
sy = croom->hy+1; break;
|
|
case 2: /* left */
|
|
if(!(walls & W_EAST)) continue;
|
|
sx = croom->lx-1; break;
|
|
case 3: /* right */
|
|
if(!(walls & W_WEST)) continue;
|
|
sx = croom->hx+1; break;
|
|
}
|
|
|
|
if(okdoor(sx,sy)) {
|
|
levl[sx][sy].typ = SDOOR;
|
|
levl[sx][sy].doormask = D_CLOSED;
|
|
return;
|
|
}
|
|
}
|
|
|
|
impossible("couldn't create secret door on any walls 0x%x", walls);
|
|
}
|
|
|
|
/*
|
|
* Create a trap in a room.
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
create_trap(t,croom)
|
|
trap *t;
|
|
struct mkroom *croom;
|
|
{
|
|
schar x,y;
|
|
coord tm;
|
|
|
|
if (croom)
|
|
get_free_room_loc(&x, &y, croom, t->coord);
|
|
else {
|
|
int trycnt = 0;
|
|
do {
|
|
get_location_coord(&x, &y, DRY, croom, t->coord);
|
|
} while ((levl[x][y].typ == STAIRS || levl[x][y].typ == LADDER) && ++trycnt <= 100);
|
|
if (trycnt > 100) return;
|
|
}
|
|
|
|
tm.x = x;
|
|
tm.y = y;
|
|
|
|
mktrap(t->type, 1, (struct mkroom*) 0, &tm);
|
|
}
|
|
|
|
/*
|
|
* Create a monster in a room.
|
|
*/
|
|
|
|
STATIC_OVL int
|
|
noncoalignment(alignment)
|
|
aligntyp alignment;
|
|
{
|
|
int k;
|
|
|
|
k = rn2(2);
|
|
if (!alignment)
|
|
return(k ? -1 : 1);
|
|
return(k ? -alignment : 0);
|
|
}
|
|
|
|
/* attempt to screen out locations where a mimic-as-boulder shouldn't occur */
|
|
STATIC_OVL boolean
|
|
m_bad_boulder_spot(x, y)
|
|
int x, y;
|
|
{
|
|
struct rm *lev;
|
|
|
|
/* avoid trap locations */
|
|
if (t_at(x, y)) return TRUE;
|
|
/* try to avoid locations which already have a boulder (this won't
|
|
actually work; we get called before objects have been placed...) */
|
|
if (sobj_at(BOULDER, x, y)) return TRUE;
|
|
/* avoid closed doors */
|
|
lev = &levl[x][y];
|
|
if (IS_DOOR(lev->typ) && (lev->doormask & (D_CLOSED | D_LOCKED)) != 0)
|
|
return TRUE;
|
|
/* spot is ok */
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
create_monster(m,croom)
|
|
monster *m;
|
|
struct mkroom *croom;
|
|
{
|
|
struct monst *mtmp;
|
|
schar x, y;
|
|
char class;
|
|
aligntyp amask;
|
|
coord cc;
|
|
struct permonst *pm;
|
|
unsigned g_mvflags;
|
|
|
|
|
|
if (m->class >= 0)
|
|
class = (char) def_char_to_monclass((char)m->class);
|
|
else
|
|
class = 0;
|
|
|
|
if (class == MAXMCLASSES)
|
|
panic("create_monster: unknown monster class '%c'", m->class);
|
|
|
|
amask = (m->align == AM_SPLEV_CO) ?
|
|
Align2amask(u.ualignbase[A_ORIGINAL]) :
|
|
(m->align == AM_SPLEV_NONCO) ?
|
|
Align2amask(noncoalignment(u.ualignbase[A_ORIGINAL])) :
|
|
(m->align <= -(MAX_REGISTERS+1)) ? induced_align(80) :
|
|
(m->align < 0 ? ralign[-m->align-1] : m->align);
|
|
|
|
if (!class)
|
|
pm = (struct permonst *) 0;
|
|
else if (m->id != NON_PM) {
|
|
pm = &mons[m->id];
|
|
g_mvflags = (unsigned) mvitals[monsndx(pm)].mvflags;
|
|
if ((pm->geno & G_UNIQ) && (g_mvflags & G_EXTINCT))
|
|
return;
|
|
else if (g_mvflags & G_GONE) /* genocided or extinct */
|
|
pm = (struct permonst *) 0; /* make random monster */
|
|
} else {
|
|
pm = mkclass(class,G_NOGEN);
|
|
/* if we can't get a specific monster type (pm == 0) then the
|
|
class has been genocided, so settle for a random monster */
|
|
}
|
|
if (In_mines(&u.uz) && pm && your_race(pm) &&
|
|
(Race_if(PM_DWARF) || Race_if(PM_GNOME)) && rn2(3))
|
|
pm = (struct permonst *) 0;
|
|
|
|
if (pm) {
|
|
int loc = DRY;
|
|
if (pm->mlet == S_EEL || amphibious(pm) || is_swimmer(pm)) loc = WET;
|
|
if (is_flyer(pm) || is_floater(pm)) loc |= (HOT | WET);
|
|
if (passes_walls(pm) || noncorporeal(pm)) loc |= SOLID;
|
|
if (flaming(pm)) loc |= HOT;
|
|
/* If water-liking monster, first try is without DRY */
|
|
get_location_coord(&x, &y, loc|NO_LOC_WARN, croom, m->coord);
|
|
if (x == -1 && y == -1) {
|
|
loc |= DRY;
|
|
get_location_coord(&x, &y, loc, croom, m->coord);
|
|
}
|
|
} else {
|
|
get_location_coord(&x, &y, DRY, croom, m->coord);
|
|
}
|
|
|
|
/* try to find a close place if someone else is already there */
|
|
if (MON_AT(x,y) && enexto(&cc, x, y, pm))
|
|
x = cc.x, y = cc.y;
|
|
|
|
if(m->align != -(MAX_REGISTERS+2))
|
|
mtmp = mk_roamer(pm, Amask2align(amask), x, y, m->peaceful);
|
|
else if(PM_ARCHEOLOGIST <= m->id && m->id <= PM_WIZARD)
|
|
mtmp = mk_mplayer(pm, x, y, FALSE);
|
|
else mtmp = makemon(pm, x, y, NO_MM_FLAGS);
|
|
|
|
if (mtmp) {
|
|
x = mtmp->mx, y = mtmp->my; /* sanity precaution */
|
|
m->x = x, m->y = y;
|
|
/* handle specific attributes for some special monsters */
|
|
if (m->name.str) mtmp = christen_monst(mtmp, m->name.str);
|
|
|
|
/*
|
|
* This is currently hardwired for mimics only. It should
|
|
* eventually be expanded.
|
|
*/
|
|
if (m->appear_as.str && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham)) {
|
|
int i;
|
|
|
|
switch (m->appear) {
|
|
case M_AP_NOTHING:
|
|
impossible(
|
|
"create_monster: mon has an appearance, \"%s\", but no type",
|
|
m->appear_as.str);
|
|
break;
|
|
|
|
case M_AP_FURNITURE:
|
|
for (i = 0; i < MAXPCHARS; i++)
|
|
if (!strcmp(defsyms[i].explanation,
|
|
m->appear_as.str))
|
|
break;
|
|
if (i == MAXPCHARS) {
|
|
impossible(
|
|
"create_monster: can't find feature \"%s\"",
|
|
m->appear_as.str);
|
|
} else {
|
|
mtmp->m_ap_type = M_AP_FURNITURE;
|
|
mtmp->mappearance = i;
|
|
}
|
|
break;
|
|
|
|
case M_AP_OBJECT:
|
|
for (i = 0; i < NUM_OBJECTS; i++)
|
|
if (OBJ_NAME(objects[i]) &&
|
|
!strcmp(OBJ_NAME(objects[i]),m->appear_as.str))
|
|
break;
|
|
if (i == NUM_OBJECTS) {
|
|
impossible(
|
|
"create_monster: can't find object \"%s\"",
|
|
m->appear_as.str);
|
|
} else {
|
|
mtmp->m_ap_type = M_AP_OBJECT;
|
|
mtmp->mappearance = i;
|
|
/* try to avoid placing mimic boulder on a trap */
|
|
if (i == BOULDER && m->x < 0 &&
|
|
m_bad_boulder_spot(x, y)) {
|
|
int retrylimit = 10;
|
|
|
|
remove_monster(x, y);
|
|
do {
|
|
x = m->x;
|
|
y = m->y;
|
|
get_location(&x, &y, DRY, croom);
|
|
if (MON_AT(x,y) && enexto(&cc, x, y, pm))
|
|
x = cc.x, y = cc.y;
|
|
} while (m_bad_boulder_spot(x, y) &&
|
|
--retrylimit > 0);
|
|
place_monster(mtmp, x, y);
|
|
/* if we didn't find a good spot
|
|
then mimic something else */
|
|
if (!retrylimit) set_mimic_sym(mtmp);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case M_AP_MONSTER:
|
|
{
|
|
int mndx;
|
|
if (!strcmpi(m->appear_as.str, "random"))
|
|
mndx = select_newcham_form(mtmp);
|
|
else
|
|
mndx = name_to_mon(m->appear_as.str);
|
|
if ((mndx != NON_PM) && (&mons[mndx] != mtmp->data)) {
|
|
struct permonst *mdat = &mons[mndx];
|
|
struct permonst *olddata = mtmp->data;
|
|
/* this code duplicated from newcham() */
|
|
if(is_male(mdat)) {
|
|
if(mtmp->female) mtmp->female = FALSE;
|
|
} else if (is_female(mdat)) {
|
|
if(!mtmp->female) mtmp->female = TRUE;
|
|
} else if (!is_neuter(mdat)) {
|
|
if(!rn2(10)) mtmp->female = !mtmp->female;
|
|
}
|
|
set_mon_data(mtmp, mdat, 0);
|
|
if (emits_light(olddata) != emits_light(mtmp->data)) {
|
|
/* used to give light, now doesn't, or vice versa,
|
|
or light's range has changed */
|
|
if (emits_light(olddata))
|
|
del_light_source(LS_MONSTER, (genericptr_t)mtmp);
|
|
if (emits_light(mtmp->data))
|
|
new_light_source(mtmp->mx, mtmp->my, emits_light(mtmp->data),
|
|
LS_MONSTER, (genericptr_t)mtmp);
|
|
}
|
|
if (!mtmp->perminvis || pm_invisible(olddata))
|
|
mtmp->perminvis = pm_invisible(mdat);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
impossible(
|
|
"create_monster: unimplemented mon appear type [%d,\"%s\"]",
|
|
m->appear, m->appear_as.str);
|
|
break;
|
|
}
|
|
if (does_block(x, y, &levl[x][y]))
|
|
block_point(x, y);
|
|
}
|
|
|
|
if (m->peaceful >= 0) {
|
|
mtmp->mpeaceful = m->peaceful;
|
|
/* changed mpeaceful again; have to reset malign */
|
|
set_malign(mtmp);
|
|
}
|
|
if (m->asleep >= 0) {
|
|
#ifdef UNIXPC
|
|
/* optimizer bug strikes again */
|
|
if (m->asleep)
|
|
mtmp->msleeping = 1;
|
|
else
|
|
mtmp->msleeping = 0;
|
|
#else
|
|
mtmp->msleeping = m->asleep;
|
|
#endif
|
|
}
|
|
if (m->seentraps) mtmp->mtrapseen = m->seentraps;
|
|
if (m->female) mtmp->female = 1;
|
|
if (m->cancelled) mtmp->mcan = 1;
|
|
if (m->revived) mtmp->mrevived = 1;
|
|
if (m->avenge) mtmp->mavenge = 1;
|
|
if (m->stunned) mtmp->mstun = 1;
|
|
if (m->confused) mtmp->mconf = 1;
|
|
if (m->invis) {
|
|
mtmp->minvis = mtmp->perminvis = 1;
|
|
}
|
|
if (m->blinded) {
|
|
mtmp->mcansee = 0;
|
|
mtmp->mblinded = (m->blinded % 127);
|
|
}
|
|
if (m->paralyzed) {
|
|
mtmp->mcanmove = 0;
|
|
mtmp->mfrozen = (m->paralyzed % 127);
|
|
}
|
|
if (m->fleeing) {
|
|
mtmp->mflee = 1;
|
|
mtmp->mfleetim = (m->fleeing % 127);
|
|
}
|
|
|
|
if (m->has_invent) {
|
|
discard_minvent(mtmp);
|
|
invent_carrying_monster = mtmp;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Create an object in a room.
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
create_object(o,croom)
|
|
object *o;
|
|
struct mkroom *croom;
|
|
{
|
|
struct obj *otmp;
|
|
schar x, y;
|
|
char c;
|
|
boolean named; /* has a name been supplied in level description? */
|
|
|
|
named = o->name.str ? TRUE : FALSE;
|
|
|
|
get_location_coord(&x, &y, DRY, croom, o->coord);
|
|
|
|
if (o->class >= 0)
|
|
c = o->class;
|
|
else
|
|
c = 0;
|
|
|
|
if (!c)
|
|
otmp = mkobj_at(RANDOM_CLASS, x, y, !named);
|
|
else if (o->id != -1)
|
|
otmp = mksobj_at(o->id, x, y, TRUE, !named);
|
|
else {
|
|
/*
|
|
* The special levels are compiled with the default "text" object
|
|
* class characters. We must convert them to the internal format.
|
|
*/
|
|
char oclass = (char) def_char_to_objclass(c);
|
|
|
|
if (oclass == MAXOCLASSES)
|
|
panic("create_object: unexpected object class '%c'",c);
|
|
|
|
/* KMH -- Create piles of gold properly */
|
|
if (oclass == COIN_CLASS)
|
|
otmp = mkgold(0L, x, y);
|
|
else
|
|
otmp = mkobj_at(oclass, x, y, !named);
|
|
}
|
|
|
|
if (o->spe != -127) /* That means NOT RANDOM! */
|
|
otmp->spe = (schar)o->spe;
|
|
|
|
switch (o->curse_state) {
|
|
case 1: bless(otmp); break; /* BLESSED */
|
|
case 2: unbless(otmp); uncurse(otmp); break; /* uncursed */
|
|
case 3: curse(otmp); break; /* CURSED */
|
|
default: break; /* Otherwise it's random and we're happy
|
|
* with what mkobj gave us! */
|
|
}
|
|
|
|
/* corpsenm is "empty" if -1, random if -2, otherwise specific */
|
|
if (o->corpsenm != NON_PM) {
|
|
if (o->corpsenm == NON_PM - 1)
|
|
set_corpsenm(otmp, rndmonnum());
|
|
else set_corpsenm(otmp, o->corpsenm);
|
|
}
|
|
/* set_corpsenm() took care of egg hatch and corpse timers */
|
|
|
|
if (named)
|
|
otmp = oname(otmp, o->name.str);
|
|
|
|
if (o->eroded) {
|
|
if (o->eroded < 0) otmp->oerodeproof = 1;
|
|
else {
|
|
otmp->oeroded = (o->eroded % 4);
|
|
otmp->oeroded2 = ((o->eroded >> 2) % 4);
|
|
}
|
|
}
|
|
if (o->recharged) otmp->recharged = (o->recharged % 8);
|
|
if (o->locked) otmp->olocked = 1;
|
|
else if (o->broken) {
|
|
otmp->obroken = 1;
|
|
otmp->olocked = 0; /* obj generation may set */
|
|
}
|
|
if (o->trapped) otmp->otrapped = 1;
|
|
if (o->greased) otmp->greased = 1;
|
|
#ifdef INVISIBLE_OBJECTS
|
|
if (o->invis) otmp->oinvis = 1;
|
|
#endif
|
|
|
|
if ((o->quan > 0) && objects[otmp->otyp].oc_merge) {
|
|
otmp->quan = o->quan;
|
|
otmp->owt = weight(otmp);
|
|
}
|
|
|
|
/* contents */
|
|
if (o->containment & SP_OBJ_CONTENT) {
|
|
if (!container_idx) {
|
|
if (!invent_carrying_monster) {
|
|
/*impossible("create_object: no container");*/
|
|
/* don't complain, the monster may be gone legally
|
|
(eg. unique demon already generated)
|
|
TODO: In the case of unique demon lords, they should
|
|
get their inventories even when they get generated
|
|
outside the des-file. Maybe another data file that
|
|
determines what inventories monsters get by default?
|
|
*/
|
|
} else {
|
|
int ci;
|
|
struct obj *objcheck = otmp;
|
|
int inuse = -1;
|
|
|
|
for (ci = 0; ci < container_idx; ci++)
|
|
if (container_obj[ci] == objcheck)
|
|
inuse = ci;
|
|
remove_object(otmp);
|
|
if (mpickobj(invent_carrying_monster, otmp)) {
|
|
if (inuse > -1) {
|
|
impossible(
|
|
"container given to monster was merged or deallocated.");
|
|
for (ci = inuse; ci < container_idx - 1; ci++)
|
|
container_obj[ci] = container_obj[ci + 1];
|
|
container_obj[container_idx] = NULL;
|
|
container_idx--;
|
|
}
|
|
/* we lost track of it. */
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
remove_object(otmp);
|
|
if (container_obj[container_idx-1])
|
|
(void) add_to_container(container_obj[container_idx-1], otmp);
|
|
else {
|
|
obj_extract_self(otmp);
|
|
obfree(otmp, NULL);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
/* container */
|
|
if (o->containment & SP_OBJ_CONTAINER) {
|
|
delete_contents(otmp);
|
|
if (container_idx < MAX_CONTAINMENT) {
|
|
container_obj[container_idx] = otmp;
|
|
container_idx++;
|
|
} else impossible("create_object: too deeply nested containers.");
|
|
}
|
|
|
|
/* Medusa level special case: statues are petrified monsters, so they
|
|
* are not stone-resistant and have monster inventory. They also lack
|
|
* other contents, but that can be specified as an empty container.
|
|
*/
|
|
if (o->id == STATUE && Is_medusa_level(&u.uz) &&
|
|
o->corpsenm == NON_PM) {
|
|
struct monst *was;
|
|
struct obj *obj;
|
|
int wastyp;
|
|
int i = 0; /* prevent endless loop in case makemon always fails */
|
|
|
|
/* Named random statues are of player types, and aren't stone-
|
|
* resistant (if they were, we'd have to reset the name as well as
|
|
* setting corpsenm).
|
|
*/
|
|
for (wastyp = otmp->corpsenm; i < 1000 ; i++, wastyp = rndmonnum()) {
|
|
/* makemon without rndmonst() might create a group */
|
|
was = makemon(&mons[wastyp], 0, 0, MM_NOCOUNTBIRTH);
|
|
if (was) {
|
|
if (!resists_ston(was)) {
|
|
(void) propagate(wastyp, TRUE, FALSE);
|
|
break;
|
|
}
|
|
mongone(was);
|
|
was = NULL;
|
|
}
|
|
}
|
|
if (was) {
|
|
set_corpsenm(otmp, wastyp);
|
|
while(was->minvent) {
|
|
obj = was->minvent;
|
|
obj->owornmask = 0;
|
|
obj_extract_self(obj);
|
|
(void) add_to_container(otmp, obj);
|
|
}
|
|
otmp->owt = weight(otmp);
|
|
mongone(was);
|
|
}
|
|
}
|
|
|
|
/* Nasty hack here: try to determine if this is the Mines or Sokoban
|
|
* "prize" and then set record_achieve_special (maps to corpsenm)
|
|
* for the object. That field will later be checked to find out if
|
|
* the player obtained the prize. */
|
|
if(otmp->otyp == LUCKSTONE && Is_mineend_level(&u.uz)) {
|
|
otmp->record_achieve_special = 1;
|
|
} else if((otmp->otyp == AMULET_OF_REFLECTION ||
|
|
otmp->otyp == BAG_OF_HOLDING) &&
|
|
Is_sokoend_level(&u.uz)) {
|
|
otmp->record_achieve_special = 1;
|
|
}
|
|
|
|
stackobj(otmp);
|
|
|
|
if (o->lit) {
|
|
begin_burn(otmp, FALSE);
|
|
}
|
|
|
|
if (o->buried) {
|
|
boolean dealloced;
|
|
(void) bury_an_obj(otmp, &dealloced);
|
|
if (dealloced && container_idx) {
|
|
container_obj[container_idx-1] = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Create an altar in a room.
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
create_altar(a, croom)
|
|
altar *a;
|
|
struct mkroom *croom;
|
|
{
|
|
schar sproom,x,y;
|
|
aligntyp amask;
|
|
boolean croom_is_temple = TRUE;
|
|
int oldtyp;
|
|
|
|
if (croom) {
|
|
get_free_room_loc(&x, &y, croom, a->coord);
|
|
if (croom->rtype != TEMPLE)
|
|
croom_is_temple = FALSE;
|
|
} else {
|
|
get_location_coord(&x, &y, DRY, croom, a->coord);
|
|
if ((sproom = (schar) *in_rooms(x, y, TEMPLE)) != 0)
|
|
croom = &rooms[sproom - ROOMOFFSET];
|
|
else
|
|
croom_is_temple = FALSE;
|
|
}
|
|
|
|
/* check for existing features */
|
|
oldtyp = levl[x][y].typ;
|
|
if (oldtyp == STAIRS || oldtyp == LADDER)
|
|
return;
|
|
|
|
/* Is the alignment random ?
|
|
* If so, it's an 80% chance that the altar will be co-aligned.
|
|
*
|
|
* The alignment is encoded as amask values instead of alignment
|
|
* values to avoid conflicting with the rest of the encoding,
|
|
* shared by many other parts of the special level code.
|
|
*/
|
|
|
|
amask = (a->align == AM_SPLEV_CO) ?
|
|
Align2amask(u.ualignbase[A_ORIGINAL]) :
|
|
(a->align == AM_SPLEV_NONCO) ?
|
|
Align2amask(noncoalignment(u.ualignbase[A_ORIGINAL])) :
|
|
(a->align == -(MAX_REGISTERS+1)) ? induced_align(80) :
|
|
(a->align < 0 ? ralign[-a->align-1] : a->align);
|
|
|
|
levl[x][y].typ = ALTAR;
|
|
levl[x][y].altarmask = amask;
|
|
|
|
if (a->shrine < 0) a->shrine = rn2(2); /* handle random case */
|
|
|
|
if (!croom_is_temple || !a->shrine) return;
|
|
|
|
if (a->shrine) { /* Is it a shrine or sanctum? */
|
|
priestini(&u.uz, croom, x, y, (a->shrine > 1));
|
|
levl[x][y].altarmask |= AM_SHRINE;
|
|
level.flags.has_temple = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
replace_terrain(terr, croom)
|
|
replaceterrain *terr;
|
|
struct mkroom *croom;
|
|
{
|
|
schar x, y, x1, y1, x2, y2;
|
|
|
|
if (terr->toter >= MAX_TYPE) return;
|
|
|
|
x1 = terr->x1; y1 = terr->y1;
|
|
get_location(&x1, &y1, ANY_LOC, croom);
|
|
|
|
x2 = terr->x2; y2 = terr->y2;
|
|
get_location(&x2, &y2, ANY_LOC, croom);
|
|
|
|
for (x = max(x1,0); x <= min(x2,COLNO-1); x++)
|
|
for (y = max(y1,0); y <= min(y2,ROWNO-1); y++)
|
|
if ((levl[x][y].typ == terr->fromter) && (rn2(100) < terr->chance)) {
|
|
SET_TYPLIT(x,y, terr->toter, terr->tolit);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Search for a door in a room on a specified wall.
|
|
*/
|
|
|
|
STATIC_OVL boolean
|
|
search_door(croom,x,y,wall,cnt)
|
|
struct mkroom *croom;
|
|
xchar *x, *y;
|
|
xchar wall;
|
|
int cnt;
|
|
{
|
|
int dx, dy;
|
|
int xx,yy;
|
|
|
|
switch(wall) {
|
|
case W_NORTH:
|
|
dy = 0; dx = 1;
|
|
xx = croom->lx;
|
|
yy = croom->hy + 1;
|
|
break;
|
|
case W_SOUTH:
|
|
dy = 0; dx = 1;
|
|
xx = croom->lx;
|
|
yy = croom->ly - 1;
|
|
break;
|
|
case W_EAST:
|
|
dy = 1; dx = 0;
|
|
xx = croom->hx + 1;
|
|
yy = croom->ly;
|
|
break;
|
|
case W_WEST:
|
|
dy = 1; dx = 0;
|
|
xx = croom->lx - 1;
|
|
yy = croom->ly;
|
|
break;
|
|
default:
|
|
dx = dy = xx = yy = 0;
|
|
panic("search_door: Bad wall!");
|
|
break;
|
|
}
|
|
while (xx <= croom->hx+1 && yy <= croom->hy+1) {
|
|
if (IS_DOOR(levl[xx][yy].typ) || levl[xx][yy].typ == SDOOR) {
|
|
*x = xx;
|
|
*y = yy;
|
|
if (cnt-- <= 0)
|
|
return TRUE;
|
|
}
|
|
xx += dx;
|
|
yy += dy;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Dig a corridor between two points.
|
|
*/
|
|
|
|
boolean
|
|
dig_corridor(org,dest,nxcor,ftyp,btyp)
|
|
coord *org, *dest;
|
|
boolean nxcor;
|
|
schar ftyp, btyp;
|
|
{
|
|
register int dx=0, dy=0, dix, diy, cct;
|
|
register struct rm *crm;
|
|
register int tx, ty, xx, yy;
|
|
|
|
xx = org->x; yy = org->y;
|
|
tx = dest->x; ty = dest->y;
|
|
if (xx <= 0 || yy <= 0 || tx <= 0 || ty <= 0 ||
|
|
xx > COLNO-1 || tx > COLNO-1 ||
|
|
yy > ROWNO-1 || ty > ROWNO-1) {
|
|
debugpline4("dig_corridor: bad coords <%d,%d> <%d,%d>.",
|
|
xx, yy, tx, ty);
|
|
return FALSE;
|
|
}
|
|
if (tx > xx) dx = 1;
|
|
else if (ty > yy) dy = 1;
|
|
else if (tx < xx) dx = -1;
|
|
else dy = -1;
|
|
|
|
xx -= dx;
|
|
yy -= dy;
|
|
cct = 0;
|
|
while(xx != tx || yy != ty) {
|
|
/* loop: dig corridor at [xx,yy] and find new [xx,yy] */
|
|
if(cct++ > 500 || (nxcor && !rn2(35)))
|
|
return FALSE;
|
|
|
|
xx += dx;
|
|
yy += dy;
|
|
|
|
if(xx >= COLNO-1 || xx <= 0 || yy <= 0 || yy >= ROWNO-1)
|
|
return FALSE; /* impossible */
|
|
|
|
crm = &levl[xx][yy];
|
|
if(crm->typ == btyp) {
|
|
if(ftyp != CORR || rn2(100)) {
|
|
crm->typ = ftyp;
|
|
if(nxcor && !rn2(50))
|
|
(void) mksobj_at(BOULDER, xx, yy, TRUE, FALSE);
|
|
} else {
|
|
crm->typ = SCORR;
|
|
}
|
|
} else
|
|
if(crm->typ != ftyp && crm->typ != SCORR) {
|
|
/* strange ... */
|
|
return FALSE;
|
|
}
|
|
|
|
/* find next corridor position */
|
|
dix = abs(xx-tx);
|
|
diy = abs(yy-ty);
|
|
|
|
/* do we have to change direction ? */
|
|
if(dy && dix > diy) {
|
|
register int ddx = (xx > tx) ? -1 : 1;
|
|
|
|
crm = &levl[xx+ddx][yy];
|
|
if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) {
|
|
dx = ddx;
|
|
dy = 0;
|
|
continue;
|
|
}
|
|
} else if(dx && diy > dix) {
|
|
register int ddy = (yy > ty) ? -1 : 1;
|
|
|
|
crm = &levl[xx][yy+ddy];
|
|
if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) {
|
|
dy = ddy;
|
|
dx = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* continue straight on? */
|
|
crm = &levl[xx+dx][yy+dy];
|
|
if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR)
|
|
continue;
|
|
|
|
/* no, what must we do now?? */
|
|
if(dx) {
|
|
dx = 0;
|
|
dy = (ty < yy) ? -1 : 1;
|
|
} else {
|
|
dy = 0;
|
|
dx = (tx < xx) ? -1 : 1;
|
|
}
|
|
crm = &levl[xx+dx][yy+dy];
|
|
if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR)
|
|
continue;
|
|
dy = -dy;
|
|
dx = -dx;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Disgusting hack: since special levels have their rooms filled before
|
|
* sorting the rooms, we have to re-arrange the speed values upstairs_room
|
|
* and dnstairs_room after the rooms have been sorted. On normal levels,
|
|
* stairs don't get created until _after_ sorting takes place.
|
|
*/
|
|
STATIC_OVL void
|
|
fix_stair_rooms()
|
|
{
|
|
int i;
|
|
struct mkroom *croom;
|
|
|
|
if(xdnstair &&
|
|
!((dnstairs_room->lx <= xdnstair && xdnstair <= dnstairs_room->hx) &&
|
|
(dnstairs_room->ly <= ydnstair && ydnstair <= dnstairs_room->hy))) {
|
|
for(i=0; i < nroom; i++) {
|
|
croom = &rooms[i];
|
|
if((croom->lx <= xdnstair && xdnstair <= croom->hx) &&
|
|
(croom->ly <= ydnstair && ydnstair <= croom->hy)) {
|
|
dnstairs_room = croom;
|
|
break;
|
|
}
|
|
}
|
|
if(i == nroom)
|
|
panic("Couldn't find dnstair room in fix_stair_rooms!");
|
|
}
|
|
if(xupstair &&
|
|
!((upstairs_room->lx <= xupstair && xupstair <= upstairs_room->hx) &&
|
|
(upstairs_room->ly <= yupstair && yupstair <= upstairs_room->hy))) {
|
|
for(i=0; i < nroom; i++) {
|
|
croom = &rooms[i];
|
|
if((croom->lx <= xupstair && xupstair <= croom->hx) &&
|
|
(croom->ly <= yupstair && yupstair <= croom->hy)) {
|
|
upstairs_room = croom;
|
|
break;
|
|
}
|
|
}
|
|
if(i == nroom)
|
|
panic("Couldn't find upstair room in fix_stair_rooms!");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Corridors always start from a door. But it can end anywhere...
|
|
* Basically we search for door coordinates or for endpoints coordinates
|
|
* (from a distance).
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
create_corridor(c)
|
|
corridor *c;
|
|
{
|
|
coord org, dest;
|
|
|
|
if (c->src.room == -1) {
|
|
fix_stair_rooms();
|
|
makecorridors(); /*makecorridors(c->src.door);*/
|
|
return;
|
|
}
|
|
|
|
if( !search_door(&rooms[c->src.room], &org.x, &org.y, c->src.wall,
|
|
c->src.door))
|
|
return;
|
|
|
|
if (c->dest.room != -1) {
|
|
if(!search_door(&rooms[c->dest.room], &dest.x, &dest.y,
|
|
c->dest.wall, c->dest.door))
|
|
return;
|
|
switch(c->src.wall) {
|
|
case W_NORTH: org.y--; break;
|
|
case W_SOUTH: org.y++; break;
|
|
case W_WEST: org.x--; break;
|
|
case W_EAST: org.x++; break;
|
|
}
|
|
switch(c->dest.wall) {
|
|
case W_NORTH: dest.y--; break;
|
|
case W_SOUTH: dest.y++; break;
|
|
case W_WEST: dest.x--; break;
|
|
case W_EAST: dest.x++; break;
|
|
}
|
|
(void) dig_corridor(&org, &dest, FALSE, CORR, STONE);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill a room (shop, zoo, etc...) with appropriate stuff.
|
|
*/
|
|
|
|
void
|
|
fill_room(croom, prefilled)
|
|
struct mkroom *croom;
|
|
boolean prefilled;
|
|
{
|
|
if (!croom || croom->rtype == OROOM)
|
|
return;
|
|
|
|
if (!prefilled) {
|
|
int x,y;
|
|
|
|
/* Shop ? */
|
|
if (croom->rtype >= SHOPBASE) {
|
|
stock_room(croom->rtype - SHOPBASE, croom);
|
|
level.flags.has_shop = TRUE;
|
|
return;
|
|
}
|
|
|
|
switch (croom->rtype) {
|
|
case VAULT:
|
|
for (x=croom->lx;x<=croom->hx;x++)
|
|
for (y=croom->ly;y<=croom->hy;y++)
|
|
(void) mkgold((long)rn1(abs(depth(&u.uz))*100, 51), x, y);
|
|
break;
|
|
case COURT:
|
|
case ZOO:
|
|
case BEEHIVE:
|
|
case MORGUE:
|
|
case BARRACKS:
|
|
fill_zoo(croom);
|
|
break;
|
|
}
|
|
}
|
|
switch (croom->rtype) {
|
|
case VAULT:
|
|
level.flags.has_vault = TRUE;
|
|
break;
|
|
case ZOO:
|
|
level.flags.has_zoo = TRUE;
|
|
break;
|
|
case COURT:
|
|
level.flags.has_court = TRUE;
|
|
break;
|
|
case MORGUE:
|
|
level.flags.has_morgue = TRUE;
|
|
break;
|
|
case BEEHIVE:
|
|
level.flags.has_beehive = TRUE;
|
|
break;
|
|
case BARRACKS:
|
|
level.flags.has_barracks = TRUE;
|
|
break;
|
|
case TEMPLE:
|
|
level.flags.has_temple = TRUE;
|
|
break;
|
|
case SWAMP:
|
|
level.flags.has_swamp = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
struct mkroom *
|
|
build_room(r, mkr)
|
|
room *r;
|
|
struct mkroom *mkr;
|
|
{
|
|
boolean okroom;
|
|
struct mkroom *aroom;
|
|
xchar rtype = (!r->chance || rn2(100) < r->chance) ? r->rtype : OROOM;
|
|
|
|
if(mkr) {
|
|
aroom = &subrooms[nsubroom];
|
|
okroom = create_subroom(mkr, r->x, r->y, r->w, r->h,
|
|
rtype, r->rlit);
|
|
} else {
|
|
aroom = &rooms[nroom];
|
|
okroom = create_room(r->x, r->y, r->w, r->h, r->xalign,
|
|
r->yalign, rtype, r->rlit);
|
|
}
|
|
|
|
if (okroom) {
|
|
#ifdef SPECIALIZATION
|
|
topologize(aroom,FALSE); /* set roomno */
|
|
#else
|
|
topologize(aroom); /* set roomno */
|
|
#endif
|
|
aroom->needfill = r->filled;
|
|
aroom->needjoining = r->joined;
|
|
return aroom;
|
|
}
|
|
return (struct mkroom *)0;
|
|
}
|
|
|
|
/*
|
|
* set lighting in a region that will not become a room.
|
|
*/
|
|
STATIC_OVL void
|
|
light_region(tmpregion)
|
|
region *tmpregion;
|
|
{
|
|
register boolean litstate = tmpregion->rlit ? 1 : 0;
|
|
register int hiy = tmpregion->y2;
|
|
register int x, y;
|
|
register struct rm *lev;
|
|
int lowy = tmpregion->y1;
|
|
int lowx = tmpregion->x1, hix = tmpregion->x2;
|
|
|
|
if(litstate) {
|
|
/* adjust region size for walls, but only if lighted */
|
|
lowx = max(lowx-1,1);
|
|
hix = min(hix+1,COLNO-1);
|
|
lowy = max(lowy-1,0);
|
|
hiy = min(hiy+1, ROWNO-1);
|
|
}
|
|
for(x = lowx; x <= hix; x++) {
|
|
lev = &levl[x][lowy];
|
|
for(y = lowy; y <= hiy; y++) {
|
|
if (lev->typ != LAVAPOOL) /* this overrides normal lighting */
|
|
lev->lit = litstate;
|
|
lev++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
wallify_map(x1,y1,x2,y2)
|
|
int x1,y1,x2,y2;
|
|
{
|
|
int x, y, xx, yy, lo_xx, lo_yy, hi_xx, hi_yy;
|
|
|
|
for (y = y1; y <= y2; y++) {
|
|
lo_yy = (y > 0) ? y - 1 : 0;
|
|
hi_yy = (y < y2) ? y + 1 : y2;
|
|
for (x = x1; x <= x2; x++) {
|
|
if (levl[x][y].typ != STONE) continue;
|
|
lo_xx = (x > 0) ? x - 1 : 0;
|
|
hi_xx = (x < x2) ? x + 1 : x2;
|
|
for (yy = lo_yy; yy <= hi_yy; yy++)
|
|
for (xx = lo_xx; xx <= hi_xx; xx++)
|
|
if (IS_ROOM(levl[xx][yy].typ) ||
|
|
levl[xx][yy].typ == CROSSWALL) {
|
|
levl[x][y].typ = (yy != y) ? HWALL : VWALL;
|
|
yy = hi_yy; /* end `yy' loop */
|
|
break; /* end `xx' loop */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Select a random coordinate in the maze.
|
|
*
|
|
* We want a place not 'touched' by the loader. That is, a place in
|
|
* the maze outside every part of the special level.
|
|
*/
|
|
|
|
STATIC_OVL void
|
|
maze1xy(m, humidity)
|
|
coord *m;
|
|
int humidity;
|
|
{
|
|
register int x, y, tryct = 2000;
|
|
/* tryct: normally it won't take more than ten or so tries due
|
|
to the circumstances under which we'll be called, but the
|
|
`humidity' screening might drastically change the chances */
|
|
|
|
do {
|
|
x = rn1(x_maze_max - 3, 3);
|
|
y = rn1(y_maze_max - 3, 3);
|
|
if (--tryct < 0) break; /* give up */
|
|
} while (!(x % 2) || !(y % 2) || SpLev_Map[x][y] ||
|
|
!is_ok_location((schar)x, (schar)y, humidity));
|
|
|
|
m->x = (xchar)x, m->y = (xchar)y;
|
|
}
|
|
|
|
/*
|
|
* If there's a significant portion of maze unused by the special level,
|
|
* we don't want it empty.
|
|
*
|
|
* Makes the number of traps, monsters, etc. proportional
|
|
* to the size of the maze.
|
|
*/
|
|
STATIC_OVL void
|
|
fill_empty_maze()
|
|
{
|
|
int mapcountmax, mapcount, mapfact;
|
|
xchar x,y;
|
|
coord mm;
|
|
|
|
mapcountmax = mapcount = (x_maze_max - 2) * (y_maze_max - 2);
|
|
mapcountmax = mapcountmax / 2;
|
|
|
|
for(x = 2; x < x_maze_max; x++)
|
|
for(y = 0; y < y_maze_max; y++)
|
|
if(SpLev_Map[x][y]) mapcount--;
|
|
|
|
if ((mapcount > (int) (mapcountmax / 10))) {
|
|
mapfact = (int) ((mapcount * 100L) / mapcountmax);
|
|
for(x = rnd((int) (20 * mapfact) / 100); x; x--) {
|
|
maze1xy(&mm, DRY);
|
|
(void) mkobj_at(rn2(2) ? GEM_CLASS : RANDOM_CLASS,
|
|
mm.x, mm.y, TRUE);
|
|
}
|
|
for(x = rnd((int) (12 * mapfact) / 100); x; x--) {
|
|
maze1xy(&mm, DRY);
|
|
(void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE);
|
|
}
|
|
for (x = rn2(2); x; x--) {
|
|
maze1xy(&mm, DRY);
|
|
(void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS);
|
|
}
|
|
for(x = rnd((int) (12 * mapfact) / 100); x; x--) {
|
|
maze1xy(&mm, DRY);
|
|
(void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS);
|
|
}
|
|
for(x = rn2((int) (15 * mapfact) / 100); x; x--) {
|
|
maze1xy(&mm, DRY);
|
|
(void) mkgold(0L,mm.x,mm.y);
|
|
}
|
|
for(x = rn2((int) (15 * mapfact) / 100); x; x--) {
|
|
int trytrap;
|
|
|
|
maze1xy(&mm, DRY);
|
|
trytrap = rndtrap();
|
|
if (sobj_at(BOULDER, mm.x, mm.y))
|
|
while (trytrap == PIT || trytrap == SPIKED_PIT ||
|
|
trytrap == TRAPDOOR || trytrap == HOLE)
|
|
trytrap = rndtrap();
|
|
(void) maketrap(mm.x, mm.y, trytrap);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* special level loader
|
|
*/
|
|
STATIC_OVL boolean
|
|
sp_level_loader(fd, lvl)
|
|
dlb *fd;
|
|
sp_lev *lvl;
|
|
{
|
|
long n_opcode = 0;
|
|
struct opvar *opdat;
|
|
int opcode;
|
|
|
|
Fread((genericptr_t)&(lvl->n_opcodes), 1, sizeof(lvl->n_opcodes), fd);
|
|
|
|
lvl->opcodes = (_opcode *)alloc(sizeof(_opcode) * (lvl->n_opcodes));
|
|
if (!lvl->opcodes) panic("sp lvl load opcodes alloc");
|
|
while (n_opcode < lvl->n_opcodes) {
|
|
|
|
Fread((genericptr_t) &lvl->opcodes[n_opcode].opcode, 1,
|
|
sizeof(lvl->opcodes[n_opcode].opcode), fd);
|
|
opcode = lvl->opcodes[n_opcode].opcode;
|
|
|
|
opdat = NULL;
|
|
|
|
if (opcode < SPO_NULL || opcode >= MAX_SP_OPCODES)
|
|
panic("sp_level_loader: impossible opcode %i.", opcode);
|
|
|
|
if (opcode == SPO_PUSH) {
|
|
struct opvar *ov = (opdat = (struct opvar *)alloc(sizeof(struct opvar)));
|
|
int nsize;
|
|
|
|
if (!ov) panic("push ov alloc");
|
|
ov->spovartyp = SPO_NULL;
|
|
ov->vardata.l = 0;
|
|
Fread((genericptr_t)&(ov->spovartyp), 1, sizeof(ov->spovartyp), fd);
|
|
|
|
switch (ov->spovartyp) {
|
|
case SPOVAR_NULL: break;
|
|
case SPOVAR_COORD:
|
|
case SPOVAR_REGION:
|
|
case SPOVAR_MAPCHAR:
|
|
case SPOVAR_MONST:
|
|
case SPOVAR_OBJ:
|
|
case SPOVAR_INT:
|
|
Fread((genericptr_t)&(ov->vardata.l), 1, sizeof(ov->vardata.l), fd);
|
|
break;
|
|
case SPOVAR_VARIABLE:
|
|
case SPOVAR_STRING:
|
|
case SPOVAR_SEL:
|
|
{
|
|
char *opd;
|
|
Fread((genericptr_t) &nsize, 1, sizeof(nsize), fd);
|
|
opd = (char *)alloc(nsize + 1);
|
|
if (!opd) panic("sp lvl load opd alloc");
|
|
if (nsize) Fread(opd, 1, nsize, fd);
|
|
opd[nsize] = 0;
|
|
ov->vardata.str = opd;
|
|
}
|
|
break;
|
|
default:
|
|
panic("sp_level_loader: unknown opvar type %i", ov->spovartyp);
|
|
}
|
|
}
|
|
|
|
lvl->opcodes[n_opcode].opdat = opdat;
|
|
n_opcode++;
|
|
} /*while*/
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* Frees the memory allocated for special level creation structs */
|
|
STATIC_OVL boolean
|
|
sp_level_free(lvl)
|
|
sp_lev *lvl;
|
|
{
|
|
long n_opcode = 0;
|
|
|
|
while (n_opcode < lvl->n_opcodes) {
|
|
int opcode = lvl->opcodes[n_opcode].opcode;
|
|
struct opvar *opdat = lvl->opcodes[n_opcode].opdat;
|
|
|
|
if (opcode < SPO_NULL || opcode >= MAX_SP_OPCODES)
|
|
panic("sp_level_free: unknown opcode %i", opcode);
|
|
|
|
if (opdat) opvar_free(opdat);
|
|
n_opcode++;
|
|
}
|
|
Free(lvl->opcodes);
|
|
lvl->opcodes = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
splev_initlev(linit)
|
|
lev_init *linit;
|
|
{
|
|
switch (linit->init_style) {
|
|
default: impossible("Unrecognized level init style."); break;
|
|
case LVLINIT_NONE: break;
|
|
case LVLINIT_SOLIDFILL:
|
|
if (linit->lit == -1) linit->lit = rn2(2);
|
|
lvlfill_solid(linit->filling, linit->lit);
|
|
break;
|
|
case LVLINIT_MAZEGRID:
|
|
lvlfill_maze_grid(2,0, x_maze_max,y_maze_max, linit->filling);
|
|
break;
|
|
#ifdef REINCARNATION
|
|
case LVLINIT_ROGUE:
|
|
makeroguerooms();
|
|
break;
|
|
#endif
|
|
case LVLINIT_MINES:
|
|
if (linit->lit == -1) linit->lit = rn2(2);
|
|
if (linit->filling > -1) lvlfill_solid(linit->filling, 0);
|
|
linit->icedpools = icedpools;
|
|
mkmap(linit);
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct sp_frame *
|
|
frame_new(execptr)
|
|
long execptr;
|
|
{
|
|
struct sp_frame *frame = (struct sp_frame *)alloc(sizeof(struct sp_frame));
|
|
if (!frame) panic("could not create execution frame.");
|
|
frame->next = NULL;
|
|
frame->variables = NULL;
|
|
frame->n_opcode = execptr;
|
|
frame->stack = (struct splevstack *)alloc(sizeof(struct splevstack));
|
|
if (!frame->stack) panic("could not create execution frame stack.");
|
|
splev_stack_init(frame->stack);
|
|
return frame;
|
|
}
|
|
|
|
void
|
|
frame_del(frame)
|
|
struct sp_frame *frame;
|
|
{
|
|
if (!frame) return;
|
|
if (frame->stack) {
|
|
splev_stack_done(frame->stack);
|
|
frame->stack = NULL;
|
|
}
|
|
if (frame->variables) {
|
|
variable_list_del(frame->variables);
|
|
frame->variables = NULL;
|
|
}
|
|
Free(frame);
|
|
}
|
|
|
|
void
|
|
spo_frame_push(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct sp_frame *tmpframe = frame_new(coder->frame->n_opcode);
|
|
tmpframe->next = coder->frame;
|
|
coder->frame = tmpframe;
|
|
}
|
|
|
|
void
|
|
spo_frame_pop(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
if (coder->frame && coder->frame->next) {
|
|
struct sp_frame *tmpframe = coder->frame->next;
|
|
frame_del(coder->frame);
|
|
coder->frame = tmpframe;
|
|
coder->stack = coder->frame->stack;
|
|
}
|
|
}
|
|
|
|
long
|
|
sp_code_jmpaddr(curpos, jmpaddr)
|
|
long curpos, jmpaddr;
|
|
{
|
|
return (curpos + jmpaddr);
|
|
}
|
|
|
|
void
|
|
spo_call(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *addr;
|
|
struct opvar *params;
|
|
struct sp_frame *tmpframe;
|
|
|
|
if (!OV_pop_i(addr) || !OV_pop_i(params)) return;
|
|
if (OV_i(params) < 0) return;
|
|
|
|
tmpframe = frame_new(sp_code_jmpaddr(coder->frame->n_opcode, OV_i(addr)-1));
|
|
|
|
while (OV_i(params)-- > 0) {
|
|
splev_stack_push(tmpframe->stack, splev_stack_getdat_any(coder));
|
|
}
|
|
splev_stack_reverse(tmpframe->stack);
|
|
|
|
/* push a frame */
|
|
tmpframe->next = coder->frame;
|
|
coder->frame = tmpframe;
|
|
|
|
opvar_free(addr);
|
|
opvar_free(params);
|
|
}
|
|
|
|
void
|
|
spo_return(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *params;
|
|
if (!coder->frame || !coder->frame->next) panic("return: no frame.");
|
|
if (!OV_pop_i(params)) return;
|
|
if (OV_i(params) < 0) return;
|
|
|
|
while (OV_i(params)-- > 0) {
|
|
splev_stack_push(coder->frame->next->stack, splev_stack_pop(coder->stack));
|
|
}
|
|
|
|
/* pop the frame */
|
|
if (coder->frame->next) {
|
|
struct sp_frame *tmpframe = coder->frame->next;
|
|
frame_del(coder->frame);
|
|
coder->frame = tmpframe;
|
|
coder->stack = coder->frame->stack;
|
|
}
|
|
|
|
opvar_free(params);
|
|
}
|
|
|
|
/*ARGUSED*/
|
|
void
|
|
spo_end_moninvent(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
if (invent_carrying_monster)
|
|
m_dowear(invent_carrying_monster, TRUE);
|
|
invent_carrying_monster = NULL;
|
|
}
|
|
|
|
/*ARGUSED*/
|
|
void
|
|
spo_pop_container(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
if (container_idx > 0) {
|
|
container_idx--;
|
|
container_obj[container_idx] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
spo_message(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *op;
|
|
char *msg, *levmsg;
|
|
int old_n, n;
|
|
if (!OV_pop_s(op)) return;
|
|
msg = OV_s(op);
|
|
if (!msg) return;
|
|
|
|
old_n = lev_message ? (strlen(lev_message)+1) : 0;
|
|
n = strlen(msg);
|
|
|
|
levmsg = (char *) alloc(old_n+n+1);
|
|
if (!levmsg) panic("spo_message alloc");
|
|
if (old_n) levmsg[old_n-1] = '\n';
|
|
if (lev_message)
|
|
(void) memcpy((genericptr_t)levmsg, (genericptr_t)lev_message, old_n-1);
|
|
(void) memcpy((genericptr_t)&levmsg[old_n], msg, n);
|
|
levmsg[old_n+n] = '\0';
|
|
Free(lev_message);
|
|
lev_message = levmsg;
|
|
opvar_free(op);
|
|
}
|
|
|
|
void
|
|
spo_monster(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
int nparams = 0;
|
|
|
|
struct opvar *varparam;
|
|
struct opvar *id, *mcoord, *has_inv;
|
|
monster tmpmons;
|
|
|
|
tmpmons.peaceful = -1;
|
|
tmpmons.asleep = -1;
|
|
tmpmons.name.str = (char *)0;
|
|
tmpmons.appear = 0;
|
|
tmpmons.appear_as.str = (char *)0;
|
|
tmpmons.align = - MAX_REGISTERS - 2;
|
|
tmpmons.female = 0;
|
|
tmpmons.invis = 0;
|
|
tmpmons.cancelled = 0;
|
|
tmpmons.revived = 0;
|
|
tmpmons.avenge = 0;
|
|
tmpmons.fleeing = 0;
|
|
tmpmons.blinded = 0;
|
|
tmpmons.paralyzed = 0;
|
|
tmpmons.stunned = 0;
|
|
tmpmons.confused = 0;
|
|
tmpmons.seentraps = 0;
|
|
tmpmons.has_invent = 0;
|
|
|
|
if (!OV_pop_i(has_inv)) return;
|
|
|
|
if (!OV_pop_i(varparam)) return;
|
|
|
|
while ((nparams++ < (SP_M_V_END+1)) &&
|
|
(OV_typ(varparam) == SPOVAR_INT) &&
|
|
(OV_i(varparam) >= 0) &&
|
|
(OV_i(varparam) < SP_M_V_END)) {
|
|
struct opvar *parm = NULL;
|
|
OV_pop(parm);
|
|
switch (OV_i(varparam)) {
|
|
case SP_M_V_NAME:
|
|
if ((OV_typ(parm) == SPOVAR_STRING) &&
|
|
!tmpmons.name.str)
|
|
tmpmons.name.str = strdup(OV_s(parm));
|
|
break;
|
|
case SP_M_V_APPEAR:
|
|
if ((OV_typ(parm) == SPOVAR_INT) &&
|
|
!tmpmons.appear_as.str) {
|
|
tmpmons.appear = OV_i(parm);
|
|
opvar_free(parm);
|
|
OV_pop(parm);
|
|
tmpmons.appear_as.str = strdup(OV_s(parm));
|
|
}
|
|
break;
|
|
case SP_M_V_ASLEEP:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.asleep = OV_i(parm);
|
|
break;
|
|
case SP_M_V_ALIGN:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.align = OV_i(parm);
|
|
break;
|
|
case SP_M_V_PEACEFUL:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.peaceful = OV_i(parm);
|
|
break;
|
|
case SP_M_V_FEMALE:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.female = OV_i(parm);
|
|
break;
|
|
case SP_M_V_INVIS:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.invis = OV_i(parm);
|
|
break;
|
|
case SP_M_V_CANCELLED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.cancelled = OV_i(parm);
|
|
break;
|
|
case SP_M_V_REVIVED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.revived = OV_i(parm);
|
|
break;
|
|
case SP_M_V_AVENGE:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.avenge = OV_i(parm);
|
|
break;
|
|
case SP_M_V_FLEEING:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.fleeing = OV_i(parm);
|
|
break;
|
|
case SP_M_V_BLINDED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.blinded = OV_i(parm);
|
|
break;
|
|
case SP_M_V_PARALYZED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.paralyzed = OV_i(parm);
|
|
break;
|
|
case SP_M_V_STUNNED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.stunned = OV_i(parm);
|
|
break;
|
|
case SP_M_V_CONFUSED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.confused = OV_i(parm);
|
|
break;
|
|
case SP_M_V_SEENTRAPS:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpmons.seentraps = OV_i(parm);
|
|
break;
|
|
case SP_M_V_END:
|
|
nparams = SP_M_V_END+1;
|
|
break;
|
|
default:
|
|
impossible("MONSTER with unknown variable param type!");
|
|
break;
|
|
}
|
|
opvar_free(parm);
|
|
if (OV_i(varparam) != SP_M_V_END) {
|
|
opvar_free(varparam);
|
|
OV_pop(varparam);
|
|
}
|
|
}
|
|
|
|
if (!OV_pop_c(mcoord)) panic("no monster coord?");
|
|
|
|
if (!OV_pop_typ(id, SPOVAR_MONST)) panic("no mon type");
|
|
|
|
tmpmons.id = SP_MONST_PM(OV_i(id));
|
|
tmpmons.class = SP_MONST_CLASS(OV_i(id));
|
|
tmpmons.coord = OV_i(mcoord);
|
|
tmpmons.has_invent = OV_i(has_inv);
|
|
|
|
create_monster(&tmpmons, coder->croom);
|
|
|
|
Free(tmpmons.name.str);
|
|
Free(tmpmons.appear_as.str);
|
|
|
|
opvar_free(id);
|
|
opvar_free(mcoord);
|
|
opvar_free(has_inv);
|
|
opvar_free(varparam);
|
|
}
|
|
|
|
void
|
|
spo_object(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
int nparams = 0;
|
|
long quancnt;
|
|
|
|
struct opvar *varparam;
|
|
struct opvar *id, *containment;
|
|
|
|
object tmpobj;
|
|
|
|
tmpobj.spe = -127;
|
|
tmpobj.curse_state = -1;
|
|
tmpobj.corpsenm = NON_PM;
|
|
tmpobj.name.str = (char *)0;
|
|
tmpobj.quan = -1;
|
|
tmpobj.buried = 0;
|
|
tmpobj.lit = 0;
|
|
tmpobj.eroded = 0;
|
|
tmpobj.locked = 0;
|
|
tmpobj.trapped = 0;
|
|
tmpobj.recharged = 0;
|
|
tmpobj.invis = 0;
|
|
tmpobj.greased = 0;
|
|
tmpobj.broken = 0;
|
|
tmpobj.coord = SP_COORD_PACK_RANDOM(0);
|
|
|
|
if (!OV_pop_i(containment)) return;
|
|
|
|
if (!OV_pop_i(varparam)) return;
|
|
|
|
while ((nparams++ < (SP_O_V_END+1)) &&
|
|
(OV_typ(varparam) == SPOVAR_INT) &&
|
|
(OV_i(varparam) >= 0) &&
|
|
(OV_i(varparam) < SP_O_V_END)) {
|
|
struct opvar *parm;
|
|
OV_pop(parm);
|
|
switch (OV_i(varparam)) {
|
|
case SP_O_V_NAME:
|
|
if ((OV_typ(parm) == SPOVAR_STRING) &&
|
|
!tmpobj.name.str)
|
|
tmpobj.name.str = strdup(OV_s(parm));
|
|
break;
|
|
case SP_O_V_CORPSENM:
|
|
if (OV_typ(parm) == SPOVAR_MONST) {
|
|
char monclass = SP_MONST_CLASS(OV_i(parm));
|
|
int monid = SP_MONST_PM(OV_i(parm));
|
|
if (monid >= 0 && monid < NUMMONS) {
|
|
tmpobj.corpsenm = monid;
|
|
break; /* we're done! */
|
|
} else {
|
|
struct permonst *pm = (struct permonst *)0;
|
|
if (def_char_to_monclass(monclass) != MAXMCLASSES) {
|
|
pm = mkclass(def_char_to_monclass(monclass), G_NOGEN);
|
|
} else {
|
|
pm = rndmonst();
|
|
}
|
|
if (pm)
|
|
tmpobj.corpsenm = monsndx(pm);
|
|
}
|
|
}
|
|
break;
|
|
case SP_O_V_CURSE:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.curse_state = OV_i(parm);
|
|
break;
|
|
case SP_O_V_SPE:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.spe = OV_i(parm);
|
|
break;
|
|
case SP_O_V_QUAN:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.quan = OV_i(parm);
|
|
break;
|
|
case SP_O_V_BURIED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.buried = OV_i(parm);
|
|
break;
|
|
case SP_O_V_LIT:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.lit = OV_i(parm);
|
|
break;
|
|
case SP_O_V_ERODED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.eroded = OV_i(parm);
|
|
break;
|
|
case SP_O_V_LOCKED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.locked = OV_i(parm);
|
|
break;
|
|
case SP_O_V_TRAPPED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.trapped = OV_i(parm);
|
|
break;
|
|
case SP_O_V_RECHARGED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.recharged = OV_i(parm);
|
|
break;
|
|
case SP_O_V_INVIS:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.invis = OV_i(parm);
|
|
break;
|
|
case SP_O_V_GREASED:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.greased = OV_i(parm);
|
|
break;
|
|
case SP_O_V_BROKEN:
|
|
if (OV_typ(parm) == SPOVAR_INT)
|
|
tmpobj.broken = OV_i(parm);
|
|
break;
|
|
case SP_O_V_COORD:
|
|
if (OV_typ(parm) != SPOVAR_COORD)
|
|
panic("no coord for obj?");
|
|
tmpobj.coord = OV_i(parm);
|
|
break;
|
|
case SP_O_V_END:
|
|
nparams = SP_O_V_END+1;
|
|
break;
|
|
default:
|
|
impossible("OBJECT with unknown variable param type!");
|
|
break;
|
|
}
|
|
opvar_free(parm);
|
|
if (OV_i(varparam) != SP_O_V_END) {
|
|
opvar_free(varparam);
|
|
OV_pop(varparam);
|
|
}
|
|
}
|
|
|
|
if (!OV_pop_typ(id, SPOVAR_OBJ)) panic("no obj type");
|
|
|
|
tmpobj.id = SP_OBJ_TYP(OV_i(id));
|
|
tmpobj.class = SP_OBJ_CLASS(OV_i(id));
|
|
tmpobj.containment = OV_i(containment);
|
|
|
|
quancnt = (tmpobj.id > STRANGE_OBJECT) ? tmpobj.quan : 0;
|
|
|
|
do {
|
|
create_object(&tmpobj, coder->croom);
|
|
quancnt--;
|
|
} while ((quancnt > 0) &&
|
|
((tmpobj.id > STRANGE_OBJECT) &&
|
|
!objects[tmpobj.id].oc_merge));
|
|
|
|
Free(tmpobj.name.str);
|
|
|
|
opvar_free(varparam);
|
|
opvar_free(id);
|
|
opvar_free(containment);
|
|
}
|
|
|
|
void
|
|
spo_level_flags(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *flagdata;
|
|
long lflags;
|
|
|
|
if (!OV_pop_i(flagdata)) return;
|
|
lflags = OV_i(flagdata);
|
|
|
|
if (lflags & NOTELEPORT) level.flags.noteleport = 1;
|
|
if (lflags & HARDFLOOR) level.flags.hardfloor = 1;
|
|
if (lflags & NOMMAP) level.flags.nommap = 1;
|
|
if (lflags & SHORTSIGHTED) level.flags.shortsighted = 1;
|
|
if (lflags & ARBOREAL) level.flags.arboreal = 1;
|
|
if (lflags & MAZELEVEL) level.flags.is_maze_lev = 1;
|
|
if (lflags & PREMAPPED) coder->premapped = TRUE;
|
|
if (lflags & SHROUD) level.flags.hero_memory = 0;
|
|
if (lflags & GRAVEYARD) level.flags.graveyard = 1;
|
|
if (lflags & ICEDPOOLS) icedpools = TRUE;
|
|
if (lflags & SOLIDIFY) coder->solidify = TRUE;
|
|
|
|
opvar_free(flagdata);
|
|
}
|
|
|
|
void
|
|
spo_initlevel(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
lev_init init_lev;
|
|
struct opvar *init_style, *fg, *bg, *smoothed, *joined, *lit, *walled, *filling;
|
|
|
|
if (!OV_pop_i(fg) ||
|
|
!OV_pop_i(bg) ||
|
|
!OV_pop_i(smoothed) ||
|
|
!OV_pop_i(joined) ||
|
|
!OV_pop_i(lit) ||
|
|
!OV_pop_i(walled) ||
|
|
!OV_pop_i(filling) ||
|
|
!OV_pop_i(init_style)) return;
|
|
|
|
splev_init_present = TRUE;
|
|
|
|
init_lev.init_style = OV_i(init_style);
|
|
init_lev.fg = OV_i(fg);
|
|
init_lev.bg = OV_i(bg);
|
|
init_lev.smoothed = OV_i(smoothed);
|
|
init_lev.joined = OV_i(joined);
|
|
init_lev.lit = OV_i(lit);
|
|
init_lev.walled = OV_i(walled);
|
|
init_lev.filling = OV_i(filling);
|
|
|
|
coder->lvl_is_joined = OV_i(joined);
|
|
|
|
splev_initlev(&init_lev);
|
|
|
|
opvar_free(init_style);
|
|
opvar_free(fg);
|
|
opvar_free(bg);
|
|
opvar_free(smoothed);
|
|
opvar_free(joined);
|
|
opvar_free(lit);
|
|
opvar_free(walled);
|
|
opvar_free(filling);
|
|
}
|
|
|
|
void
|
|
spo_engraving(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *etyp, *txt, *ecoord;
|
|
xchar x,y;
|
|
|
|
if (!OV_pop_i(etyp) ||
|
|
!OV_pop_s(txt) ||
|
|
!OV_pop_c(ecoord)) return;
|
|
|
|
get_location_coord(&x, &y, DRY, coder->croom, OV_i(ecoord));
|
|
make_engr_at(x, y, OV_s(txt), 0L, OV_i(etyp));
|
|
|
|
opvar_free(etyp);
|
|
opvar_free(txt);
|
|
opvar_free(ecoord);
|
|
}
|
|
|
|
void
|
|
spo_mineralize(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *kelp_pool, *kelp_moat, *gold_prob, *gem_prob;
|
|
|
|
if (!OV_pop_i(gem_prob) ||
|
|
!OV_pop_i(gold_prob) ||
|
|
!OV_pop_i(kelp_moat) ||
|
|
!OV_pop_i(kelp_pool)) return;
|
|
|
|
mineralize(OV_i(kelp_pool), OV_i(kelp_moat), OV_i(gold_prob), OV_i(gem_prob), TRUE);
|
|
|
|
opvar_free(gem_prob);
|
|
opvar_free(gold_prob);
|
|
opvar_free(kelp_moat);
|
|
opvar_free(kelp_pool);
|
|
}
|
|
|
|
void
|
|
spo_room(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
if (coder->n_subroom > MAX_NESTED_ROOMS) {
|
|
panic("Too deeply nested rooms?!");
|
|
} else {
|
|
struct opvar *rflags, *h, *w, *yalign, *xalign,
|
|
*y, *x, *rlit, *chance, *rtype;
|
|
|
|
room tmproom;
|
|
struct mkroom *tmpcr;
|
|
|
|
if (!OV_pop_i(h) ||
|
|
!OV_pop_i(w) ||
|
|
!OV_pop_i(y) ||
|
|
!OV_pop_i(x) ||
|
|
!OV_pop_i(yalign) ||
|
|
!OV_pop_i(xalign) ||
|
|
!OV_pop_i(rflags) ||
|
|
!OV_pop_i(rlit) ||
|
|
!OV_pop_i(chance) ||
|
|
!OV_pop_i(rtype)) return;
|
|
|
|
|
|
tmproom.x = OV_i(x);
|
|
tmproom.y = OV_i(y);
|
|
tmproom.w = OV_i(w);
|
|
tmproom.h = OV_i(h);
|
|
tmproom.xalign = OV_i(xalign);
|
|
tmproom.yalign = OV_i(yalign);
|
|
tmproom.rtype = OV_i(rtype);
|
|
tmproom.chance = OV_i(chance);
|
|
tmproom.rlit = OV_i(rlit);
|
|
tmproom.filled = (OV_i(rflags) & (1 << 0));
|
|
/*tmproom.irregular = (OV_i(rflags) & (1 << 1));*/
|
|
tmproom.joined = !(OV_i(rflags) & (1 << 2));
|
|
|
|
opvar_free(x);
|
|
opvar_free(y);
|
|
opvar_free(w);
|
|
opvar_free(h);
|
|
opvar_free(xalign);
|
|
opvar_free(yalign);
|
|
opvar_free(rtype);
|
|
opvar_free(chance);
|
|
opvar_free(rlit);
|
|
opvar_free(rflags);
|
|
|
|
if (!coder->failed_room[coder->n_subroom-1]) {
|
|
tmpcr = build_room(&tmproom, coder->croom);
|
|
if (tmpcr) {
|
|
coder->tmproomlist[coder->n_subroom] = tmpcr;
|
|
coder->failed_room[coder->n_subroom] = FALSE;
|
|
coder->n_subroom++;
|
|
return;
|
|
}
|
|
} /* failed to create parent room, so fail this too */
|
|
}
|
|
coder->tmproomlist[coder->n_subroom] = (struct mkroom *)0;
|
|
coder->failed_room[coder->n_subroom] = TRUE;
|
|
coder->n_subroom++;
|
|
}
|
|
|
|
void
|
|
spo_endroom(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
if (coder->n_subroom > 1) {
|
|
coder->n_subroom--;
|
|
coder->tmproomlist[coder->n_subroom] = NULL;
|
|
coder->failed_room[coder->n_subroom] = TRUE;
|
|
} else {
|
|
/* no subroom, get out of top-level room */
|
|
/* Need to ensure xstart/ystart/xsize/ysize have something sensible,
|
|
in case there's some stuff to be created outside the outermost room,
|
|
and there's no MAP.
|
|
*/
|
|
if(xsize <= 1 && ysize <= 1) {
|
|
xstart = 1;
|
|
ystart = 0;
|
|
xsize = COLNO-1;
|
|
ysize = ROWNO;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
spo_stair(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
xchar x,y;
|
|
struct opvar *up, *scoord;
|
|
struct trap *badtrap;
|
|
|
|
if (!OV_pop_i(up) ||
|
|
!OV_pop_c(scoord)) return;
|
|
|
|
get_location_coord(&x, &y, DRY, coder->croom, OV_i(scoord));
|
|
if ((badtrap = t_at(x,y)) != 0) deltrap(badtrap);
|
|
mkstairs(x, y, (char)OV_i(up), coder->croom);
|
|
SpLev_Map[x][y] = 1;
|
|
|
|
opvar_free(scoord);
|
|
opvar_free(up);
|
|
}
|
|
|
|
void
|
|
spo_ladder(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
xchar x,y;
|
|
struct opvar *up, *lcoord;
|
|
|
|
if (!OV_pop_i(up) ||
|
|
!OV_pop_c(lcoord)) return;
|
|
|
|
get_location_coord(&x, &y, DRY, coder->croom, OV_i(lcoord));
|
|
|
|
levl[x][y].typ = LADDER;
|
|
SpLev_Map[x][y] = 1;
|
|
if (OV_i(up)) {
|
|
xupladder = x; yupladder = y;
|
|
levl[x][y].ladder = LA_UP;
|
|
} else {
|
|
xdnladder = x; ydnladder = y;
|
|
levl[x][y].ladder = LA_DOWN;
|
|
}
|
|
opvar_free(lcoord);
|
|
opvar_free(up);
|
|
}
|
|
|
|
void
|
|
spo_grave(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *gcoord, *typ, *txt;
|
|
schar x,y;
|
|
if (!OV_pop_i(typ) ||
|
|
!OV_pop_s(txt) ||
|
|
!OV_pop_c(gcoord)) return;
|
|
|
|
get_location_coord(&x, &y, DRY, coder->croom, OV_i(gcoord));
|
|
|
|
if (isok(x, y) && !t_at(x, y)) {
|
|
levl[x][y].typ = GRAVE;
|
|
switch (OV_i(typ)) {
|
|
case 2: make_grave(x, y, OV_s(txt)); break;
|
|
case 1: make_grave(x, y, NULL); break;
|
|
default: del_engr_at(x, y); break;
|
|
}
|
|
}
|
|
|
|
opvar_free(gcoord);
|
|
opvar_free(typ);
|
|
opvar_free(txt);
|
|
}
|
|
|
|
void
|
|
spo_altar(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *al, *shrine, *acoord;
|
|
altar tmpaltar;
|
|
|
|
if (!OV_pop_i(al) ||
|
|
!OV_pop_i(shrine) ||
|
|
!OV_pop_c(acoord)) return;
|
|
|
|
tmpaltar.coord = OV_i(acoord);
|
|
tmpaltar.align = OV_i(al);
|
|
tmpaltar.shrine = OV_i(shrine);
|
|
|
|
create_altar(&tmpaltar, coder->croom);
|
|
|
|
opvar_free(acoord);
|
|
opvar_free(shrine);
|
|
opvar_free(al);
|
|
}
|
|
|
|
void
|
|
spo_trap(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *type;
|
|
struct opvar *tcoord;
|
|
trap tmptrap;
|
|
|
|
if (!OV_pop_i(type) ||
|
|
!OV_pop_c(tcoord)) return;
|
|
|
|
tmptrap.coord = OV_i(tcoord);
|
|
tmptrap.type = OV_i(type);
|
|
|
|
create_trap(&tmptrap, coder->croom);
|
|
opvar_free(tcoord);
|
|
opvar_free(type);
|
|
}
|
|
|
|
void
|
|
spo_gold(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *gcoord, *amt;
|
|
schar x,y;
|
|
long amount;
|
|
|
|
if (!OV_pop_c(gcoord) || !OV_pop_i(amt)) return;
|
|
amount = OV_i(amt);
|
|
get_location_coord(&x, &y, DRY, coder->croom, OV_i(gcoord));
|
|
if (amount == -1) amount = rnd(200);
|
|
mkgold(amount, x,y);
|
|
opvar_free(gcoord);
|
|
opvar_free(amt);
|
|
}
|
|
|
|
void
|
|
spo_corridor(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *deswall, *desdoor, *desroom,
|
|
*srcwall, *srcdoor, *srcroom;
|
|
corridor tc;
|
|
|
|
if (!OV_pop_i(deswall) ||
|
|
!OV_pop_i(desdoor) ||
|
|
!OV_pop_i(desroom) ||
|
|
!OV_pop_i(srcwall) ||
|
|
!OV_pop_i(srcdoor) ||
|
|
!OV_pop_i(srcroom)) return;
|
|
|
|
tc.src.room = OV_i(srcroom);
|
|
tc.src.door = OV_i(srcdoor);
|
|
tc.src.wall = OV_i(srcwall);
|
|
tc.dest.room = OV_i(desroom);
|
|
tc.dest.door = OV_i(desdoor);
|
|
tc.dest.wall = OV_i(deswall);
|
|
|
|
create_corridor(&tc);
|
|
|
|
opvar_free(deswall);
|
|
opvar_free(desdoor);
|
|
opvar_free(desroom);
|
|
opvar_free(srcwall);
|
|
opvar_free(srcdoor);
|
|
opvar_free(srcroom);
|
|
}
|
|
|
|
|
|
struct opvar *
|
|
selection_opvar(nbuf)
|
|
char *nbuf;
|
|
{
|
|
struct opvar *ov;
|
|
char buf[(COLNO*ROWNO)+1];
|
|
|
|
if (!nbuf) {
|
|
(void) memset(buf, 1, sizeof(buf));
|
|
buf[(COLNO*ROWNO)] = '\0';
|
|
ov = opvar_new_str(buf);
|
|
} else {
|
|
ov = opvar_new_str(nbuf);
|
|
}
|
|
ov->spovartyp = SPOVAR_SEL;
|
|
return ov;
|
|
}
|
|
|
|
xchar
|
|
selection_getpoint(x,y,ov)
|
|
int x,y;
|
|
struct opvar *ov;
|
|
{
|
|
if (!ov || ov->spovartyp != SPOVAR_SEL) return 0;
|
|
if (x < 0 || y < 0 || x >= COLNO || y >= ROWNO) return 0;
|
|
|
|
return (ov->vardata.str[COLNO*y + x] - 1);
|
|
}
|
|
|
|
void
|
|
selection_setpoint(x,y,ov, c)
|
|
int x,y;
|
|
struct opvar *ov;
|
|
xchar c;
|
|
{
|
|
if (!ov || ov->spovartyp != SPOVAR_SEL) return;
|
|
if (x < 0 || y < 0 || x >= COLNO || y >= ROWNO) return;
|
|
|
|
ov->vardata.str[COLNO*y + x] = (char)(c + 1);
|
|
}
|
|
|
|
struct opvar *
|
|
selection_not(s)
|
|
struct opvar *s;
|
|
{
|
|
struct opvar *ov;
|
|
int x,y;
|
|
ov = selection_opvar(NULL);
|
|
if (!ov) return NULL;
|
|
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
if (!selection_getpoint(x,y,s))
|
|
selection_setpoint(x,y,ov,1);
|
|
|
|
return ov;
|
|
}
|
|
|
|
struct opvar *
|
|
selection_logical_oper(s1, s2, oper)
|
|
struct opvar *s1, *s2;
|
|
char oper;
|
|
{
|
|
struct opvar *ov;
|
|
int x,y;
|
|
|
|
ov = selection_opvar(NULL);
|
|
if (!ov) return NULL;
|
|
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++) {
|
|
switch (oper) {
|
|
default:
|
|
case '|':
|
|
if (selection_getpoint(x,y,s1) || selection_getpoint(x,y,s2))
|
|
selection_setpoint(x,y,ov,1);
|
|
break;
|
|
case '&':
|
|
if (selection_getpoint(x,y,s1) && selection_getpoint(x,y,s2))
|
|
selection_setpoint(x,y,ov,1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ov;
|
|
}
|
|
|
|
struct opvar *
|
|
selection_filter_mapchar(ov, mc)
|
|
struct opvar *ov;
|
|
struct opvar *mc;
|
|
{
|
|
int x,y;
|
|
schar mapc;
|
|
xchar lit;
|
|
struct opvar *ret = selection_opvar(NULL);
|
|
if (!ov || !mc || !ret) return NULL;
|
|
mapc = SP_MAPCHAR_TYP(OV_i(mc));
|
|
lit = SP_MAPCHAR_LIT(OV_i(mc));
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
if (selection_getpoint(x,y,ov) && (levl[x][y].typ == mapc)) {
|
|
switch (lit) {
|
|
default:
|
|
case -2: selection_setpoint(x,y,ret, 1); break;
|
|
case -1: selection_setpoint(x,y,ret, rn2(2)); break;
|
|
case 0:
|
|
case 1: if (levl[x][y].lit == lit) selection_setpoint(x,y,ret, 1); break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
selection_filter_percent(ov, percent)
|
|
struct opvar *ov;
|
|
int percent;
|
|
{
|
|
int x,y;
|
|
if (!ov) return;
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
if (selection_getpoint(x,y,ov) && (rn2(100) >= percent))
|
|
selection_setpoint(x,y,ov,0);
|
|
}
|
|
|
|
int
|
|
selection_rndcoord(ov, x,y)
|
|
struct opvar *ov;
|
|
schar *x, *y;
|
|
{
|
|
int idx = 0;
|
|
int c;
|
|
int dx,dy;
|
|
|
|
for (dx = 0; dx < COLNO; dx++)
|
|
for (dy = 0; dy < ROWNO; dy++)
|
|
if (isok(dx,dy) && selection_getpoint(dx,dy,ov)) idx++;
|
|
|
|
if (idx) {
|
|
c = rn2(idx);
|
|
for (dx = 0; dx < COLNO; dx++)
|
|
for (dy = 0; dy < ROWNO; dy++)
|
|
if (isok(dx,dy) && selection_getpoint(dx,dy, ov)) {
|
|
if (!c) {
|
|
*x = dx;
|
|
*y = dy;
|
|
return 1;
|
|
}
|
|
c--;
|
|
}
|
|
}
|
|
*x = *y = -1;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
selection_do_grow(ov, dir)
|
|
struct opvar *ov;
|
|
int dir;
|
|
{
|
|
int x,y, c;
|
|
char tmp[COLNO][ROWNO];
|
|
|
|
if (ov->spovartyp != SPOVAR_SEL) return;
|
|
if (!ov) return;
|
|
|
|
(void) memset(tmp, 0, sizeof(tmp));
|
|
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++) {
|
|
c = 0;
|
|
if ((dir & W_WEST) && (x > 0) && (selection_getpoint(x-1,y, ov))) c++;
|
|
if ((dir & (W_WEST|W_NORTH)) && (x > 0) && (y > 0) && (selection_getpoint(x-1,y-1, ov))) c++;
|
|
if ((dir & W_NORTH) && (y > 0) && (selection_getpoint(x,y-1, ov))) c++;
|
|
if ((dir & (W_NORTH|W_EAST)) && (y > 0) && (x < COLNO-1) && (selection_getpoint(x+1,y-1, ov))) c++;
|
|
if ((dir & W_EAST) && (x < COLNO-1) && (selection_getpoint(x+1,y, ov))) c++;
|
|
if ((dir & (W_EAST|W_SOUTH)) && (x < COLNO-1) && (y < ROWNO-1) && (selection_getpoint(x+1,y+1, ov))) c++;
|
|
if ((dir & W_SOUTH) && (y < ROWNO-1) && (selection_getpoint(x,y+1, ov))) c++;
|
|
if ((dir & (W_SOUTH|W_WEST)) && (y < ROWNO-1) && (x > 0) && (selection_getpoint(x-1,y+1, ov))) c++;
|
|
if (c) tmp[x][y] = 1;
|
|
}
|
|
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
if (tmp[x][y]) selection_setpoint(x,y,ov,1);
|
|
}
|
|
|
|
void
|
|
selection_floodfill(ov, x,y)
|
|
struct opvar *ov;
|
|
int x,y;
|
|
{
|
|
struct opvar *tmp = selection_opvar(NULL);
|
|
#define SEL_FLOOD_STACK (COLNO*ROWNO)
|
|
#define SEL_FLOOD(nx,ny) {if (idx<SEL_FLOOD_STACK) { dx[idx]=(nx); dy[idx]=(ny); idx++; } else panic("floodfill stack overrun");}
|
|
int idx = 0;
|
|
xchar dx[SEL_FLOOD_STACK];
|
|
xchar dy[SEL_FLOOD_STACK];
|
|
schar under = levl[x][y].typ;
|
|
SEL_FLOOD(x,y);
|
|
do {
|
|
idx--;
|
|
x = dx[idx];
|
|
y = dy[idx];
|
|
if (isok(x,y)) {
|
|
selection_setpoint(x,y, ov, 1);
|
|
selection_setpoint(x,y, tmp, 1);
|
|
}
|
|
if (isok(x+1,y) && (levl[x+1][y].typ == under) && !selection_getpoint(x+1,y,tmp)) SEL_FLOOD(x+1, y);
|
|
if (isok(x-1,y) && (levl[x-1][y].typ == under) && !selection_getpoint(x-1,y,tmp)) SEL_FLOOD(x-1, y);
|
|
if (isok(x,y+1) && (levl[x][y+1].typ == under) && !selection_getpoint(x,y+1,tmp)) SEL_FLOOD(x, y+1);
|
|
if (isok(x,y-1) && (levl[x][y-1].typ == under) && !selection_getpoint(x,y-1,tmp)) SEL_FLOOD(x, y-1);
|
|
} while (idx > 0);
|
|
#undef SEL_FLOOD
|
|
#undef SEL_FLOOD_STACK
|
|
opvar_free(tmp);
|
|
}
|
|
|
|
/* McIlroy's Ellipse Algorithm */
|
|
void
|
|
selection_do_ellipse(ov, xc,yc, a,b, filled)
|
|
struct opvar *ov;
|
|
int xc,yc,a,b,filled;
|
|
{ /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */
|
|
int x = 0, y = b;
|
|
long a2 = (long)a*a, b2 = (long)b*b;
|
|
long crit1 = -(a2/4 + a%2 + b2);
|
|
long crit2 = -(b2/4 + b%2 + a2);
|
|
long crit3 = -(b2/4 + b%2);
|
|
long t = -a2*y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */
|
|
long dxt = 2*b2*x, dyt = -2*a2*y;
|
|
long d2xt = 2*b2, d2yt = 2*a2;
|
|
long width = 1;
|
|
long i;
|
|
|
|
if (!ov) return;
|
|
|
|
filled = !filled;
|
|
|
|
if (!filled) {
|
|
while (y>=0 && x<=a) {
|
|
selection_setpoint(xc+x, yc+y, ov, 1);
|
|
if (x!=0 || y!=0)
|
|
selection_setpoint(xc-x, yc-y, ov, 1);
|
|
if (x!=0 && y!=0) {
|
|
selection_setpoint(xc+x, yc-y, ov, 1);
|
|
selection_setpoint(xc-x, yc+y, ov, 1);
|
|
}
|
|
if (t + b2*x <= crit1 || /* e(x+1,y-1/2) <= 0 */
|
|
t + a2*y <= crit3) { /* e(x+1/2,y) <= 0 */
|
|
x++; dxt += d2xt; t += dxt;
|
|
} else if (t - a2*y > crit2) { /* e(x+1/2,y-1) > 0 */
|
|
y--; dyt += d2yt; t += dyt;
|
|
} else {
|
|
x++; dxt += d2xt; t += dxt;
|
|
y--; dyt += d2yt; t += dyt;
|
|
}
|
|
}
|
|
} else {
|
|
while (y>=0 && x<=a) {
|
|
if (t + b2*x <= crit1 || /* e(x+1,y-1/2) <= 0 */
|
|
t + a2*y <= crit3) { /* e(x+1/2,y) <= 0 */
|
|
x++; dxt += d2xt; t += dxt;
|
|
width += 2;
|
|
} else if (t - a2*y > crit2) { /* e(x+1/2,y-1) > 0 */
|
|
for (i = 0; i < width; i++) selection_setpoint(xc-x+i, yc-y, ov, 1);
|
|
if (y!=0)
|
|
for (i = 0; i < width; i++) selection_setpoint(xc-x+i, yc+y, ov, 1);
|
|
y--; dyt += d2yt; t += dyt;
|
|
} else {
|
|
for (i = 0; i < width; i++) selection_setpoint(xc-x+i, yc-y, ov, 1);
|
|
if (y!=0)
|
|
for (i = 0; i < width; i++) selection_setpoint(xc-x+i, yc+y, ov, 1);
|
|
x++; dxt += d2xt; t += dxt;
|
|
y--; dyt += d2yt; t += dyt;
|
|
width += 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* distance from line segment (x1,y1, x2,y2) to point (x3,y3) */
|
|
long
|
|
line_dist_coord(x1,y1, x2,y2, x3,y3)
|
|
long x1,y1,x2,y2,x3,y3;
|
|
{
|
|
long px = x2-x1;
|
|
long py = y2-y1;
|
|
long s = px*px + py*py;
|
|
long x, y, dx, dy, dist = 0;
|
|
float lu = 0;
|
|
|
|
if (x1 == x2 && y1 == y2) return isqrt(dist2(x1,y1, x3,y3));
|
|
|
|
lu = ((x3 - x1) * px + (y3 - y1) * py) / (float)s;
|
|
if (lu > 1) lu = 1;
|
|
else if (lu < 0) lu = 0;
|
|
|
|
x = x1 + lu * px;
|
|
y = y1 + lu * py;
|
|
dx = x - x3;
|
|
dy = y - y3;
|
|
dist = isqrt(dx*dx + dy*dy);
|
|
|
|
return dist;
|
|
}
|
|
|
|
void
|
|
selection_do_gradient(ov, x,y, x2,y2, gtyp, mind, maxd, limit)
|
|
struct opvar *ov;
|
|
long x, y, x2,y2, gtyp, mind, maxd, limit;
|
|
{
|
|
long dx,dy, dofs;
|
|
|
|
if (mind > maxd) {
|
|
long tmp = mind;
|
|
mind = maxd;
|
|
maxd = tmp;
|
|
}
|
|
|
|
dofs = maxd - mind;
|
|
if (dofs < 1) dofs = 1;
|
|
|
|
switch (gtyp) {
|
|
default:
|
|
case SEL_GRADIENT_RADIAL:
|
|
{
|
|
for (dx = 0; dx < COLNO; dx++)
|
|
for (dy = 0; dy < ROWNO; dy++) {
|
|
long d = line_dist_coord(x,y, x2,y2, dx,dy);
|
|
if (d >= mind && (!limit || (d <= maxd))) {
|
|
if ((d - mind) > rn2(dofs))
|
|
selection_setpoint(dx,dy, ov, 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SEL_GRADIENT_SQUARE:
|
|
{
|
|
for (dx = 0; dx < COLNO; dx++)
|
|
for (dy = 0; dy < ROWNO; dy++) {
|
|
long d1 = line_dist_coord(x,y, x2,y2, x,dy);
|
|
long d2 = line_dist_coord(x,y, x2,y2, dx,y);
|
|
long d3 = line_dist_coord(x,y, x2,y2, x2,dy);
|
|
long d4 = line_dist_coord(x,y, x2,y2, dx,y2);
|
|
long d5 = line_dist_coord(x,y, x2,y2, dx,dy);
|
|
long d = min(d5, min(max(d1, d2),max(d3,d4)));
|
|
|
|
if (d >= mind && (!limit || (d <= maxd))) {
|
|
if ((d - mind) > rn2(dofs))
|
|
selection_setpoint(dx,dy, ov, 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
selection_do_line(x1,y1,x2,y2, ov) /* bresenham line algo */
|
|
schar x1,y1,x2,y2;
|
|
struct opvar *ov;
|
|
{
|
|
int d,dx,dy,ai,bi,xi,yi;
|
|
|
|
if (x1 < x2) {
|
|
xi = 1;
|
|
dx = x2 - x1;
|
|
} else {
|
|
xi = - 1;
|
|
dx = x1 - x2;
|
|
}
|
|
|
|
if (y1 < y2) {
|
|
yi = 1;
|
|
dy = y2 - y1;
|
|
} else {
|
|
yi = - 1;
|
|
dy = y1 - y2;
|
|
}
|
|
|
|
selection_setpoint(x1,y1, ov, 1);
|
|
|
|
if (dx > dy) {
|
|
ai = (dy - dx) * 2;
|
|
bi = dy * 2;
|
|
d = bi - dx;
|
|
do {
|
|
if (d >= 0) {
|
|
y1 += yi;
|
|
d += ai;
|
|
} else d += bi;
|
|
x1 += xi;
|
|
selection_setpoint(x1,y1, ov, 1);
|
|
} while (x1 != x2);
|
|
} else {
|
|
ai = (dx - dy) * 2;
|
|
bi = dx * 2;
|
|
d = bi - dy;
|
|
do {
|
|
if (d >= 0) {
|
|
x1 += xi;
|
|
d += ai;
|
|
} else d += bi;
|
|
y1 += yi;
|
|
selection_setpoint(x1,y1, ov, 1);
|
|
} while (y1 != y2);
|
|
}
|
|
}
|
|
|
|
void
|
|
selection_do_randline(x1,y1,x2,y2,rough, rec, ov)
|
|
schar x1,y1,x2,y2,rough,rec;
|
|
struct opvar *ov;
|
|
{
|
|
int mx, my;
|
|
int dx, dy;
|
|
|
|
if (rec < 1) {
|
|
return;
|
|
}
|
|
|
|
if ((x2 == x1) && (y2 == y1)) {
|
|
selection_setpoint(x1,y1, ov, 1);
|
|
return;
|
|
}
|
|
|
|
if (rough > max(abs(x2-x1), abs(y2-y1)))
|
|
rough = max(abs(x2-x1), abs(y2-y1));
|
|
|
|
if (rough < 2) {
|
|
mx = ((x1 + x2) / 2);
|
|
my = ((y1 + y2) / 2);
|
|
} else {
|
|
do {
|
|
dx = (rand() % rough) - (rough / 2);
|
|
dy = (rand() % rough) - (rough / 2);
|
|
mx = ((x1 + x2) / 2) + dx;
|
|
my = ((y1 + y2) / 2) + dy;
|
|
} while ((mx > COLNO-1 || mx < 0 || my < 0 || my > ROWNO-1));
|
|
}
|
|
|
|
selection_setpoint(mx,my, ov, 1);
|
|
|
|
rough = (rough * 2) / 3;
|
|
|
|
rec--;
|
|
|
|
selection_do_randline(x1,y1,mx,my, rough, rec, ov);
|
|
selection_do_randline(mx,my,x2,y2, rough, rec, ov);
|
|
}
|
|
|
|
|
|
void
|
|
selection_iterate(ov, func, arg)
|
|
struct opvar *ov;
|
|
select_iter_func func;
|
|
genericptr_t arg;
|
|
{
|
|
int x,y;
|
|
/* yes, this is very naive, but it's not _that_ expensive. */
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
if (selection_getpoint(x,y, ov)) (*func)(x,y, arg);
|
|
}
|
|
|
|
void
|
|
sel_set_ter(x,y,arg)
|
|
int x,y;
|
|
genericptr_t arg;
|
|
{
|
|
terrain terr;
|
|
terr = (*(terrain *)arg);
|
|
SET_TYPLIT(x,y, terr.ter, terr.tlit);
|
|
/* handle doors and secret doors */
|
|
if (levl[x][y].typ == SDOOR || IS_DOOR(levl[x][y].typ)) {
|
|
if(levl[x][y].typ == SDOOR)
|
|
levl[x][y].doormask = D_CLOSED;
|
|
if (x && (IS_WALL(levl[x-1][y].typ) ||
|
|
levl[x-1][y].horizontal))
|
|
levl[x][y].horizontal = 1;
|
|
}
|
|
}
|
|
|
|
void
|
|
sel_set_feature(x,y,arg)
|
|
int x,y;
|
|
genericptr_t arg;
|
|
{
|
|
if (IS_FURNITURE(levl[x][y].typ)) return;
|
|
levl[x][y].typ = (*(int *)arg);
|
|
}
|
|
|
|
void
|
|
sel_set_door(dx,dy,arg)
|
|
int dx,dy;
|
|
genericptr_t arg;
|
|
{
|
|
xchar typ = (*(xchar *)arg);
|
|
xchar x = dx;
|
|
xchar y = dy;
|
|
if (!IS_DOOR(levl[x][y].typ) && levl[x][y].typ != SDOOR)
|
|
levl[x][y].typ = (typ & D_SECRET) ? SDOOR : DOOR;
|
|
if (typ & D_SECRET) {
|
|
typ &= ~D_SECRET;
|
|
if (typ < D_CLOSED)
|
|
typ = D_CLOSED;
|
|
}
|
|
levl[x][y].doormask = typ;
|
|
SpLev_Map[x][y] = 1;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
spo_door(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *msk, *sel;
|
|
xchar typ;
|
|
|
|
if (!OV_pop_i(msk) ||
|
|
!OV_pop_typ(sel, SPOVAR_SEL)) return;
|
|
|
|
typ = OV_i(msk) == -1 ? rnddoor() : (xchar)OV_i(msk);
|
|
|
|
selection_iterate(sel, sel_set_door, (genericptr_t)&typ);
|
|
|
|
opvar_free(sel);
|
|
opvar_free(msk);
|
|
}
|
|
|
|
void
|
|
spo_feature(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *sel;
|
|
int typ;
|
|
|
|
if (!OV_pop_typ(sel, SPOVAR_SEL)) return;
|
|
|
|
switch (coder->opcode) {
|
|
default: impossible("spo_feature called with wrong opcode %i.", coder->opcode); break;
|
|
case SPO_FOUNTAIN: typ = FOUNTAIN; break;
|
|
case SPO_SINK: typ = SINK; break;
|
|
case SPO_POOL: typ = POOL; break;
|
|
}
|
|
selection_iterate(sel, sel_set_feature, (genericptr_t)&typ);
|
|
opvar_free(sel);
|
|
}
|
|
|
|
void
|
|
spo_terrain(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
terrain tmpterrain;
|
|
struct opvar *ter, *sel;
|
|
|
|
if (!OV_pop_typ(ter, SPOVAR_MAPCHAR) ||
|
|
!OV_pop_typ(sel, SPOVAR_SEL)) return;
|
|
|
|
tmpterrain.ter = SP_MAPCHAR_TYP(OV_i(ter));
|
|
tmpterrain.tlit = SP_MAPCHAR_LIT(OV_i(ter));
|
|
selection_iterate(sel, sel_set_ter, (genericptr_t)&tmpterrain);
|
|
|
|
opvar_free(ter);
|
|
opvar_free(sel);
|
|
}
|
|
|
|
void
|
|
spo_replace_terrain(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
replaceterrain rt;
|
|
struct opvar *reg,*from_ter,*to_ter,*chance;
|
|
|
|
if (!OV_pop_i(chance) ||
|
|
!OV_pop_typ(to_ter, SPOVAR_MAPCHAR) ||
|
|
!OV_pop_typ(from_ter, SPOVAR_MAPCHAR) ||
|
|
!OV_pop_r(reg)) return;
|
|
|
|
rt.chance = OV_i(chance);
|
|
rt.tolit = SP_MAPCHAR_LIT(OV_i(to_ter));
|
|
rt.toter = SP_MAPCHAR_TYP(OV_i(to_ter));
|
|
rt.fromter = SP_MAPCHAR_TYP(OV_i(from_ter));
|
|
/* TODO: use SP_MAPCHAR_LIT(OV_i(from_ter)) too */
|
|
rt.x1 = SP_REGION_X1(OV_i(reg));
|
|
rt.y1 = SP_REGION_Y1(OV_i(reg));
|
|
rt.x2 = SP_REGION_X2(OV_i(reg));
|
|
rt.y2 = SP_REGION_Y2(OV_i(reg));
|
|
|
|
replace_terrain(&rt, coder->croom);
|
|
|
|
opvar_free(reg);
|
|
opvar_free(from_ter);
|
|
opvar_free(to_ter);
|
|
opvar_free(chance);
|
|
}
|
|
|
|
void
|
|
spo_levregion(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *rname, *padding, *rtype,
|
|
*del_islev, *dy2, *dx2, *dy1, *dx1,
|
|
*in_islev, *iy2, *ix2, *iy1, *ix1;
|
|
|
|
lev_region *tmplregion;
|
|
|
|
if (!OV_pop_s(rname) ||
|
|
!OV_pop_i(padding) ||
|
|
!OV_pop_i(rtype) ||
|
|
!OV_pop_i(del_islev) ||
|
|
!OV_pop_i(dy2) ||
|
|
!OV_pop_i(dx2) ||
|
|
!OV_pop_i(dy1) ||
|
|
!OV_pop_i(dx1) ||
|
|
!OV_pop_i(in_islev) ||
|
|
!OV_pop_i(iy2) ||
|
|
!OV_pop_i(ix2) ||
|
|
!OV_pop_i(iy1) ||
|
|
!OV_pop_i(ix1)) return;
|
|
|
|
tmplregion = (lev_region *)alloc(sizeof(lev_region));
|
|
if (!tmplregion) panic("levreg alloc");
|
|
tmplregion->inarea.x1 = OV_i(ix1);
|
|
tmplregion->inarea.y1 = OV_i(iy1);
|
|
tmplregion->inarea.x2 = OV_i(ix2);
|
|
tmplregion->inarea.y2 = OV_i(iy2);
|
|
|
|
tmplregion->delarea.x1 = OV_i(dx1);
|
|
tmplregion->delarea.y1 = OV_i(dy1);
|
|
tmplregion->delarea.x2 = OV_i(dx2);
|
|
tmplregion->delarea.y2 = OV_i(dy2);
|
|
|
|
tmplregion->in_islev = OV_i(in_islev);
|
|
tmplregion->del_islev = OV_i(del_islev);
|
|
tmplregion->rtype = OV_i(rtype);
|
|
tmplregion->padding = OV_i(padding);
|
|
tmplregion->rname.str = strdup(OV_s(rname));
|
|
|
|
if(!tmplregion->in_islev) {
|
|
get_location(&tmplregion->inarea.x1, &tmplregion->inarea.y1,
|
|
ANY_LOC, (struct mkroom *)0);
|
|
get_location(&tmplregion->inarea.x2, &tmplregion->inarea.y2,
|
|
ANY_LOC, (struct mkroom *)0);
|
|
}
|
|
|
|
if(!tmplregion->del_islev) {
|
|
get_location(&tmplregion->delarea.x1, &tmplregion->delarea.y1,
|
|
ANY_LOC, (struct mkroom *)0);
|
|
get_location(&tmplregion->delarea.x2, &tmplregion->delarea.y2,
|
|
ANY_LOC, (struct mkroom *)0);
|
|
}
|
|
if(num_lregions) {
|
|
/* realloc the lregion space to add the new one */
|
|
lev_region *newl = (lev_region *) alloc(sizeof(lev_region) *
|
|
(unsigned)(1+num_lregions));
|
|
if (!newl) panic("levreg newl alloc");
|
|
(void) memcpy((genericptr_t)(newl), (genericptr_t)lregions,
|
|
sizeof(lev_region) * num_lregions);
|
|
Free(lregions);
|
|
num_lregions++;
|
|
lregions = newl;
|
|
} else {
|
|
num_lregions = 1;
|
|
lregions = (lev_region *) alloc(sizeof(lev_region));
|
|
if (!lregions) panic("lregions alloc");
|
|
}
|
|
(void) memcpy(&lregions[num_lregions-1], tmplregion, sizeof(lev_region));
|
|
|
|
opvar_free(dx1);
|
|
opvar_free(dy1);
|
|
opvar_free(dx2);
|
|
opvar_free(dy2);
|
|
|
|
opvar_free(ix1);
|
|
opvar_free(iy1);
|
|
opvar_free(ix2);
|
|
opvar_free(iy2);
|
|
|
|
opvar_free(del_islev);
|
|
opvar_free(in_islev);
|
|
opvar_free(rname);
|
|
opvar_free(rtype);
|
|
opvar_free(padding);
|
|
}
|
|
|
|
void
|
|
spo_region(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *rtype, *rlit, *rflags, *area;
|
|
xchar dx1,dy1,dx2,dy2;
|
|
register struct mkroom *troom;
|
|
boolean prefilled, room_not_needed, irregular, joined;
|
|
|
|
if (!OV_pop_i(rflags) ||
|
|
!OV_pop_i(rtype) ||
|
|
!OV_pop_i(rlit) ||
|
|
!OV_pop_r(area)) return;
|
|
|
|
prefilled = !(OV_i(rflags) & (1 << 0));
|
|
irregular = (OV_i(rflags) & (1 << 1));
|
|
joined = !(OV_i(rflags) & (1 << 2));
|
|
|
|
if(OV_i(rtype) > MAXRTYPE) {
|
|
OV_i(rtype) -= MAXRTYPE+1;
|
|
prefilled = TRUE;
|
|
} else
|
|
prefilled = FALSE;
|
|
|
|
if(OV_i(rlit) < 0)
|
|
OV_i(rlit) = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77))
|
|
? TRUE : FALSE;
|
|
|
|
dx1 = SP_REGION_X1(OV_i(area));
|
|
dy1 = SP_REGION_Y1(OV_i(area));
|
|
dx2 = SP_REGION_X2(OV_i(area));
|
|
dy2 = SP_REGION_Y2(OV_i(area));
|
|
|
|
get_location(&dx1, &dy1, ANY_LOC, (struct mkroom *)0);
|
|
get_location(&dx2, &dy2, ANY_LOC, (struct mkroom *)0);
|
|
|
|
/* for an ordinary room, `prefilled' is a flag to force
|
|
an actual room to be created (such rooms are used to
|
|
control placement of migrating monster arrivals) */
|
|
room_not_needed = (OV_i(rtype) == OROOM &&
|
|
!irregular && !prefilled);
|
|
if (room_not_needed || nroom >= MAXNROFROOMS) {
|
|
region tmpregion;
|
|
if (!room_not_needed)
|
|
impossible("Too many rooms on new level!");
|
|
tmpregion.rlit = OV_i(rlit);
|
|
tmpregion.x1 = dx1;
|
|
tmpregion.y1 = dy1;
|
|
tmpregion.x2 = dx2;
|
|
tmpregion.y2 = dy2;
|
|
light_region(&tmpregion);
|
|
|
|
opvar_free(area);
|
|
opvar_free(rflags);
|
|
opvar_free(rlit);
|
|
opvar_free(rtype);
|
|
|
|
return;
|
|
}
|
|
|
|
troom = &rooms[nroom];
|
|
|
|
/* mark rooms that must be filled, but do it later */
|
|
if (OV_i(rtype) != OROOM)
|
|
troom->needfill = (prefilled ? 2 : 1);
|
|
|
|
troom->needjoining = joined;
|
|
|
|
if (irregular) {
|
|
min_rx = max_rx = dx1;
|
|
min_ry = max_ry = dy1;
|
|
smeq[nroom] = nroom;
|
|
flood_fill_rm(dx1, dy1, nroom+ROOMOFFSET,
|
|
OV_i(rlit), TRUE);
|
|
add_room(min_rx, min_ry, max_rx, max_ry,
|
|
FALSE, OV_i(rtype), TRUE);
|
|
troom->rlit = OV_i(rlit);
|
|
troom->irregular = TRUE;
|
|
} else {
|
|
add_room(dx1, dy1, dx2, dy2,
|
|
OV_i(rlit), OV_i(rtype), TRUE);
|
|
#ifdef SPECIALIZATION
|
|
topologize(troom,FALSE); /* set roomno */
|
|
#else
|
|
topologize(troom); /* set roomno */
|
|
#endif
|
|
}
|
|
|
|
if (!room_not_needed) {
|
|
if (coder->n_subroom > 1)
|
|
impossible("region as subroom");
|
|
else {
|
|
coder->tmproomlist[coder->n_subroom] = troom;
|
|
coder->failed_room[coder->n_subroom] = FALSE;
|
|
coder->n_subroom++;
|
|
}
|
|
}
|
|
|
|
opvar_free(area);
|
|
opvar_free(rflags);
|
|
opvar_free(rlit);
|
|
opvar_free(rtype);
|
|
}
|
|
|
|
void
|
|
spo_drawbridge(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
xchar x,y;
|
|
struct opvar *dir, *db_open, *dcoord;
|
|
|
|
if (!OV_pop_i(dir) ||
|
|
!OV_pop_i(db_open) ||
|
|
!OV_pop_c(dcoord)) return;
|
|
|
|
get_location_coord(&x, &y, DRY|WET|HOT, coder->croom, OV_i(dcoord));
|
|
if (!create_drawbridge(x, y, OV_i(dir), OV_i(db_open)))
|
|
impossible("Cannot create drawbridge.");
|
|
SpLev_Map[x][y] = 1;
|
|
|
|
opvar_free(dcoord);
|
|
opvar_free(db_open);
|
|
opvar_free(dir);
|
|
}
|
|
|
|
void
|
|
spo_mazewalk(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
xchar x,y;
|
|
struct opvar *ftyp, *fstocked,*fdir, *mcoord;
|
|
int dir;
|
|
|
|
if (!OV_pop_i(ftyp) ||
|
|
!OV_pop_i(fstocked) ||
|
|
!OV_pop_i(fdir) ||
|
|
!OV_pop_c(mcoord)) return;
|
|
|
|
dir = OV_i(fdir);
|
|
|
|
get_location_coord(&x, &y, ANY_LOC, coder->croom, OV_i(mcoord));
|
|
if (!isok(x,y)) return;
|
|
|
|
if (OV_i(ftyp) < 1) {
|
|
#ifndef WALLIFIED_MAZE
|
|
OV_i(ftyp) = CORR;
|
|
#else
|
|
OV_i(ftyp) = ROOM;
|
|
#endif
|
|
}
|
|
|
|
/* don't use move() - it doesn't use W_NORTH, etc. */
|
|
switch (dir) {
|
|
case W_NORTH: --y; break;
|
|
case W_SOUTH: y++; break;
|
|
case W_EAST: x++; break;
|
|
case W_WEST: --x; break;
|
|
default:
|
|
impossible("sp_level_coder: Bad MAZEWALK direction");
|
|
}
|
|
|
|
if(!IS_DOOR(levl[x][y].typ)) {
|
|
levl[x][y].typ = OV_i(ftyp);
|
|
levl[x][y].flags = 0;
|
|
}
|
|
|
|
/*
|
|
* We must be sure that the parity of the coordinates for
|
|
* walkfrom() is odd. But we must also take into account
|
|
* what direction was chosen.
|
|
*/
|
|
if(!(x % 2)) {
|
|
if (dir == W_EAST)
|
|
x++;
|
|
else
|
|
x--;
|
|
|
|
/* no need for IS_DOOR check; out of map bounds */
|
|
levl[x][y].typ = OV_i(ftyp);
|
|
levl[x][y].flags = 0;
|
|
}
|
|
|
|
if (!(y % 2)) {
|
|
if (dir == W_SOUTH)
|
|
y++;
|
|
else
|
|
y--;
|
|
}
|
|
|
|
walkfrom(x, y, OV_i(ftyp));
|
|
if (OV_i(fstocked)) fill_empty_maze();
|
|
|
|
opvar_free(mcoord);
|
|
opvar_free(fdir);
|
|
opvar_free(fstocked);
|
|
opvar_free(ftyp);
|
|
}
|
|
|
|
void
|
|
spo_wall_property(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *r;
|
|
xchar dx1,dy1,dx2,dy2;
|
|
int wprop = (coder->opcode == SPO_NON_DIGGABLE) ? W_NONDIGGABLE : W_NONPASSWALL;
|
|
|
|
if (!OV_pop_r(r)) return;
|
|
|
|
dx1 = SP_REGION_X1(OV_i(r));
|
|
dy1 = SP_REGION_Y1(OV_i(r));
|
|
dx2 = SP_REGION_X2(OV_i(r));
|
|
dy2 = SP_REGION_Y2(OV_i(r));
|
|
|
|
get_location(&dx1, &dy1, ANY_LOC, (struct mkroom *)0);
|
|
get_location(&dx2, &dy2, ANY_LOC, (struct mkroom *)0);
|
|
|
|
set_wall_property(dx1, dy1, dx2, dy2, wprop);
|
|
|
|
opvar_free(r);
|
|
}
|
|
|
|
void
|
|
spo_room_door(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *wall, *secret, *mask, *pos;
|
|
room_door tmpd;
|
|
|
|
if (!OV_pop_i(wall) ||
|
|
!OV_pop_i(secret) ||
|
|
!OV_pop_i(mask) ||
|
|
!OV_pop_i(pos) ||
|
|
!coder->croom) return;
|
|
|
|
tmpd.secret = OV_i(secret);
|
|
tmpd.mask = OV_i(mask);
|
|
tmpd.pos = OV_i(pos);
|
|
tmpd.wall = OV_i(wall);
|
|
|
|
create_door(&tmpd, coder->croom);
|
|
|
|
opvar_free(wall);
|
|
opvar_free(secret);
|
|
opvar_free(mask);
|
|
opvar_free(pos);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
void
|
|
sel_set_wallify(x,y,arg)
|
|
int x, y;
|
|
genericptr_t arg;
|
|
{
|
|
wallify_map(x,y, x,y);
|
|
}
|
|
|
|
void
|
|
spo_wallify(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *typ, *r;
|
|
int dx1,dy1,dx2,dy2;
|
|
if (!OV_pop_i(typ)) return;
|
|
switch (OV_i(typ)) {
|
|
default:
|
|
case 0:
|
|
{
|
|
if (!OV_pop_r(r)) return;
|
|
dx1 = (xchar)SP_REGION_X1(OV_i(r));
|
|
dy1 = (xchar)SP_REGION_Y1(OV_i(r));
|
|
dx2 = (xchar)SP_REGION_X2(OV_i(r));
|
|
dy2 = (xchar)SP_REGION_Y2(OV_i(r));
|
|
wallify_map(dx1 < 0 ? xstart : dx1,
|
|
dy1 < 0 ? ystart : dy1,
|
|
dx2 < 0 ? xstart+xsize : dx2,
|
|
dy2 < 0 ? ystart+ysize : dy2);
|
|
}
|
|
break;
|
|
case 1:
|
|
{
|
|
if (!OV_pop_typ(r, SPOVAR_SEL)) return;
|
|
selection_iterate(r, sel_set_wallify, NULL);
|
|
}
|
|
break;
|
|
}
|
|
opvar_free(r);
|
|
}
|
|
|
|
void
|
|
spo_map(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
mazepart tmpmazepart;
|
|
struct opvar *mpxs, *mpys, *mpmap, *mpa, *mpkeepr, *mpzalign;
|
|
xchar halign, valign;
|
|
xchar tmpxstart, tmpystart, tmpxsize, tmpysize;
|
|
unpacked_coord upc;
|
|
|
|
if (!OV_pop_i(mpxs) ||
|
|
!OV_pop_i(mpys) ||
|
|
!OV_pop_s(mpmap) ||
|
|
!OV_pop_i(mpkeepr) ||
|
|
!OV_pop_i(mpzalign) ||
|
|
!OV_pop_c(mpa)) return;
|
|
|
|
tmpmazepart.xsize = OV_i(mpxs);
|
|
tmpmazepart.ysize = OV_i(mpys);
|
|
tmpmazepart.zaligntyp = OV_i(mpzalign);
|
|
|
|
upc = get_unpacked_coord(OV_i(mpa), ANY_LOC);
|
|
tmpmazepart.halign = upc.x;
|
|
tmpmazepart.valign = upc.y;
|
|
|
|
tmpxsize = xsize; tmpysize = ysize;
|
|
tmpxstart = xstart; tmpystart = ystart;
|
|
|
|
halign = tmpmazepart.halign;
|
|
valign = tmpmazepart.valign;
|
|
xsize = tmpmazepart.xsize;
|
|
ysize = tmpmazepart.ysize;
|
|
switch (tmpmazepart.zaligntyp) {
|
|
default:
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
switch((int) halign) {
|
|
case LEFT: xstart = splev_init_present ? 1 : 3; break;
|
|
case H_LEFT: xstart = 2+((x_maze_max-2-xsize)/4); break;
|
|
case CENTER: xstart = 2+((x_maze_max-2-xsize)/2); break;
|
|
case H_RIGHT: xstart = 2+((x_maze_max-2-xsize)*3/4); break;
|
|
case RIGHT: xstart = x_maze_max-xsize-1; break;
|
|
}
|
|
switch((int) valign) {
|
|
case TOP: ystart = 3; break;
|
|
case CENTER: ystart = 2+((y_maze_max-2-ysize)/2); break;
|
|
case BOTTOM: ystart = y_maze_max-ysize-1; break;
|
|
}
|
|
if (!(xstart % 2)) xstart++;
|
|
if (!(ystart % 2)) ystart++;
|
|
break;
|
|
case 2:
|
|
if (!coder->croom) {
|
|
xstart = 1;
|
|
ystart = 0;
|
|
xsize = COLNO-1-tmpmazepart.xsize;
|
|
ysize = ROWNO-tmpmazepart.ysize;
|
|
}
|
|
get_location_coord(&halign, &valign, ANY_LOC, coder->croom, OV_i(mpa));
|
|
xsize = tmpmazepart.xsize;
|
|
ysize = tmpmazepart.ysize;
|
|
xstart = halign;
|
|
ystart = valign;
|
|
break;
|
|
}
|
|
if ((ystart < 0) || (ystart + ysize > ROWNO)) {
|
|
/* try to move the start a bit */
|
|
ystart += (ystart > 0) ? -2 : 2;
|
|
if(ysize == ROWNO) ystart = 0;
|
|
if(ystart < 0 || ystart + ysize > ROWNO)
|
|
panic("reading special level with ysize too large");
|
|
}
|
|
if (xsize <= 1 && ysize <= 1) {
|
|
xstart = 1;
|
|
ystart = 0;
|
|
xsize = COLNO-1;
|
|
ysize = ROWNO;
|
|
} else {
|
|
xchar x,y;
|
|
/* Load the map */
|
|
for(y = ystart; y < ystart+ysize; y++)
|
|
for(x = xstart; x < xstart+xsize; x++) {
|
|
xchar mptyp = (mpmap->vardata.str[(y-ystart) * xsize + (x-xstart)] - 1);
|
|
if (mptyp >= MAX_TYPE) continue;
|
|
levl[x][y].typ = mptyp;
|
|
levl[x][y].lit = FALSE;
|
|
/* clear out levl: load_common_data may set them */
|
|
levl[x][y].flags = 0;
|
|
levl[x][y].horizontal = 0;
|
|
levl[x][y].roomno = 0;
|
|
levl[x][y].edge = 0;
|
|
SpLev_Map[x][y] = 1;
|
|
/*
|
|
* Set secret doors to closed (why not trapped too?). Set
|
|
* the horizontal bit.
|
|
*/
|
|
if (levl[x][y].typ == SDOOR || IS_DOOR(levl[x][y].typ)) {
|
|
if(levl[x][y].typ == SDOOR)
|
|
levl[x][y].doormask = D_CLOSED;
|
|
/*
|
|
* If there is a wall to the left that connects to a
|
|
* (secret) door, then it is horizontal. This does
|
|
* not allow (secret) doors to be corners of rooms.
|
|
*/
|
|
if (x != xstart && (IS_WALL(levl[x-1][y].typ) ||
|
|
levl[x-1][y].horizontal))
|
|
levl[x][y].horizontal = 1;
|
|
} else if(levl[x][y].typ == HWALL ||
|
|
levl[x][y].typ == IRONBARS)
|
|
levl[x][y].horizontal = 1;
|
|
else if(levl[x][y].typ == LAVAPOOL)
|
|
levl[x][y].lit = 1;
|
|
else if (splev_init_present && levl[x][y].typ == ICE)
|
|
levl[x][y].icedpool = icedpools ? ICED_POOL : ICED_MOAT;
|
|
}
|
|
if (coder->lvl_is_joined)
|
|
remove_rooms(xstart, ystart, xstart+xsize, ystart+ysize);
|
|
}
|
|
if (!OV_i(mpkeepr)) {
|
|
xstart = tmpxstart; ystart = tmpystart;
|
|
xsize = tmpxsize; ysize = tmpysize;
|
|
}
|
|
|
|
opvar_free(mpxs);
|
|
opvar_free(mpys);
|
|
opvar_free(mpmap);
|
|
opvar_free(mpa);
|
|
opvar_free(mpkeepr);
|
|
opvar_free(mpzalign);
|
|
}
|
|
|
|
void
|
|
spo_jmp(coder, lvl)
|
|
struct sp_coder *coder;
|
|
sp_lev *lvl;
|
|
{
|
|
struct opvar *tmpa;
|
|
long a;
|
|
if (!OV_pop_i(tmpa)) return;
|
|
a = sp_code_jmpaddr(coder->frame->n_opcode, (OV_i(tmpa) - 1));
|
|
if ((a >= 0) && (a < lvl->n_opcodes) &&
|
|
(a != coder->frame->n_opcode))
|
|
coder->frame->n_opcode = a;
|
|
opvar_free(tmpa);
|
|
}
|
|
|
|
void
|
|
spo_conditional_jump(coder,lvl)
|
|
struct sp_coder *coder;
|
|
sp_lev *lvl;
|
|
{
|
|
struct opvar *oa, *oc;
|
|
long a,c;
|
|
int test = 0;
|
|
if (!OV_pop_i(oa) || !OV_pop_i(oc)) return;
|
|
|
|
a = sp_code_jmpaddr(coder->frame->n_opcode, (OV_i(oa) - 1));
|
|
c = OV_i(oc);
|
|
|
|
switch (coder->opcode) {
|
|
default: impossible("spo_conditional_jump: illegal opcode"); break;
|
|
case SPO_JL: test = (c & SP_CPUFLAG_LT); break;
|
|
case SPO_JLE: test = (c & (SP_CPUFLAG_LT|SP_CPUFLAG_EQ)); break;
|
|
case SPO_JG: test = (c & SP_CPUFLAG_GT); break;
|
|
case SPO_JGE: test = (c & (SP_CPUFLAG_GT|SP_CPUFLAG_EQ)); break;
|
|
case SPO_JE: test = (c & SP_CPUFLAG_EQ); break;
|
|
case SPO_JNE: test = (c & ~SP_CPUFLAG_EQ); break;
|
|
}
|
|
|
|
if ((test) && (a >= 0) &&
|
|
(a < lvl->n_opcodes) &&
|
|
(a != coder->frame->n_opcode))
|
|
coder->frame->n_opcode = a;
|
|
|
|
opvar_free(oa);
|
|
opvar_free(oc);
|
|
}
|
|
|
|
|
|
void
|
|
spo_var_init(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *vname;
|
|
struct opvar *arraylen;
|
|
struct opvar *vvalue;
|
|
struct splev_var *tmpvar;
|
|
struct splev_var *tmp2;
|
|
long idx;
|
|
|
|
OV_pop_s(vname);
|
|
OV_pop_i(arraylen);
|
|
|
|
if (!vname || !arraylen)
|
|
panic("no values for SPO_VAR_INIT");
|
|
|
|
tmpvar = opvar_var_defined(coder, OV_s(vname));
|
|
|
|
if (tmpvar) {
|
|
/* variable redefinition */
|
|
if (OV_i(arraylen) < 0) {
|
|
/* copy variable */
|
|
if (tmpvar->array_len) {
|
|
idx = tmpvar->array_len;
|
|
while (idx-- > 0) {
|
|
opvar_free(tmpvar->data.arrayvalues[idx]);
|
|
}
|
|
Free(tmpvar->data.arrayvalues);
|
|
} else {
|
|
opvar_free(tmpvar->data.value);
|
|
}
|
|
tmpvar->data.arrayvalues = NULL;
|
|
goto copy_variable;
|
|
} else if (OV_i(arraylen)) {
|
|
/* redefined array */
|
|
idx = tmpvar->array_len;
|
|
while (idx-- > 0) {
|
|
opvar_free(tmpvar->data.arrayvalues[idx]);
|
|
}
|
|
Free(tmpvar->data.arrayvalues);
|
|
tmpvar->data.arrayvalues = NULL;
|
|
goto create_new_array;
|
|
} else {
|
|
/* redefined single value */
|
|
OV_pop(vvalue);
|
|
if (tmpvar->svtyp != vvalue->spovartyp) panic("redefining variable as different type");
|
|
opvar_free(tmpvar->data.value);
|
|
tmpvar->data.value = vvalue;
|
|
tmpvar->array_len = 0;
|
|
}
|
|
} else {
|
|
/* new variable definition */
|
|
tmpvar = (struct splev_var *)malloc(sizeof(struct splev_var));
|
|
if (!tmpvar) panic("newvar tmpvar alloc");
|
|
tmpvar->next = coder->frame->variables;
|
|
tmpvar->name = strdup(OV_s(vname));
|
|
coder->frame->variables = tmpvar;
|
|
|
|
if (OV_i(arraylen) < 0) {
|
|
/* copy variable */
|
|
copy_variable:
|
|
OV_pop(vvalue);
|
|
tmp2 = opvar_var_defined(coder, OV_s(vvalue));
|
|
if (!tmp2) panic("no copyable var");
|
|
tmpvar->svtyp = tmp2->svtyp;
|
|
tmpvar->array_len = tmp2->array_len;
|
|
if (tmpvar->array_len) {
|
|
idx = tmpvar->array_len;
|
|
tmpvar->data.arrayvalues = (struct opvar **)malloc(sizeof(struct opvar *) * idx);
|
|
if (!tmpvar->data.arrayvalues) panic("tmpvar->data.arrayvalues alloc");
|
|
while (idx-- > 0) {
|
|
tmpvar->data.arrayvalues[idx] = opvar_clone(tmp2->data.arrayvalues[idx]);
|
|
}
|
|
} else {
|
|
tmpvar->data.value = opvar_clone(tmp2->data.value);
|
|
}
|
|
opvar_free(vvalue);
|
|
} else if (OV_i(arraylen)) {
|
|
/* new array */
|
|
create_new_array:
|
|
idx = OV_i(arraylen);
|
|
tmpvar->array_len = idx;
|
|
tmpvar->data.arrayvalues = (struct opvar **)malloc(sizeof(struct opvar *) * idx);
|
|
if (!tmpvar->data.arrayvalues) panic("malloc tmpvar->data.arrayvalues");
|
|
while (idx-- > 0) {
|
|
OV_pop(vvalue);
|
|
if (!vvalue) panic("no value for arrayvariable");
|
|
tmpvar->data.arrayvalues[idx] = vvalue;
|
|
}
|
|
tmpvar->svtyp = SPOVAR_ARRAY;
|
|
} else {
|
|
/* new single value */
|
|
OV_pop(vvalue);
|
|
if (!vvalue) panic("no value for variable");
|
|
tmpvar->svtyp = OV_typ(vvalue);
|
|
tmpvar->data.value = vvalue;
|
|
tmpvar->array_len = 0;
|
|
}
|
|
}
|
|
|
|
opvar_free(vname);
|
|
opvar_free(arraylen);
|
|
}
|
|
|
|
|
|
long
|
|
opvar_array_length(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *vname;
|
|
struct splev_var *tmp;
|
|
long len = 0;
|
|
|
|
if (!coder) return 0;
|
|
|
|
vname = splev_stack_pop(coder->stack);
|
|
if (!vname) return 0;
|
|
if (vname->spovartyp != SPOVAR_VARIABLE) goto pass;
|
|
|
|
tmp = coder->frame->variables;
|
|
while (tmp) {
|
|
if (!strcmp(tmp->name, OV_s(vname))) {
|
|
if ((tmp->svtyp & SPOVAR_ARRAY)) {
|
|
len = tmp->array_len;
|
|
if (len < 1) len = 0;
|
|
}
|
|
goto pass;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
pass:
|
|
opvar_free(vname);
|
|
return len;
|
|
}
|
|
|
|
|
|
void
|
|
spo_shuffle_array(coder)
|
|
struct sp_coder *coder;
|
|
{
|
|
struct opvar *vname;
|
|
struct splev_var *tmp;
|
|
struct opvar *tmp2;
|
|
long i,j;
|
|
|
|
if (!OV_pop_s(vname)) return;
|
|
|
|
tmp = opvar_var_defined(coder, OV_s(vname));
|
|
if (!tmp || (tmp->array_len < 1)) {
|
|
opvar_free(vname);
|
|
return;
|
|
}
|
|
for (i = tmp->array_len - 1; i > 0; i--) {
|
|
if ((j = rn2(i + 1)) == i) continue;
|
|
tmp2 = tmp->data.arrayvalues[j];
|
|
tmp->data.arrayvalues[j] = tmp->data.arrayvalues[i];
|
|
tmp->data.arrayvalues[i] = tmp2;
|
|
}
|
|
|
|
opvar_free(vname);
|
|
}
|
|
|
|
|
|
|
|
/* Special level coder, creates the special level from the sp_lev codes.
|
|
* Does not free the allocated memory.
|
|
*/
|
|
STATIC_OVL boolean
|
|
sp_level_coder(lvl)
|
|
sp_lev *lvl;
|
|
{
|
|
unsigned long exec_opcodes = 0;
|
|
int tmpi;
|
|
long room_stack = 0;
|
|
unsigned long max_execution = SPCODER_MAX_RUNTIME;
|
|
struct sp_coder *coder = (struct sp_coder *)alloc(sizeof(struct sp_coder));
|
|
if (!coder) panic("coder alloc");
|
|
coder->frame = frame_new(0);
|
|
coder->stack = NULL;
|
|
coder->premapped = FALSE;
|
|
coder->solidify = FALSE;
|
|
coder->croom = NULL;
|
|
coder->n_subroom = 1;
|
|
coder->exit_script = FALSE;
|
|
coder->lvl_is_joined = 0;
|
|
|
|
splev_init_present = FALSE;
|
|
icedpools = FALSE;
|
|
|
|
if (wizard) {
|
|
char *met = nh_getenv("SPCODER_MAX_RUNTIME");
|
|
if (met && met[0] == '1') max_execution = (1<<30) - 1;
|
|
}
|
|
|
|
for (tmpi = 0; tmpi <= MAX_NESTED_ROOMS; tmpi++) {
|
|
coder->tmproomlist[tmpi] = (struct mkroom *)0;
|
|
coder->failed_room[tmpi] = FALSE;
|
|
}
|
|
|
|
shuffle_alignments();
|
|
|
|
for (tmpi = 0; tmpi < MAX_CONTAINMENT; tmpi++) container_obj[tmpi] = NULL;
|
|
container_idx = 0;
|
|
|
|
invent_carrying_monster = NULL;
|
|
|
|
(void) memset((genericptr_t)&SpLev_Map[0][0], 0, sizeof SpLev_Map);
|
|
|
|
level.flags.is_maze_lev = 0;
|
|
|
|
xstart = 1;
|
|
ystart = 0;
|
|
xsize = COLNO-1;
|
|
ysize = ROWNO;
|
|
|
|
while (coder->frame->n_opcode < lvl->n_opcodes && !coder->exit_script) {
|
|
coder->opcode = lvl->opcodes[coder->frame->n_opcode].opcode;
|
|
coder->opdat = lvl->opcodes[coder->frame->n_opcode].opdat;
|
|
|
|
coder->stack = coder->frame->stack;
|
|
|
|
if (exec_opcodes++ > max_execution) {
|
|
impossible("Level script is taking too much time, stopping.");
|
|
coder->exit_script = TRUE;
|
|
}
|
|
|
|
if (coder->failed_room[coder->n_subroom-1] &&
|
|
coder->opcode != SPO_ENDROOM &&
|
|
coder->opcode != SPO_ROOM &&
|
|
coder->opcode != SPO_SUBROOM) goto next_opcode;
|
|
|
|
coder->croom = coder->tmproomlist[coder->n_subroom-1];
|
|
|
|
switch (coder->opcode) {
|
|
case SPO_NULL: break;
|
|
case SPO_EXIT: coder->exit_script = TRUE; break;
|
|
case SPO_FRAME_PUSH: spo_frame_push(coder); break;
|
|
case SPO_FRAME_POP: spo_frame_pop(coder); break;
|
|
case SPO_CALL: spo_call(coder); break;
|
|
case SPO_RETURN: spo_return(coder); break;
|
|
case SPO_END_MONINVENT: spo_end_moninvent(coder); break;
|
|
case SPO_POP_CONTAINER: spo_pop_container(coder); break;
|
|
case SPO_POP:
|
|
{
|
|
struct opvar *ov = splev_stack_pop(coder->stack);
|
|
opvar_free(ov);
|
|
}
|
|
break;
|
|
case SPO_PUSH: splev_stack_push(coder->stack, opvar_clone(coder->opdat)); break;
|
|
case SPO_MESSAGE: spo_message(coder); break;
|
|
case SPO_MONSTER: spo_monster(coder); break;
|
|
case SPO_OBJECT: spo_object(coder); break;
|
|
case SPO_LEVEL_FLAGS: spo_level_flags(coder); break;
|
|
case SPO_INITLEVEL: spo_initlevel(coder); break;
|
|
case SPO_ENGRAVING: spo_engraving(coder); break;
|
|
case SPO_MINERALIZE: spo_mineralize(coder); break;
|
|
case SPO_SUBROOM:
|
|
case SPO_ROOM:
|
|
if (!coder->failed_room[coder->n_subroom-1]) {
|
|
spo_room(coder);
|
|
} else room_stack++;
|
|
break;
|
|
case SPO_ENDROOM:
|
|
if (coder->failed_room[coder->n_subroom-1]) {
|
|
if (!room_stack)
|
|
spo_endroom(coder);
|
|
else
|
|
room_stack--;
|
|
} else {
|
|
spo_endroom(coder);
|
|
}
|
|
break;
|
|
case SPO_DOOR: spo_door(coder); break;
|
|
case SPO_STAIR: spo_stair(coder); break;
|
|
case SPO_LADDER: spo_ladder(coder); break;
|
|
case SPO_GRAVE: spo_grave(coder); break;
|
|
case SPO_ALTAR: spo_altar(coder); break;
|
|
case SPO_SINK:
|
|
case SPO_POOL:
|
|
case SPO_FOUNTAIN: spo_feature(coder); break;
|
|
case SPO_TRAP: spo_trap(coder); break;
|
|
case SPO_GOLD: spo_gold(coder); break;
|
|
case SPO_CORRIDOR: spo_corridor(coder); break;
|
|
case SPO_TERRAIN: spo_terrain(coder); break;
|
|
case SPO_REPLACETERRAIN: spo_replace_terrain(coder); break;
|
|
case SPO_LEVREGION: spo_levregion(coder); break;
|
|
case SPO_REGION: spo_region(coder); break;
|
|
case SPO_DRAWBRIDGE: spo_drawbridge(coder); break;
|
|
case SPO_MAZEWALK: spo_mazewalk(coder); break;
|
|
case SPO_NON_PASSWALL:
|
|
case SPO_NON_DIGGABLE: spo_wall_property(coder); break;
|
|
case SPO_ROOM_DOOR: spo_room_door(coder); break;
|
|
case SPO_WALLIFY: spo_wallify(coder); break;
|
|
case SPO_COPY:
|
|
{
|
|
struct opvar *a = splev_stack_pop(coder->stack);
|
|
splev_stack_push(coder->stack, opvar_clone(a));
|
|
splev_stack_push(coder->stack, opvar_clone(a));
|
|
opvar_free(a);
|
|
}
|
|
break;
|
|
case SPO_DEC:
|
|
{
|
|
struct opvar *a;
|
|
if (!OV_pop_i(a)) break;
|
|
OV_i(a)--;
|
|
splev_stack_push(coder->stack, a);
|
|
}
|
|
break;
|
|
case SPO_INC:
|
|
{
|
|
struct opvar *a;
|
|
if (!OV_pop_i(a)) break;
|
|
OV_i(a)++;
|
|
splev_stack_push(coder->stack, a);
|
|
}
|
|
break;
|
|
case SPO_MATH_SIGN:
|
|
{
|
|
struct opvar *a;
|
|
if (!OV_pop_i(a)) break;
|
|
OV_i(a) = ((OV_i(a) < 0) ? -1 : ((OV_i(a) > 0) ? 1 : 0));
|
|
splev_stack_push(coder->stack, a);
|
|
}
|
|
break;
|
|
case SPO_MATH_ADD:
|
|
{
|
|
struct opvar *a, *b;
|
|
if (!OV_pop(b) || !OV_pop(a)) break;
|
|
if (OV_typ(b) == OV_typ(a)) {
|
|
if (OV_typ(a) == SPOVAR_INT) {
|
|
OV_i(a) = OV_i(a) + OV_i(b);
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
} else if (OV_typ(a) == SPOVAR_STRING) {
|
|
char *tmpbuf = (char *)alloc(strlen(OV_s(a)) + strlen(OV_s(b)) + 1);
|
|
if (tmpbuf) {
|
|
struct opvar *c;
|
|
(void) sprintf(tmpbuf, "%s%s", OV_s(a), OV_s(b));
|
|
c = opvar_new_str(tmpbuf);
|
|
splev_stack_push(coder->stack, c);
|
|
opvar_free(a);
|
|
opvar_free(b);
|
|
Free(tmpbuf);
|
|
} else {
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
impossible("malloc at str concat");
|
|
}
|
|
} else {
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
impossible("adding weird types");
|
|
}
|
|
} else {
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
impossible("adding different types");
|
|
}
|
|
}
|
|
break;
|
|
case SPO_MATH_SUB:
|
|
{
|
|
struct opvar *a, *b;
|
|
if (!OV_pop_i(b) || !OV_pop_i(a)) break;
|
|
OV_i(a) = OV_i(a) - OV_i(b);
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
}
|
|
break;
|
|
case SPO_MATH_MUL:
|
|
{
|
|
struct opvar *a, *b;
|
|
if (!OV_pop_i(b) || !OV_pop_i(a)) break;
|
|
OV_i(a) = OV_i(a) * OV_i(b);
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
}
|
|
break;
|
|
case SPO_MATH_DIV:
|
|
{
|
|
struct opvar *a, *b;
|
|
if (!OV_pop_i(b) || !OV_pop_i(a)) break;
|
|
if (OV_i(b) >= 1) {
|
|
OV_i(a) = OV_i(a) / OV_i(b);
|
|
} else {
|
|
OV_i(a) = 0;
|
|
}
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
}
|
|
break;
|
|
case SPO_MATH_MOD:
|
|
{
|
|
struct opvar *a, *b;
|
|
if (!OV_pop_i(b) || !OV_pop_i(a)) break;
|
|
if (OV_i(b) > 0) {
|
|
OV_i(a) = OV_i(a) % OV_i(b);
|
|
} else {
|
|
OV_i(a) = 0;
|
|
}
|
|
splev_stack_push(coder->stack, a);
|
|
opvar_free(b);
|
|
}
|
|
break;
|
|
case SPO_CMP:
|
|
{
|
|
struct opvar *a;
|
|
struct opvar *b;
|
|
struct opvar *c;
|
|
long val = 0;
|
|
|
|
OV_pop(b);
|
|
OV_pop(a);
|
|
|
|
if (!a || !b) {
|
|
impossible("spo_cmp: no values in stack");
|
|
break;
|
|
}
|
|
|
|
if (OV_typ(a) != OV_typ(b)) {
|
|
impossible("spo_cmp: trying to compare differing datatypes");
|
|
break;
|
|
}
|
|
|
|
switch (OV_typ(a)) {
|
|
case SPOVAR_COORD:
|
|
case SPOVAR_REGION:
|
|
case SPOVAR_MAPCHAR:
|
|
case SPOVAR_MONST:
|
|
case SPOVAR_OBJ:
|
|
case SPOVAR_INT:
|
|
if (OV_i(b) > OV_i(a)) val |= SP_CPUFLAG_LT;
|
|
if (OV_i(b) < OV_i(a)) val |= SP_CPUFLAG_GT;
|
|
if (OV_i(b) == OV_i(a)) val |= SP_CPUFLAG_EQ;
|
|
c = opvar_new_int(val);
|
|
break;
|
|
case SPOVAR_STRING:
|
|
c = opvar_new_int(((!strcmp(OV_s(b), OV_s(a))) ? SP_CPUFLAG_EQ : 0));
|
|
break;
|
|
default:
|
|
c = opvar_new_int(0);
|
|
break;
|
|
}
|
|
splev_stack_push(coder->stack, c);
|
|
opvar_free(a);
|
|
opvar_free(b);
|
|
}
|
|
break;
|
|
case SPO_JMP:
|
|
spo_jmp(coder, lvl); break;
|
|
case SPO_JL:
|
|
case SPO_JLE:
|
|
case SPO_JG:
|
|
case SPO_JGE:
|
|
case SPO_JE:
|
|
case SPO_JNE:
|
|
spo_conditional_jump(coder, lvl); break;
|
|
case SPO_RN2:
|
|
{
|
|
struct opvar *tmpv;
|
|
struct opvar *t;
|
|
if (!OV_pop_i(tmpv)) break;
|
|
t = opvar_new_int((OV_i(tmpv) > 1) ? rn2(OV_i(tmpv)) : 0);
|
|
splev_stack_push(coder->stack, t);
|
|
opvar_free(tmpv);
|
|
}
|
|
break;
|
|
case SPO_DICE:
|
|
{
|
|
struct opvar *a, *b, *t;
|
|
if (!OV_pop_i(b) || !OV_pop_i(a)) break;
|
|
if (OV_i(b) < 1) OV_i(b) = 1;
|
|
if (OV_i(a) < 1) OV_i(a) = 1;
|
|
t = opvar_new_int(d(OV_i(a), OV_i(b)));
|
|
splev_stack_push(coder->stack, t);
|
|
opvar_free(a);
|
|
opvar_free(b);
|
|
}
|
|
break;
|
|
case SPO_MAP:
|
|
spo_map(coder); break;
|
|
case SPO_VAR_INIT:
|
|
spo_var_init(coder); break;
|
|
case SPO_SHUFFLE_ARRAY:
|
|
spo_shuffle_array(coder); break;
|
|
case SPO_SEL_ADD: /* actually, logical or */
|
|
{
|
|
struct opvar *sel1, *sel2, *pt;
|
|
if (!OV_pop_typ(sel1, SPOVAR_SEL)) panic("no sel1 for add");
|
|
if (!OV_pop_typ(sel2, SPOVAR_SEL)) panic("no sel2 for add");
|
|
pt = selection_logical_oper(sel1, sel2, '|');
|
|
opvar_free(sel1);
|
|
opvar_free(sel2);
|
|
splev_stack_push(coder->stack, pt);
|
|
}
|
|
break;
|
|
case SPO_SEL_COMPLEMENT:
|
|
{
|
|
struct opvar *sel, *pt;
|
|
if (!OV_pop_typ(sel, SPOVAR_SEL)) panic("no sel for not");
|
|
pt = selection_not(sel);
|
|
opvar_free(sel);
|
|
splev_stack_push(coder->stack, pt);
|
|
}
|
|
break;
|
|
case SPO_SEL_FILTER: /* sorta like logical and */
|
|
{
|
|
struct opvar *filtertype;
|
|
if (!OV_pop_i(filtertype)) panic("no sel filter type");
|
|
switch (OV_i(filtertype)) {
|
|
case SPOFILTER_PERCENT:
|
|
{
|
|
struct opvar *tmp1, *sel;
|
|
if (!OV_pop_i(tmp1)) panic("no sel filter percent");
|
|
if (!OV_pop_typ(sel, SPOVAR_SEL)) panic("no sel filter");
|
|
selection_filter_percent(sel, OV_i(tmp1));
|
|
splev_stack_push(coder->stack, sel);
|
|
opvar_free(tmp1);
|
|
}
|
|
break;
|
|
case SPOFILTER_SELECTION: /* logical and */
|
|
{
|
|
struct opvar *pt, *sel1, *sel2;
|
|
if (!OV_pop_typ(sel1, SPOVAR_SEL)) panic("no sel filter sel1");
|
|
if (!OV_pop_typ(sel2, SPOVAR_SEL)) panic("no sel filter sel2");
|
|
pt = selection_logical_oper(sel1, sel2, '&');
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(sel1);
|
|
opvar_free(sel2);
|
|
}
|
|
break;
|
|
case SPOFILTER_MAPCHAR:
|
|
{
|
|
struct opvar *pt, *tmp1, *sel;
|
|
if (!OV_pop_typ(sel, SPOVAR_SEL)) panic("no sel filter");
|
|
if (!OV_pop_typ(tmp1, SPOVAR_MAPCHAR)) panic("no sel filter mapchar");
|
|
pt = selection_filter_mapchar(sel, tmp1);
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(tmp1);
|
|
opvar_free(sel);
|
|
}
|
|
break;
|
|
default: panic("unknown sel filter type");
|
|
}
|
|
opvar_free(filtertype);
|
|
}
|
|
break;
|
|
case SPO_SEL_POINT:
|
|
{
|
|
struct opvar *tmp;
|
|
struct opvar *pt = selection_opvar(NULL);
|
|
schar x,y;
|
|
if (!OV_pop_c(tmp)) panic("no ter sel coord");
|
|
get_location_coord(&x, &y, ANY_LOC, coder->croom, OV_i(tmp));
|
|
selection_setpoint(x,y, pt, 1);
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(tmp);
|
|
}
|
|
break;
|
|
case SPO_SEL_RECT:
|
|
case SPO_SEL_FILLRECT:
|
|
{
|
|
struct opvar *tmp, *pt = selection_opvar(NULL);
|
|
schar x,y,x1,y1,x2,y2;
|
|
if (!OV_pop_r(tmp)) panic("no ter sel region");
|
|
x1 = min(SP_REGION_X1(OV_i(tmp)), SP_REGION_X2(OV_i(tmp)));
|
|
y1 = min(SP_REGION_Y1(OV_i(tmp)), SP_REGION_Y2(OV_i(tmp)));
|
|
x2 = max(SP_REGION_X1(OV_i(tmp)), SP_REGION_X2(OV_i(tmp)));
|
|
y2 = max(SP_REGION_Y1(OV_i(tmp)), SP_REGION_Y2(OV_i(tmp)));
|
|
get_location(&x1, &y1, ANY_LOC, coder->croom);
|
|
get_location(&x2, &y2, ANY_LOC, coder->croom);
|
|
x1 = (x1 < 0) ? 0 : x1;
|
|
y1 = (y1 < 0) ? 0 : y1;
|
|
x2 = (x2 >= COLNO) ? COLNO-1 : x2;
|
|
y2 = (y2 >= ROWNO) ? ROWNO-1 : y2;
|
|
if (coder->opcode == SPO_SEL_RECT) {
|
|
for (x = x1; x <= x2; x++) {
|
|
selection_setpoint(x,y1, pt, 1);
|
|
selection_setpoint(x,y2, pt, 1);
|
|
}
|
|
for (y = y1; y <= y2; y++) {
|
|
selection_setpoint(x1,y, pt, 1);
|
|
selection_setpoint(x2,y, pt, 1);
|
|
}
|
|
} else {
|
|
for (x = x1; x <= x2; x++)
|
|
for (y = y1; y <= y2; y++)
|
|
selection_setpoint(x,y, pt, 1);
|
|
}
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(tmp);
|
|
}
|
|
break;
|
|
case SPO_SEL_LINE:
|
|
{
|
|
struct opvar *tmp, *tmp2, *pt = selection_opvar(NULL);
|
|
schar x1,y1,x2,y2;
|
|
if (!OV_pop_c(tmp)) panic("no ter sel linecoord1");
|
|
if (!OV_pop_c(tmp2)) panic("no ter sel linecoord2");
|
|
get_location_coord(&x1, &y1, ANY_LOC, coder->croom, OV_i(tmp));
|
|
get_location_coord(&x2, &y2, ANY_LOC, coder->croom, OV_i(tmp2));
|
|
x1 = (x1 < 0) ? 0 : x1;
|
|
y1 = (y1 < 0) ? 0 : y1;
|
|
x2 = (x2 >= COLNO) ? COLNO-1 : x2;
|
|
y2 = (y2 >= ROWNO) ? ROWNO-1 : y2;
|
|
selection_do_line(x1,y1,x2,y2, pt);
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(tmp);
|
|
opvar_free(tmp2);
|
|
}
|
|
break;
|
|
case SPO_SEL_RNDLINE:
|
|
{
|
|
struct opvar *tmp, *tmp2, *tmp3, *pt = selection_opvar(NULL);
|
|
schar x1,y1,x2,y2;
|
|
if (!OV_pop_i(tmp3)) panic("no ter sel randline1");
|
|
if (!OV_pop_c(tmp)) panic("no ter sel randline2");
|
|
if (!OV_pop_c(tmp2)) panic("no ter sel randline3");
|
|
get_location_coord(&x1, &y1, ANY_LOC, coder->croom, OV_i(tmp));
|
|
get_location_coord(&x2, &y2, ANY_LOC, coder->croom, OV_i(tmp2));
|
|
x1 = (x1 < 0) ? 0 : x1;
|
|
y1 = (y1 < 0) ? 0 : y1;
|
|
x2 = (x2 >= COLNO) ? COLNO-1 : x2;
|
|
y2 = (y2 >= ROWNO) ? ROWNO-1 : y2;
|
|
selection_do_randline(x1,y1,x2,y2, OV_i(tmp3), 12, pt);
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(tmp);
|
|
opvar_free(tmp2);
|
|
opvar_free(tmp3);
|
|
}
|
|
break;
|
|
case SPO_SEL_GROW:
|
|
{
|
|
struct opvar *dirs, *pt;
|
|
if (!OV_pop_i(dirs)) panic("no dirs for grow");
|
|
if (!OV_pop_typ(pt, SPOVAR_SEL)) panic("no selection for grow");
|
|
selection_do_grow(pt, OV_i(dirs));
|
|
splev_stack_push(coder->stack, pt);
|
|
opvar_free(dirs);
|
|
}
|
|
break;
|
|
case SPO_SEL_FLOOD:
|
|
{
|
|
struct opvar *tmp;
|
|
schar x,y;
|
|
if (!OV_pop_c(tmp)) panic("no ter sel flood coord");
|
|
get_location_coord(&x, &y, ANY_LOC, coder->croom, OV_i(tmp));
|
|
if (isok(x,y)) {
|
|
struct opvar *pt = selection_opvar(NULL);
|
|
selection_floodfill(pt, x,y);
|
|
splev_stack_push(coder->stack, pt);
|
|
}
|
|
opvar_free(tmp);
|
|
}
|
|
break;
|
|
case SPO_SEL_RNDCOORD:
|
|
{
|
|
struct opvar *pt;
|
|
schar x,y;
|
|
if (!OV_pop_typ(pt, SPOVAR_SEL)) panic("no selection for rndcoord");
|
|
if (selection_rndcoord(pt, &x, &y)) {
|
|
x -= xstart;
|
|
y -= ystart;
|
|
}
|
|
splev_stack_push(coder->stack, opvar_new_coord(x,y));
|
|
opvar_free(pt);
|
|
}
|
|
break;
|
|
case SPO_SEL_ELLIPSE:
|
|
{
|
|
struct opvar *filled, *xaxis, *yaxis, *pt;
|
|
struct opvar *sel = selection_opvar(NULL);
|
|
schar x,y;
|
|
if (!OV_pop_i(filled)) panic("no filled for ellipse");
|
|
if (!OV_pop_i(yaxis)) panic("no yaxis for ellipse");
|
|
if (!OV_pop_i(xaxis)) panic("no xaxis for ellipse");
|
|
if (!OV_pop_c(pt)) panic("no pt for ellipse");
|
|
get_location_coord(&x, &y, ANY_LOC, coder->croom, OV_i(pt));
|
|
selection_do_ellipse(sel, x,y, OV_i(xaxis), OV_i(yaxis), OV_i(filled));
|
|
splev_stack_push(coder->stack, sel);
|
|
opvar_free(filled);
|
|
opvar_free(yaxis);
|
|
opvar_free(xaxis);
|
|
opvar_free(pt);
|
|
}
|
|
break;
|
|
case SPO_SEL_GRADIENT:
|
|
{
|
|
struct opvar *gtyp, *glim, *mind, *maxd, *gcoord, *coord2;
|
|
struct opvar *sel;
|
|
schar x,y, x2,y2;
|
|
if (!OV_pop_i(gtyp)) panic("no gtyp for grad");
|
|
if (!OV_pop_i(glim)) panic("no glim for grad");
|
|
if (!OV_pop_c(coord2)) panic("no coord2 for grad");
|
|
if (!OV_pop_c(gcoord)) panic("no coord for grad");
|
|
if (!OV_pop_i(maxd)) panic("no maxd for grad");
|
|
if (!OV_pop_i(mind)) panic("no mind for grad");
|
|
get_location_coord(&x, &y, ANY_LOC, coder->croom,
|
|
OV_i(gcoord));
|
|
get_location_coord(&x2, &y2, ANY_LOC, coder->croom,
|
|
OV_i(coord2));
|
|
|
|
sel = selection_opvar(NULL);
|
|
selection_do_gradient(sel, x,y, x2,y2, OV_i(gtyp),
|
|
OV_i(mind), OV_i(maxd), OV_i(glim));
|
|
splev_stack_push(coder->stack, sel);
|
|
|
|
opvar_free(gtyp);
|
|
opvar_free(glim);
|
|
opvar_free(gcoord);
|
|
opvar_free(coord2);
|
|
opvar_free(maxd);
|
|
opvar_free(mind);
|
|
}
|
|
break;
|
|
default:
|
|
panic("sp_level_coder: Unknown opcode %i", coder->opcode);
|
|
}
|
|
|
|
next_opcode:
|
|
coder->frame->n_opcode++;
|
|
} /*while*/
|
|
|
|
link_doors_rooms();
|
|
fill_rooms();
|
|
remove_boundary_syms();
|
|
wallification(1, 0, COLNO-1, ROWNO-1);
|
|
|
|
count_features();
|
|
|
|
if (coder->premapped) sokoban_detect();
|
|
if (coder->solidify) solidify_map();
|
|
|
|
if (coder->frame) {
|
|
struct sp_frame *tmpframe;
|
|
do {
|
|
tmpframe = coder->frame->next;
|
|
frame_del(coder->frame);
|
|
coder->frame = tmpframe;
|
|
} while (coder->frame);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* General loader
|
|
*/
|
|
|
|
boolean
|
|
load_special(name)
|
|
const char *name;
|
|
{
|
|
dlb *fd;
|
|
sp_lev *lvl = NULL;
|
|
boolean result = FALSE;
|
|
struct version_info vers_info;
|
|
|
|
fd = dlb_fopen(name, RDBMODE);
|
|
if (!fd) return FALSE;
|
|
Fread((genericptr_t) &vers_info, sizeof vers_info, 1, fd);
|
|
if (!check_version(&vers_info, name, TRUE)) {
|
|
(void)dlb_fclose(fd);
|
|
goto give_up;
|
|
}
|
|
lvl = (sp_lev *)alloc(sizeof(sp_lev));
|
|
if (!lvl) panic("alloc sp_lev");
|
|
result = sp_level_loader(fd, lvl);
|
|
(void)dlb_fclose(fd);
|
|
if (result) result = sp_level_coder(lvl);
|
|
sp_level_free(lvl);
|
|
Free(lvl);
|
|
|
|
give_up:
|
|
return result;
|
|
}
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
/*sp_lev.c*/
|