root/games/larn/global.c
/*      $NetBSD: global.c,v 1.15 2021/05/02 12:50:45 rillig Exp $       */

/*
 * global.c             Larn is copyrighted 1986 by Noah Morgan.
 *
 * raiselevel()         subroutine to raise the player one level
 * loselevel()          subroutine to lower the player by one level
 * raiseexperience(x)   subroutine to increase experience points
 * loseexperience(x)    subroutine to lose experience points
 * losehp(x)            subroutine to remove hit points from the player
 * losemhp(x)           subroutine to remove max # hit points from the player
 * raisehp(x)           subroutine to gain hit points
 * raisemhp(x)          subroutine to gain maximum hit points
 * losemspells(x)       subroutine to lose maximum spells
 * raisemspells(x)      subroutine to gain maximum spells
 * makemonst(lev)       function to return monster number for a randomly
 *                      selected monster
 * positionplayer()     function to be sure player is not in a wall
 * recalc()             function to recalculate the armor class of the player
 * quit()               subroutine to ask if the player really wants to quit
 */
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: global.c,v 1.15 2021/05/02 12:50:45 rillig Exp $");
#endif /* not lint */

#include <string.h>
#include <unistd.h>
#include "header.h"
#include "extern.h"
extern int      score[], dropflag;
extern char     *what[], *who[];
extern char     winner[];
extern char     sciv[SCORESIZE + 1][26][2];
extern const char *password;

/*
        raiselevel()

        subroutine to raise the player one level
        uses the skill[] array to find level boundarys
        uses c[EXPERIENCE]  c[LEVEL]
 */
void
raiselevel(void)
{
        if (c[LEVEL] < MAXPLEVEL)
                raiseexperience((long) (skill[c[LEVEL]] - c[EXPERIENCE]));
}

/*
    loselevel()

        subroutine to lower the players character level by one
 */
void
loselevel(void)
{
        if (c[LEVEL] > 1)
                loseexperience((long) (c[EXPERIENCE] - skill[c[LEVEL] - 1] + 1));
}

/*
        raiseexperience(x)

        subroutine to increase experience points
 */
void
raiseexperience(long x)
{
        int    i, tmp;
        i = c[LEVEL];
        c[EXPERIENCE] += x;
        while (c[EXPERIENCE] >= skill[c[LEVEL]] && (c[LEVEL] < MAXPLEVEL)) {
                tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1;
                c[LEVEL]++;
                raisemhp((int) (rnd(3) + rnd((tmp > 0) ? tmp : 1)));
                raisemspells((int) rund(3));
                if (c[LEVEL] < 7 - c[HARDGAME])
                        raisemhp((int) (c[CONSTITUTION] >> 2));
        }
        if (c[LEVEL] != i) {
                cursors();
                beep();
                lprintf("\nWelcome to level %ld", (long) c[LEVEL]);     /* if we changed levels  */
        }
        bottomline();
}

/*
        loseexperience(x)

        subroutine to lose experience points
 */
void
loseexperience(long x)
{
        int    i, tmp;
        i = c[LEVEL];
        c[EXPERIENCE] -= x;
        if (c[EXPERIENCE] < 0)
                c[EXPERIENCE] = 0;
        while (c[EXPERIENCE] < skill[c[LEVEL] - 1]) {
                if (--c[LEVEL] <= 1)
                        c[LEVEL] = 1;   /* down one level                */
                tmp = (c[CONSTITUTION] - c[HARDGAME]) >> 1;     /* lose hpoints */
                losemhp((int) rnd((tmp > 0) ? tmp : 1));        /* lose hpoints */
                if (c[LEVEL] < 7 - c[HARDGAME])
                        losemhp((int) (c[CONSTITUTION] >> 2));
                losemspells((int) rund(3));     /* lose spells           */
        }
        if (i != c[LEVEL]) {
                cursors();
                beep();
                lprintf("\nYou went down to level %ld!", (long) c[LEVEL]);
        }
        bottomline();
}

/*
        losehp(x)
        losemhp(x)

        subroutine to remove hit points from the player
        warning -- will kill player if hp goes to zero
 */
void
losehp(int x)
{
        if ((c[HP] -= x) <= 0) {
                beep();
                lprcat("\n");
                nap(3000);
                died(lastnum);
        }
}

