/
help/
log/
player/
post/
rooms/
util/
util/italk/
util/list/
util/msg/
util/muddle/
/*
 * CREATURE.C:
 *
 *  Routines that act on creatures.
 *
 *  Copyright (C) 1991, 1992, 1993 Brett J. Vickers
 *
 */

#include "mstruct.h"
#include "mextern.h"

/**********************************************************************/
/*              find_crt                  */
/**********************************************************************/

/* This function will attempt to locate a given creature within a given */
/* list.  The first parameter contains a pointer to the creature doing  */
/* the search, the second contains a tag pointer to the first tag in    */
/* the list.  The third contains the match string, and the fourth       */
/* contains the number of times to match the string.            */

creature *find_crt(ply_ptr, first_ct, str, val)
creature    *ply_ptr;
ctag        *first_ct;
char        *str;
int     val;
{
    ctag    *cp;
    int match=0, found=0;

    cp = first_ct;
    while(cp) {
        if(cp->crt->class >= CARETAKER && F_ISSET(cp->crt, PDMINV)) {
            cp = cp->next_tag;
            continue;
        }
        if(EQUAL(cp->crt, str) &&
           (F_ISSET(ply_ptr, PDINVI) ?
           1:!F_ISSET(cp->crt, MINVIS))) {
            match++;
            if(match == val) {
                found = 1;
                break;
            }
        }
        cp = cp->next_tag;
    }

    if(found)
        return(cp->crt);
    else
        return(0);
}

/**********************************************************************/
/*              add_enm_crt               */
/**********************************************************************/

/* This function adds a new enemy's name to the front a creature's enemy */
/* list.  The closer to the front of the list, the higher the likelihood */
/* that you will be attacked.                        */

int add_enm_crt(enemy, crt_ptr)
char        *enemy;
creature    *crt_ptr;
{
    etag    *ep;
    int n;
    n = del_enm_crt(enemy, crt_ptr);

    ep = (etag *)malloc(sizeof(etag));
    if(!ep)
        merror("add_enm_crt", FATAL);
    
    strcpy(ep->enemy, enemy);
    ep->next_tag = 0;
    ep->damage = (n > -1) ? n : 0;

    if(!crt_ptr->first_enm) {
        crt_ptr->first_enm = ep;
        return(n);
    }

    ep->next_tag = crt_ptr->first_enm;
    crt_ptr->first_enm = ep;

    if(n < 0) crt_ptr->NUMHITS = 0;

    return(n);

}

/**********************************************************************/
/*              del_enm_crt               */
/**********************************************************************/

/* This function removes an enemy's name from his enemy list.         */

int del_enm_crt(enemy, crt_ptr)
char        *enemy;
creature    *crt_ptr;
{
    etag    *ep, *prev;
    int dmg;

    ep = crt_ptr->first_enm;
    if(!ep) return(-1);

    if(!strcmp(ep->enemy, enemy)) {
        crt_ptr->first_enm = ep->next_tag;
        dmg = ep->damage;
        free(ep);
        return(dmg);
    }

    while(ep) {
        if(!strcmp(ep->enemy, enemy)) {
            prev->next_tag = ep->next_tag;
            dmg = ep->damage;
            free(ep);
            return(dmg);
        }
        prev = ep;
        ep = ep->next_tag;
    }

    return(-1);

}

/**********************************************************************/
/*              end_enm_crt               */
/**********************************************************************/

/* This function moves an enemy within a monster's enemy list to the */
/* very end of the list.                         */

void end_enm_crt(enemy, crt_ptr)
char        *enemy;
creature    *crt_ptr;
{
    etag    *ep, *move;

    move = (etag *)malloc(sizeof(etag));
    if(!move)
        merror("end_enm_crt", FATAL);

    strcpy(move->enemy, enemy);
    move->next_tag = 0;
    move->damage =  del_enm_crt(enemy, crt_ptr);

    ep = crt_ptr->first_enm;
    if(!ep) {
        crt_ptr->first_enm = move;
        return;
    }

    while(ep->next_tag)
        ep = ep->next_tag;

    ep->next_tag = move;

}

