root/games/hack/hack.mkshop.c
/*      $NetBSD: hack.mkshop.c,v 1.11 2011/08/07 06:03:45 dholland Exp $        */

/*
 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
 * Amsterdam
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Stichting Centrum voor Wiskunde en
 * Informatica, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: hack.mkshop.c,v 1.11 2011/08/07 06:03:45 dholland Exp $");
#endif                          /* not lint */

#include <stdlib.h>
#ifndef QUEST
#include "hack.h"
#include "extern.h"
#include "def.mkroom.h"
#include "def.eshk.h"

#define ESHK    ((struct eshk *)(&(shk->mextra[0])))

/* their probabilities */
static const schar shprobs[] = {3, 3, 5, 5, 10, 10, 14, 50};

static const struct permonst *morguemon(void);
static int nexttodoor(int, int);
static int has_dnstairs(struct mkroom *);
static int has_upstairs(struct mkroom *);
static int isbig(struct mkroom *);
static int dist2(int, int, int, int);
static int sq(int);

void
mkshop(void)
{
        struct mkroom  *sroom;
        int             sh, sx, sy, i = -1;
        char            let;
        int             roomno;
        struct monst   *shk;
#ifdef WIZARD
        /* first determine shoptype */
        if (wizard) {
                char           *ep = getenv("SHOPTYPE");
                if (ep) {
                        if (*ep == 'z' || *ep == 'Z') {
                                mkzoo(ZOO);
                                return;
                        }
                        if (*ep == 'm' || *ep == 'M') {
                                mkzoo(MORGUE);
                                return;
                        }
                        if (*ep == 'b' || *ep == 'B') {
                                mkzoo(BEEHIVE);
                                return;
                        }
                        if (*ep == 's' || *ep == 'S') {
                                mkswamp();
                                return;
                        }
                        for (i = 0; shtypes[i]; i++)
                                if (*ep == shtypes[i])
                                        break;
                        goto gottype;
                }
        }
gottype:
#endif  /* WIZARD */
        for (sroom = &rooms[0], roomno = 0;; sroom++, roomno++) {
                if (sroom->hx < 0)
                        return;
                if (sroom - rooms >= nroom) {
                        pline("rooms not closed by -1?");
                        return;
                }
                if (sroom->rtype)
                        continue;
                if (!sroom->rlit || has_dnstairs(sroom) || has_upstairs(sroom))
                        continue;
                if (
#ifdef WIZARD
                    (wizard && getenv("SHOPTYPE") && sroom->doorct != 0) ||
#endif  /* WIZARD */
                    sroom->doorct == 1)
                        break;
        }

        if (i < 0) {            /* shoptype not yet determined */
                int             j;

                for (j = rn2(100), i = 0; (j -= shprobs[i]) >= 0; i++)
                        if (!shtypes[i])
                                break;  /* superfluous */
                if (isbig(sroom) && i + SHOPBASE == WANDSHOP)
                        i = GENERAL - SHOPBASE;
        }
        sroom->rtype = i + SHOPBASE;
        let = shtypes[i];
        sh = sroom->fdoor;
        sx = doors[sh].x;
        sy = doors[sh].y;
        if (sx == sroom->lx - 1)
                sx++;
        else if (sx == sroom->hx + 1)
                sx--;
        else if (sy == sroom->ly - 1)
                sy++;
        else if (sy == sroom->hy + 1)
                sy--;
        else {
#ifdef WIZARD
                /* This is said to happen sometimes, but I've never seen it. */
                if (wizard) {
                        int             j = sroom->doorct;

                        pline("Where is shopdoor?");
                        pline("Room at (%d,%d),(%d,%d).", sroom->lx, sroom->ly,
                              sroom->hx, sroom->hy);
                        pline("doormax=%d doorct=%d fdoor=%d",
                              doorindex, sroom->doorct, sh);
                        while (j--) {
                                pline("door [%d,%d]", doors[sh].x, doors[sh].y);
                                sh++;
                        }
                        more();
                }
#endif  /* WIZARD */
                return;
        }
        if (!(shk = makemon(PM_SHK, sx, sy)))
                return;
        shk->isshk = shk->mpeaceful = 1;
        shk->msleep = 0;
        shk->mtrapseen = ~0U;   /* we know all the traps already */
        ESHK->shoproom = roomno;
        ESHK->shoplevel = dlevel;
        ESHK->shd = doors[sh];
        ESHK->shk.x = sx;
        ESHK->shk.y = sy;
        ESHK->robbed = 0;
        ESHK->visitct = 0;
        ESHK->following = 0;
        shk->mgold = 1000 + 30 * rnd(100);      /* initial capital */
        ESHK->billct = 0;
        findname(ESHK->shknam, let);
        for (sx = sroom->lx; sx <= sroom->hx; sx++)
                for (sy = sroom->ly; sy <= sroom->hy; sy++) {
                        struct monst   *mtmp;
                        if ((sx == sroom->lx && doors[sh].x == sx - 1) ||
                            (sx == sroom->hx && doors[sh].x == sx + 1) ||
                            (sy == sroom->ly && doors[sh].y == sy - 1) ||
                            (sy == sroom->hy && doors[sh].y == sy + 1))
                                continue;
                        if (rn2(100) < dlevel && !m_at(sx, sy) &&
                            (mtmp = makemon(PM_MIMIC, sx, sy))) {
                                mtmp->mimic = 1;
                                mtmp->mappearance =
                                        (let && rn2(10) < dlevel) ? let : ']';
                                continue;
                        }
                        (void) mkobj_at(let, sx, sy);
                }
}