void
losemhp(int x)
{
        c[HP] -= x;
        if (c[HP] < 1)
                c[HP] = 1;
        c[HPMAX] -= x;
        if (c[HPMAX] < 1)
                c[HPMAX] = 1;
}

/*
        raisehp(x)
        raisemhp(x)

        subroutine to gain maximum hit points
 */
void
raisehp(int x)
{
        if ((c[HP] += x) > c[HPMAX])
                c[HP] = c[HPMAX];
}

void
raisemhp(int x)
{
        c[HPMAX] += x;
        c[HP] += x;
}

/*
        raisemspells(x)

        subroutine to gain maximum spells
 */
void
raisemspells(int x)
{
        c[SPELLMAX] += x;
        c[SPELLS] += x;
}

/*
        losemspells(x)

        subroutine to lose maximum spells
 */
void
losemspells(int x)
{
        if ((c[SPELLMAX] -= x) < 0)
                c[SPELLMAX] = 0;
        if ((c[SPELLS] -= x) < 0)
                c[SPELLS] = 0;
}

/*
        makemonst(lev)
                int lev;

        function to return monster number for a randomly selected monster
                for the given cave level
 */
int
makemonst(int lev)
{
        int    tmp, x;
        if (lev < 1)
                lev = 1;
        if (lev > 12)
                lev = 12;
        tmp = WATERLORD;
        if (lev < 5)
                while (tmp == WATERLORD)
                        tmp = rnd((x = monstlevel[lev - 1]) ? x : 1);
        else
                while (tmp == WATERLORD)
                        tmp = rnd((x = monstlevel[lev - 1] - monstlevel[lev - 4]) ? x : 1) + monstlevel[lev - 4];

        while (monster[tmp].genocided && tmp < MAXMONST)
                tmp++;          /* genocided? */
        return (tmp);
}

/*
        positionplayer()

        function to be sure player is not in a wall
 */
void
positionplayer(void)
{
        int             try;
        try = 2;
        while ((item[playerx][playery] || mitem[playerx][playery]) && (try))
                if (++playerx >= MAXX - 1) {
                        playerx = 1;
                        if (++playery >= MAXY - 1) {
                                playery = 1;
                                --try;
                        }
                }
        if (try == 0)
                lprcat("Failure in positionplayer\n");
}

/*
        recalc()        function to recalculate the armor class of the player
 */
void
recalc(void)
{
        int    i, j, k;
        c[AC] = c[MOREDEFENSES];
        if (c[WEAR] >= 0)
                switch (iven[c[WEAR]]) {
                case OSHIELD:
                        c[AC] += 2 + ivenarg[c[WEAR]];
                        break;
                case OLEATHER:
                        c[AC] += 2 + ivenarg[c[WEAR]];
                        break;
                case OSTUDLEATHER:
                        c[AC] += 3 + ivenarg[c[WEAR]];
                        break;
                case ORING:
                        c[AC] += 5 + ivenarg[c[WEAR]];
                        break;
                case OCHAIN:
                        c[AC] += 6 + ivenarg[c[WEAR]];
                        break;
                case OSPLINT:
                        c[AC] += 7 + ivenarg[c[WEAR]];
                        break;
                case OPLATE:
                        c[AC] += 9 + ivenarg[c[WEAR]];
                        break;
                case OPLATEARMOR:
                        c[AC] += 10 + ivenarg[c[WEAR]];
                        break;
                case OSSPLATE:
                        c[AC] += 12 + ivenarg[c[WEAR]];
                        break;
                }

        if (c[SHIELD] >= 0)
                if (iven[c[SHIELD]] == OSHIELD)
                        c[AC] += 2 + ivenarg[c[SHIELD]];
        if (c[WIELD] < 0)
                c[WCLASS] = 0;
        else {
                i = ivenarg[c[WIELD]];
                switch (iven[c[WIELD]]) {
                case ODAGGER:
                        c[WCLASS] = 3 + i;
                        break;
                case OBELT:
                        c[WCLASS] = 7 + i;
                        break;
                case OSHIELD:
                        c[WCLASS] = 8 + i;
                        break;
                case OSPEAR:
                        c[WCLASS] = 10 + i;
                        break;
                case OFLAIL:
                        c[WCLASS] = 14 + i;
                        break;
                case OBATTLEAXE:
                        c[WCLASS] = 17 + i;
                        break;
                case OLANCE:
                        c[WCLASS] = 19 + i;
                        break;
                case OLONGSWORD:
                        c[WCLASS] = 22 + i;
                        break;
                case O2SWORD:
                        c[WCLASS] = 26 + i;
                        break;
                case OSWORD:
                        c[WCLASS] = 32 + i;
                        break;
                case OSWORDofSLASHING:
                        c[WCLASS] = 30 + i;
                        break;
                case OHAMMER:
                        c[WCLASS] = 35 + i;
                        break;
                default:
                        c[WCLASS] = 0;
                }
        }
        c[WCLASS] += c[MOREDAM];

        /* now for regeneration abilities based on rings         */
        c[REGEN] = 1;
        c[ENERGY] = 0;
        j = 0;
        for (k = 25; k > 0; k--)
                if (iven[k]) {
                        j = k;
                        k = 0;
                }
        for (i = 0; i <= j; i++) {
                switch (iven[i]) {
                case OPROTRING:
                        c[AC] += ivenarg[i] + 1;
                        break;
                case ODAMRING:
                        c[WCLASS] += ivenarg[i] + 1;
                        break;
                case OBELT:
                        c[WCLASS] += ((ivenarg[i] << 1)) + 2;
                        break;

                case OREGENRING:
                        c[REGEN] += ivenarg[i] + 1;
                        break;
                case ORINGOFEXTRA:
                        c[REGEN] += 5 * (ivenarg[i] + 1);
                        break;
                case OENERGYRING:
                        c[ENERGY] += ivenarg[i] + 1;
                        break;
                }
        }
}