/************************************************************************/
/*              add_enm_dmg             */
/************************************************************************/

/* This function adds the amount of damage indicated by the third   */
/* argument to the enemy total for the creature pointed to by the   */
/* second argument.  The first argument contains the name of the player */
/* who hit the creature.                        */

void add_enm_dmg(enemy, crt_ptr, dmg)
char        *enemy;
creature    *crt_ptr;
int     dmg;
{
    etag    *ep;

    ep = crt_ptr->first_enm;
    while(ep) {
        if(!strcmp(enemy, ep->enemy))
            ep->damage += dmg;
        ep = ep->next_tag;
    }
}

/**********************************************************************/
/*              is_enm_crt                */
/**********************************************************************/

/* This function returns true if the name passed in the first parameter */
/* is in the enemy list of the creature pointed to by the second.       */

int is_enm_crt(enemy, crt_ptr)
char        *enemy;
creature    *crt_ptr;
{
    etag    *ep;

    ep = crt_ptr->first_enm;

    while(ep) {
        if(!strcmp(ep->enemy, enemy))
            return(1);
        ep = ep->next_tag;
    }

    return(0);
}

/**********************************************************************/
/*              die                   */
/**********************************************************************/

/* This function is called whenever a player or a monster dies.  The */
/* first parameter contains a pointer to the creature that has died. */
/* The second contains a pointer to the creature who did the dirty   */
/* deed.  Different chains of events are taken depending on whether  */
/* the deceased was a player or a monster.               */