void
mkzoo(int type)
{
        struct mkroom  *sroom;
        struct monst   *mon;
        int             sh, sx, sy, i;
        int             goldlim = 500 * dlevel;
        int             moct = 0;

        i = nroom;
        for (sroom = &rooms[rn2(nroom)];; sroom++) {
                if (sroom == &rooms[nroom])
                        sroom = &rooms[0];
                if (!i-- || sroom->hx < 0)
                        return;
                if (sroom->rtype)
                        continue;
                if (type == MORGUE && sroom->rlit)
                        continue;
                if (has_upstairs(sroom) || (has_dnstairs(sroom) && rn2(3)))
                        continue;
                if (sroom->doorct == 1 || !rn2(5))
                        break;
        }
        sroom->rtype = type;
        sh = sroom->fdoor;
        for (sx = sroom->lx; sx <= sroom->hx; sx++)
                for (sy = sroom->ly; sy <= sroom->hy; sy++) {
                        if ((sx == sroom->lx && doors[sh].x == sx - 1) ||
                            (sx == sroom->hx && doors[sh].x == sx + 1) ||
                            (sy == sroom->ly && doors[sh].y == sy - 1) ||
                            (sy == sroom->hy && doors[sh].y == sy + 1))
                                continue;
                        mon = makemon(
                                      (type == MORGUE) ? morguemon() :
                                      (type == BEEHIVE) ? PM_KILLER_BEE : (struct permonst *) 0,
                                      sx, sy);
                        if (mon)
                                mon->msleep = 1;
                        switch (type) {
                        case ZOO:
                                i = sq(dist2(sx, sy, doors[sh].x, doors[sh].y));
                                if (i >= goldlim)
                                        i = 5 * dlevel;
                                goldlim -= i;
                                mkgold((long) (10 + rn2(i)), sx, sy);
                                break;
                        case MORGUE:
                                /*
                                 * Usually there is one dead body in the
                                 * morgue
                                 */
                                if (!moct && rn2(3)) {
                                        mksobj_at(CORPSE, sx, sy);
                                        moct++;
                                }
                                break;
                        case BEEHIVE:
                                if (!rn2(3))
                                        mksobj_at(LUMP_OF_ROYAL_JELLY, sx, sy);
                                break;
                        }
                }
}

static const struct permonst *
morguemon(void)
{
        int             i = rn2(100), hd = rn2(dlevel);

        if (hd > 10 && i < 10)
                return (PM_DEMON);
        if (hd > 8 && i > 85)
                return (PM_VAMPIRE);
        return ((i < 40) ? PM_GHOST : (i < 60) ? PM_WRAITH : PM_ZOMBIE);
}

void
mkswamp(void)
{                               /* Michiel Huisjes & Fred de Wilde */
        struct mkroom  *sroom;
        int             sx, sy, i, eelct = 0;

        for (i = 0; i < 5; i++) {       /* 5 tries */
                sroom = &rooms[rn2(nroom)];
                if (sroom->hx < 0 || sroom->rtype ||
                    has_upstairs(sroom) || has_dnstairs(sroom))
                        continue;

                /* satisfied; make a swamp */
                sroom->rtype = SWAMP;
                for (sx = sroom->lx; sx <= sroom->hx; sx++)
                        for (sy = sroom->ly; sy <= sroom->hy; sy++)
                                if ((sx + sy) % 2 && !o_at(sx, sy) && !t_at(sx, sy)
                                  && !m_at(sx, sy) && !nexttodoor(sx, sy)) {
                                        levl[sx][sy].typ = POOL;
                                        levl[sx][sy].scrsym = POOL_SYM;
                                        if (!eelct || !rn2(4)) {
                                                (void) makemon(PM_EEL, sx, sy);
                                                eelct++;
                                        }
                                }
        }
}

static int
nexttodoor(int sx, int sy)
{
        int             dx, dy;
        struct rm      *lev;
        for (dx = -1; dx <= 1; dx++)
                for (dy = -1; dy <= 1; dy++)
                        if ((lev = &levl[sx + dx][sy + dy])->typ == DOOR ||
                            lev->typ == SDOOR || lev->typ == LDOOR)
                                return (1);
        return (0);
}

static int
has_dnstairs(struct mkroom *sroom)
{
        return (sroom->lx <= xdnstair && xdnstair <= sroom->hx &&
                sroom->ly <= ydnstair && ydnstair <= sroom->hy);
}

static int
has_upstairs(struct mkroom *sroom)
{
        return (sroom->lx <= xupstair && xupstair <= sroom->hx &&
                sroom->ly <= yupstair && yupstair <= sroom->hy);
}

static int
isbig(struct mkroom *sroom)
{
        int             area = (sroom->hx - sroom->lx) * (sroom->hy - sroom->ly);
        return (area > 20);
}

static int
dist2(int x0, int y0, int x1, int y1)
{
        return ((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1));
}

static int
sq(int a)
{
        return (a * a);
}
#endif  /* QUEST */