/*
        quit()

        subroutine to ask if the player really wants to quit
 */
void
quit(void)
{
        int    i;
        cursors();
        strcpy(lastmonst, "");
        lprcat("\n\nDo you really want to quit?");
        while (1) {
                i = ttgetch();
                if (i == 'y') {
                        died(300);
                        return;
                }
                if ((i == 'n') || (i == '\33')) {
                        lprcat(" no");
                        lflush();
                        return;
                }
                lprcat("\n");
                setbold();
                lprcat("Yes");
                resetbold();
                lprcat(" or ");
                setbold();
                lprcat("No");
                resetbold();
                lprcat(" please?   Do you want to quit? ");
        }
}

/*
        function to ask --more-- then the user must enter a space
 */
void
more(void)
{
        lprcat("\n  --- press ");
        standout("space");
        lprcat(" to continue --- ");
        while (ttgetch() != ' ');
}

/*
        function to put something in the players inventory
        returns 0 if success, 1 if a failure
 */
int
take(int theitem, int arg)
{
        int    i, limit;
        /* cursors(); */
        if ((limit = 15 + (c[LEVEL] >> 1)) > 26)
                limit = 26;
        for (i = 0; i < limit; i++)
                if (iven[i] == 0) {
                        iven[i] = theitem;
                        ivenarg[i] = arg;
                        limit = 0;
                        switch (theitem) {
                        case OPROTRING:
                        case ODAMRING:
                        case OBELT:
                                limit = 1;
                                break;
                        case ODEXRING:
                                c[DEXTERITY] += ivenarg[i] + 1;
                                limit = 1;
                                break;
                        case OSTRRING:
                                c[STREXTRA] += ivenarg[i] + 1;
                                limit = 1;
                                break;
                        case OCLEVERRING:
                                c[INTELLIGENCE] += ivenarg[i] + 1;
                                limit = 1;
                                break;
                        case OHAMMER:
                                c[DEXTERITY] += 10;
                                c[STREXTRA] += 10;
                                c[INTELLIGENCE] -= 10;
                                limit = 1;
                                break;

                        case OORBOFDRAGON:
                                c[SLAYING]++;
                                break;
                        case OSPIRITSCARAB:
                                c[NEGATESPIRIT]++;
                                break;
                        case OCUBEofUNDEAD:
                                c[CUBEofUNDEAD]++;
                                break;
                        case ONOTHEFT:
                                c[NOTHEFT]++;
                                break;
                        case OSWORDofSLASHING:
                                c[DEXTERITY] += 5;
                                limit = 1;
                                break;
                        };
                        lprcat("\nYou pick up:");
                        srcount = 0;
                        show3(i);
                        if (limit)
                                bottomline();
                        return (0);
                }
        lprcat("\nYou can't carry anything else");
        return (1);
}

/*
        subroutine to drop an object
        returns 1 if something there already else 0
 */