void die(crt_ptr, att_ptr)
creature    *crt_ptr;
creature    *att_ptr;
{
    otag        *op, *temp;
    etag        *ep;
    creature    *ply_ptr;
    room        *rom_ptr;
    object      *obj_ptr;
    char        str[2048];
    long        i, t, profloss, total, xpv;
    int     n, levels = 0, expdiv, lessthanzero=0;

   str[0] =0;
    if(crt_ptr->type == MONSTER) {

        ep = crt_ptr->first_enm;
        while(ep) {
            ply_ptr = find_who(ep->enemy);
            if(ply_ptr) levels += ply_ptr->level;
            ep = ep->next_tag;
        }

        expdiv = crt_ptr->experience / levels;

        ep = crt_ptr->first_enm;
        while(ep) {
            ply_ptr = find_who(ep->enemy);
            if(ply_ptr) {
                expdiv = (crt_ptr->experience * ep->damage) /
                    MAX(crt_ptr->hpmax,1);
                expdiv = MIN(expdiv, crt_ptr->experience);
                print(ply_ptr->fd,
            "You gained %d experience for the death of %m.\n",
                    expdiv, crt_ptr);
                ply_ptr->experience += expdiv;
                ply_ptr->alignment -= crt_ptr->alignment/5;
                if(ply_ptr->alignment > 1000)
                    ply_ptr->alignment = 1000;
                if(ply_ptr->alignment < -1000)
                    ply_ptr->alignment = -1000;
            }
            ep = ep->next_tag;
        }
	if(!F_ISSET(crt_ptr, MTRADE)){
        sprintf(str, "%s was carrying: ", crt_str(crt_ptr, 0,CAP|INV));
        n = strlen(str);
        i = list_obj(&str[n], att_ptr, crt_ptr->first_obj);
	}
        if(F_ISSET(crt_ptr, MPERMT))
            die_perm_crt(crt_ptr);

        op = crt_ptr->first_obj;
        while(op) {
            temp = op->next_tag;
            obj_ptr = op->obj;
            del_obj_crt(obj_ptr, crt_ptr);
            if(!F_ISSET(crt_ptr, MTRADE)){
	    add_obj_rom(obj_ptr, crt_ptr->parent_rom);
            }
	    op = temp;
        }

        if(crt_ptr->gold) {
            load_obj(0, &obj_ptr);
            sprintf(obj_ptr->name, "%d gold coins", crt_ptr->gold);
            if(i)
                strcat(str, ", ");
            strcat(str, obj_ptr->name);
            obj_ptr->value = crt_ptr->gold;
            add_obj_rom(obj_ptr, crt_ptr->parent_rom);
        }

        del_active(crt_ptr);
        del_crt_rom(crt_ptr, crt_ptr->parent_rom);

        free_crt(crt_ptr);

        if(strlen(str) > n)
            print(att_ptr->fd, "%s.\n", str);
    }

    else if(crt_ptr->type == PLAYER) {

        rom_ptr = crt_ptr->parent_rom;

        i = LT(crt_ptr, LT_PLYKL);
        t = time(0);

        if(att_ptr->type != PLAYER || att_ptr == crt_ptr) {
            if(crt_ptr->level < 4)
                crt_ptr->experience -= crt_ptr->experience/10;
            else {
                crt_ptr->experience -= crt_ptr->experience/4;
                n = crt_ptr->level -
                    exp_to_lev(crt_ptr->experience);
		if (n > 1)
			if ( crt_ptr->level < (MAXALVL+2))
				crt_ptr->experience = needed_exp[crt_ptr->level-3];
			else
			    crt_ptr->experience = 
			    (long)((needed_exp[MAXALVL-1]*(crt_ptr->level-2)));   
            }
        }

        crt_ptr->lasttime[LT_PLYKL].ltime = 0L;
        crt_ptr->lasttime[LT_PLYKL].interval = 0L;

        if(att_ptr->type == PLAYER) {
            att_ptr->lasttime[LT_PLYKL].ltime = t;
            att_ptr->lasttime[LT_PLYKL].interval = 
                (long)mrand(7,14) * 86400L;
	    if(F_ISSET(att_ptr,PPLDGK) && F_ISSET(crt_ptr,PPLDGK) && 
(crt_ptr->name != att_ptr->name))
		if(F_ISSET(att_ptr,PKNGDM) != F_ISSET(crt_ptr,PKNGDM))
		{
		    xpv = (crt_ptr->level*crt_ptr->level)*10;
		    print(crt_ptr->fd, "You have been bested.\nYou lose %d experience.\n",xpv);
		    crt_ptr->experience -= xpv;
		    if (crt_ptr->experience <=0) crt_ptr->experience=0;
		    xpv /= 2;
		    print(att_ptr->fd, "You have vanquished %m\n",crt_ptr);
		    print(att_ptr->fd, "You gain %d for your heroic deed.\n",xpv);
		    att_ptr->experience += xpv;
		    add_prof(att_ptr,xpv);
		}
		else{
		    xpv = (crt_ptr->level*crt_ptr->level)*5;
		    xpv = MIN(att_ptr->experience,xpv);
		    crt_ptr->experience -= xpv; 
		    if(crt_ptr->experience<=0) crt_ptr->experience=0;
		    print(crt_ptr->fd, "You are killed by a fellow member of your organization!\n");
		    print(crt_ptr->fd, "You lose %d experience!\n",xpv);

		    xpv = (crt_ptr->level*crt_ptr->level)*10;
		    xpv = MIN(att_ptr->experience,xpv);
		    print(att_ptr->fd, "You killed a fellow member of your organization!\n");
		    print(att_ptr->fd, "You lose %d experience!\n",xpv);
		    att_ptr->experience -= xpv;
		    if(att_ptr->experience<=0) att_ptr->experience=0;
		    lower_prof(att_ptr,xpv);
		}
	}
		if(att_ptr->type == PLAYER && (att_ptr->level-4>crt_ptr->level)){
			xpv = ((att_ptr->level-crt_ptr->level)*25);
			xpv = MIN(att_ptr->experience,xpv);
			print(att_ptr->fd, "Try fighting a worthy foe.\n");
			print(att_ptr->fd, "You lose %d experience for your cowardly act!\n", xpv);
			att_ptr->experience -= xpv;
			if(att_ptr->experience<=0) att_ptr->experience=0;


	        }

        else
            del_enm_crt(crt_ptr->name, att_ptr);

        for(n=0,total=0L; n<5; n++)
            total += crt_ptr->proficiency[n];
        for(n=0; n<4; n++)
            total += crt_ptr->realm[n];

        profloss = MAX(0L, total - crt_ptr->experience - 1024L);
        while(profloss > 9L && lessthanzero < 9) {
            lessthanzero = 0;
            for(n=0; n<9; n++) {
                if(n < 5) {
                    crt_ptr->proficiency[n] -=
                       profloss/(9-n);
                    profloss -= profloss/(9-n);
                    if(crt_ptr->proficiency[n] < 0L) {
                        lessthanzero++;
                        profloss -=
                           crt_ptr->proficiency[n];
                        crt_ptr->proficiency[n] = 0L;
                    }
                }
                else {
                    crt_ptr->realm[n-5] -= profloss/(9-n);
                    profloss -= profloss/(9-n);
                    if(crt_ptr->realm[n-5] < 0L) {
                        lessthanzero++;
                        profloss -=
                            crt_ptr->realm[n-5];
                        crt_ptr->realm[n-5] = 0L;
                    }
                }
            }
        }
        for(n=1,total=0; n<5; n++)
            if(crt_ptr->proficiency[n] >
                crt_ptr->proficiency[total]) total = n;
        if(crt_ptr->proficiency[total] < 1024L)
            crt_ptr->proficiency[total] = 1024L;

        n = exp_to_lev(crt_ptr->experience);
        while(crt_ptr->level > n)
            down_level(crt_ptr);

	if (att_ptr->type == PLAYER) {
		crt_ptr->hpcur = crt_ptr->hpmax;
        	crt_ptr->mpcur = MAX(crt_ptr->mpcur,(crt_ptr->mpmax)/10);

	} else {
		crt_ptr->hpcur = crt_ptr->hpmax;
        	crt_ptr->mpcur = crt_ptr->mpmax;
	}
        F_CLR(crt_ptr, PPOISN);
        F_CLR(crt_ptr, PDISEA);

        if(crt_ptr->ready[WIELD-1] &&
           !F_ISSET(crt_ptr->ready[WIELD-1], OCURSE)) {
            add_obj_rom(crt_ptr->ready[WIELD-1], 
                crt_ptr->parent_rom);
            temp_perm(crt_ptr->ready[WIELD-1]);
            crt_ptr->ready[WIELD-1] = 0;
        }

        for(i=0; i<MAXWEAR; i++) {
            if(crt_ptr->ready[i] &&
               !F_ISSET(crt_ptr->ready[i], OCURSE)) {
                if(att_ptr->type == PLAYER)
                    add_obj_rom(crt_ptr->ready[i],
                    crt_ptr->parent_rom);
                else
                    add_obj_crt(crt_ptr->ready[i],
                    crt_ptr);
                crt_ptr->ready[i] = 0;
            }
        }
        compute_ac(crt_ptr);
        compute_thaco(crt_ptr);

        if(crt_ptr == att_ptr)
            broadcast("### Sadly, %s died.", crt_ptr->name);
        else
            broadcast("### Sadly, %s was killed by %1m.", 
                   crt_ptr->name, att_ptr);

        del_ply_rom(crt_ptr, rom_ptr);
        load_rom(50, &rom_ptr);
        add_ply_rom(crt_ptr, rom_ptr);

        savegame(crt_ptr, 0);
        print(crt_ptr->fd, 
            "If you had a weapon, it was dropped where you died.\n");
    }
        
}

void temp_perm(obj_ptr)
object  *obj_ptr;
{
    otag    *op;

    F_SET(obj_ptr, OPERM2);
    F_SET(obj_ptr, OTEMPP);
    op = obj_ptr->first_obj;
    while(op) {
        temp_perm(op->obj);
        op = op->next_tag;
    }
}

/**********************************************************************/
/*              die_perm_crt                  */
/**********************************************************************/

/* This function is called whenever a permanent monster is killed.  The */
/* room the monster was in has its permanent monster list checked to    */
/* if the monster was loaded from that room.  If it was, then the last- */
/* time field for that permanent monster is updated.            */

void die_perm_crt(crt_ptr)
creature    *crt_ptr;
{
    creature    *temp_crt;
    room        *rom_ptr;
    long        t;
    int     i;

    t = time(0);

    rom_ptr = crt_ptr->parent_rom;
    
    for(i=0; i<10; i++) {
        if(!rom_ptr->perm_mon[i].misc) continue;
        if(rom_ptr->perm_mon[i].ltime + rom_ptr->perm_mon[i].interval >
           t) continue;
        if(load_crt(rom_ptr->perm_mon[i].misc, &temp_crt) < 0)
            continue;
        if(!strcmp(temp_crt->name, crt_ptr->name)) {
            rom_ptr->perm_mon[i].ltime = t;
            free_crt(temp_crt);
            break;
        }
        free_crt(temp_crt);
    }

	if(F_ISSET(crt_ptr,MDEATH)){
	  	int     fd,n;
    	char    tmp[2048], file[80],name[80];
   
		strcpy(name, crt_ptr->name);
        for(i=0; name[i]; i++)
            if(name[i] == ' ') name[i] = '_';

    	sprintf(file,"%s/ddesc/%s_%d",OBJPATH,name,crt_ptr->level);
    	fd = open(file,O_RDONLY,0);
    	if (fd){
		    n = read(fd,tmp,2048);
    		tmp[n] = 0;
   			broadcast_rom(-1, crt_ptr->rom_num,"\n%s",tmp);
		}
    	close(fd);
	}
}