int
drop_object(int k)
{
        int             theitem;
        if ((k < 0) || (k > 25))
                return (0);
        theitem = iven[k];
        cursors();
        if (theitem == 0) {
                lprintf("\nYou don't have item %c! ", k + 'a');
                return (1);
        }
        if (item[playerx][playery]) {
                beep();
                lprcat("\nThere's something here already");
                return (1);
        }
        if (playery == MAXY - 1 && playerx == 33)
                return (1);     /* not in entrance */
        item[playerx][playery] = theitem;
        iarg[playerx][playery] = ivenarg[k];
        srcount = 0;
        lprcat("\n  You drop:");
        show3(k);               /* show what item you dropped */
        know[playerx][playery] = 0;
        iven[k] = 0;
        if (c[WIELD] == k)
                c[WIELD] = -1;
        if (c[WEAR] == k)
                c[WEAR] = -1;
        if (c[SHIELD] == k)
                c[SHIELD] = -1;
        adjustcvalues(theitem, ivenarg[k]);
        dropflag = 1;           /* say dropped an item so wont ask to pick it
                                 * up right away */
        return (0);
}

/*
        function to enchant armor player is currently wearing
 */
void
enchantarmor(void)
{
        int    tmp;
        if (c[WEAR] < 0) {
                if (c[SHIELD] < 0) {
                        cursors();
                        beep();
                        lprcat("\nYou feel a sense of loss");
                        return;
                } else {
                        tmp = iven[c[SHIELD]];
                        if (tmp != OSCROLL)
                                if (tmp != OPOTION) {
                                        ivenarg[c[SHIELD]]++;
                                        bottomline();
                                }
                }
        }
        tmp = iven[c[WEAR]];
        if (tmp != OSCROLL)
                if (tmp != OPOTION) {
                        ivenarg[c[WEAR]]++;
                        bottomline();
                }
}

/*
        function to enchant a weapon presently being wielded
 */
void
enchweapon(void)
{
        int    tmp;
        if (c[WIELD] < 0) {
                cursors();
                beep();
                lprcat("\nYou feel a sense of loss");
                return;
        }
        tmp = iven[c[WIELD]];
        if (tmp != OSCROLL)
                if (tmp != OPOTION) {
                        ivenarg[c[WIELD]]++;
                        if (tmp == OCLEVERRING)
                                c[INTELLIGENCE]++;
                        else if (tmp == OSTRRING)
                                c[STREXTRA]++;
                        else if (tmp == ODEXRING)
                                c[DEXTERITY]++;
                        bottomline();
                }
}

/*
        routine to tell if player can carry one more thing
        returns 1 if pockets are full, else 0
 */
int
pocketfull(void)
{
        int    i, limit;
        if ((limit = 15 + (c[LEVEL] >> 1)) > 26)
                limit = 26;
        for (i = 0; i < limit; i++)
                if (iven[i] == 0)
                        return (0);
        return (1);
}

/*
        function to return 1 if a monster is next to the player else returns 0
 */
int
nearbymonst(void)
{
        int    tmp, tmp2;
        for (tmp = playerx - 1; tmp < playerx + 2; tmp++)
                for (tmp2 = playery - 1; tmp2 < playery + 2; tmp2++)
                        if (mitem[tmp][tmp2])
                                return (1);     /* if monster nearby */
        return (0);
}

/*
        function to steal an item from the players pockets
        returns 1 if steals something else returns 0
 */
int
stealsomething(void)
{
        int    i, j;
        j = 100;
        while (1) {
                i = rund(26);
                if (iven[i])
                        if (c[WEAR] != i)
                                if (c[WIELD] != i)
                                        if (c[SHIELD] != i) {
                                                srcount = 0;
                                                show3(i);
                                                adjustcvalues(iven[i], ivenarg[i]);
                                                iven[i] = 0;
                                                return (1);
                                        }
                if (--j <= 0)
                        return (0);
        }
}

/*
        function to return 1 is player carrys nothing else return 0
 */
int
emptyhanded(void)
{
        int    i;
        for (i = 0; i < 26; i++)
                if (iven[i])
                        if (i != c[WIELD])
                                if (i != c[WEAR])
                                        if (i != c[SHIELD])
                                                return (0);
        return (1);
}

/*
        function to create a gem on a square near the player
 */
void
creategem(void)
{
        int    i, j;
        switch (rnd(4)) {
        case 1:
                i = ODIAMOND;
                j = 50;
                break;
        case 2:
                i = ORUBY;
                j = 40;
                break;
        case 3:
                i = OEMERALD;
                j = 30;
                break;
        default:
                i = OSAPPHIRE;
                j = 20;
                break;
        };
        createitem(i, rnd(j) + j / 10);
}

/*
        function to change character levels as needed when dropping an object
        that affects these characteristics
 */
void
adjustcvalues(int theitem, int arg)
{
        int    flag;
        flag = 0;
        switch (theitem) {
        case ODEXRING:
                c[DEXTERITY] -= arg + 1;
                flag = 1;
                break;
        case OSTRRING:
                c[STREXTRA] -= arg + 1;
                flag = 1;
                break;
        case OCLEVERRING:
                c[INTELLIGENCE] -= arg + 1;
                flag = 1;
                break;
        case OHAMMER:
                c[DEXTERITY] -= 10;
                c[STREXTRA] -= 10;
                c[INTELLIGENCE] += 10;
                flag = 1;
                break;
        case OSWORDofSLASHING:
                c[DEXTERITY] -= 5;
                flag = 1;
                break;
        case OORBOFDRAGON:
                --c[SLAYING];
                return;
        case OSPIRITSCARAB:
                --c[NEGATESPIRIT];
                return;
        case OCUBEofUNDEAD:
                --c[CUBEofUNDEAD];
                return;
        case ONOTHEFT:
                --c[NOTHEFT];
                return;
        case OLANCE:
                c[LANCEDEATH] = 0;
                return;
        case OPOTION:
        case OSCROLL:
                return;

        default:
                flag = 1;
        };
        if (flag)
                bottomline();
}

/*
        function to ask user for a password (no echo)
        returns 1 if entered correctly, 0 if not
 */
static char     gpwbuf[33];
int
getpassword(void)
{
        int    i, j;
        char  *gpwp;
        scbr();                 /* system("stty -echo cbreak"); */
        gpwp = gpwbuf;
        lprcat("\nEnter Password: ");
        lflush();
        i = strlen(password);
        for (j = 0; j < i; j++)
                *gpwp++ = ttgetch();
        gpwbuf[i] = 0;
        sncbr();                /* system("stty echo -cbreak"); */
        if (strcmp(gpwbuf, password) != 0) {
                lprcat("\nSorry\n");
                lflush();
                return (0);
        } else
                return (1);
}

/*
        subroutine to get a yes or no response from the user
        returns y or n
 */
int
getyn(void)
{
        int    i;
        i = 0;
        while (i != 'y' && i != 'n' && i != '\33')
                i = ttgetch();
        return (i);
}

/*
        function to calculate the pack weight of the player
        returns the number of pounds the player is carrying
 */
int
packweight(void)
{
        int    i, j, k;
        k = c[GOLD] / 1000;
        j = 25;
        while ((iven[j] == 0) && (j > 0))
                --j;
        for (i = 0; i <= j; i++)
                switch (iven[i]) {
                case 0:
                        break;
                case OSSPLATE:
                case OPLATEARMOR:
                        k += 40;
                        break;
                case OPLATE:
                        k += 35;
                        break;
                case OHAMMER:
                        k += 30;
                        break;
                case OSPLINT:
                        k += 26;
                        break;
                case OSWORDofSLASHING:
                case OCHAIN:
                case OBATTLEAXE:
                case O2SWORD:
                        k += 23;
                        break;
                case OLONGSWORD:
                case OSWORD:
                case ORING:
                case OFLAIL:
                        k += 20;
                        break;
                case OLANCE:
                case OSTUDLEATHER:
                        k += 15;
                        break;
                case OLEATHER:
                case OSPEAR:
                        k += 8;
                        break;
                case OORBOFDRAGON:
                case OBELT:
                        k += 4;
                        break;
                case OSHIELD:
                        k += 7;
                        break;
                case OCHEST:
                        k += 30 + ivenarg[i];
                        break;
                default:
                        k++;
                };
        return (k);
}

#ifndef MACRORND
/* macros to generate random numbers   1<=rnd(N)<=N   0<=rund(N)<=N-1 */
int
rnd(int x)
{
        return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)) + 1);
}

int
rund(int x)
{
        return ((((randx = randx * 1103515245 + 12345) >> 7) % (x)));
}
#endif  /* MACRORND */