/**********************************************************************/
/*              check_for_flee                */
/**********************************************************************/

/* This function will determine if a monster is about to flee to another */
/* room.  Only fleeing monsters will flee, and then only after they have */
/* less than 10% of their hit points.  Plus, they can only flee into     */
/* rooms that have already been loaded into memory.          */

void check_for_flee(crt_ptr)
creature    *crt_ptr;
{
    room    *rom_ptr;
    xtag    *xp;

    if(!F_ISSET(crt_ptr, MFLEER))
        return;

    if(crt_ptr->hpcur > crt_ptr->hpmax/5)
        return;

    rom_ptr = crt_ptr->parent_rom;

    xp = rom_ptr->first_ext;
    while(xp) {
        if(!F_ISSET(xp->ext, XSECRT) && !F_ISSET(xp->ext, XCLOSD) &&
           is_rom_loaded(xp->ext->room) && mrand(1,100) < 75) {
            broadcast_rom(-1, rom_ptr->rom_num, 
                      "%M flees to the %s.", crt_ptr,
                      xp->ext->name);
            die_perm_crt(crt_ptr);
            del_crt_rom(crt_ptr, rom_ptr);
            load_rom(xp->ext->room, &rom_ptr);
            add_crt_rom(crt_ptr, rom_ptr, 1);
            if(!rom_ptr->first_ply)
                del_active(crt_ptr);
            return;
        }
        xp = xp->next_tag;
    }
}

/**********************************************************************/
/*              consider                  */
/**********************************************************************/

/* This function allows the player pointed to by the first argument to */
/* consider how difficult it would be to kill the creature in the      */
/* second parameter.                               */

void consider(ply_ptr, crt_ptr)
creature    *ply_ptr;
creature    *crt_ptr;
{
    char    he[5], him[5];
    int fd, diff;

    fd = ply_ptr->fd;
    diff = ply_ptr->level - crt_ptr->level;
    diff = MAX(-4, diff);
    diff = MIN(4, diff);

    sprintf(he, "%s", F_ISSET(crt_ptr, MMALES) ? "He":"She");
    sprintf(him, "%s", F_ISSET(crt_ptr, MMALES) ? "him":"her");

    switch(diff) {
        case 0:
            print(fd, "%s is a perfect match for you!\n", he);
            break;
        case 1:
            print(fd, "%s is not quite as good as you.\n", he);
            break;
        case -1:
            print(fd, "%s is a little better than you.\n", he);
            break;
        case 2:
            print(fd, "%s shouldn't be too tough to kill.\n", he);
            break;
        case -2:
            print(fd, "%s might be tough to kill.\n", he);
            break;
        case 3:
            print(fd, "%s should be easy to kill.\n", he);
            break;
        case -3:
            print(fd, "%s should be really hard to kill.\n", he);
            break;
        case 4:
            print(fd, "You could kill %s with a needle.\n", him);
            break;
        case -4:
            print(fd, "%s could kill you with a needle.\n", he);
            break;
    }
}