/* ************************************************************************
*  file: phys.c, Physical routines                        Part of DIKUMUD *
*  Usage : Various physical checks/changes                                *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************* */



#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interp.h"
#include "db.h"
#include "bio.h"
#include "error.h"
#include "proto.h"


/* Global data */

extern struct room_data *world;
extern char *spell_wear_off_msg[];

void check_fall(struct char_data *ch);

/* Extern procedures */

char *strdup(char *str);

/* Extern procedures */


void check_fall(struct char_data *ch)
{
    int dam=0;
    int to_room;
    struct obj_info_exit *exit;

    /* Now handle the possibility of falling */

    while(world[ch->in_room].sector_type==SECT_NO_GROUND &&
	    GET_POS(ch) <POS_LEVITATE) {

	act("$n falls downward.",TRUE,ch,0,0,TO_ROOM);
	if(GET_POS(ch)>POS_SLEEP)
	    act("You fall, shapes and sounds shredding past you.",FALSE,ch,0,0,TO_CHAR);

        exit = get_exit(ch->in_room,5);
	if(exit && (exit->to_room!=ch->in_room)) {
	    to_room=exit->to_room;
	    char_from_room(ch);
	    char_to_room(ch,to_room,5);
	    act("$n falls here from above.",TRUE,ch,0,0,TO_ROOM);
	    if(dam)
		dam = dam << 1;
	    else
		dam = 10;
	} else {
	    /* Death from falling */
	    act("You do not survive the fall...", FALSE,ch,0,0,TO_CHAR);
	    death_cry(ch);
	    char_from_room(ch);
	    char_to_room(ch,0,5);
	    extract_char(ch);
	    dam=0;
	    break; /* get out of the while loop */
	}
    }

    if(dam) {
	damage(ch,ch,dam,DAM_FALLING);
	GET_POS(ch)=POS_SIT;
    }
}



void clone_obj(struct obj_data *obj)
{
    struct obj_data *clone;


    CREATE(clone, struct obj_data, 1);

    *clone = *obj;

    clone->name               = strdup(obj->name);
    clone->description        = strdup(obj->description);
    clone->short_description  = strdup(obj->short_description);
    clone->action_description = strdup(obj->action_description);
    clone->ex_description     = 0;

    /* REMEMBER EXTRA DESCRIPTIONS */
    clone->carried_by         = 0;
    clone->in_obj             = 0;
    clone->contains           = 0;
    clone->next_content       = 0;
    clone->next               = 0;

    /* VIRKER IKKE ENDNU */
}



/* Check if making CH follow VICTIM will create an illegal */
/* Follow "Loop/circle"                                    */
bool circle_follow(struct char_data *ch, struct char_data *victim)
{
    struct char_data *k;

    for(k=victim; k; k=k->master) {
	if (k == ch)
	    return(TRUE);
    }

    return(FALSE);
}



/* Called when stop following persons, or stopping charm */
/* This will NOT do if a character quits/dies!!          */
void stop_follower(struct char_data *ch)
{
    struct follow_type *j, *k;

    if(!ch->master) {
        log("BUG: No master in stop_follower");
        return;
    }
#if 0
    if (IS_AFFECTED(ch, AFF_CHARM)) {
	act("You realize that $N is a jerk!", FALSE, ch, 0, ch->master, TO_CHAR);
	act("$n realizes that $N is a jerk!", FALSE, ch, 0, ch->master, TO_NOTVICT);
	act("$n hates your guts!", FALSE, ch, 0, ch->master, TO_VICT);
	if (affected_by_spell(ch, SPELL_CHARM_PERSON))
	    affect_from_char(ch, SPELL_CHARM_PERSON);
    } else {
	act("You stop following $N.", FALSE, ch, 0, ch->master, TO_CHAR);
	act("$n stops following $N.", FALSE, ch, 0, ch->master, TO_NOTVICT);
	act("$n stops following you.", FALSE, ch, 0, ch->master, TO_VICT);
    }
#endif

    if (ch->master->followers->follower == ch) { /* Head of follower-list? */
	k = ch->master->followers;
	ch->master->followers = k->next;
	free(k);
    } else { /* locate follower who is not head of list */
	for(k = ch->master->followers; k->next->follower!=ch; k=k->next)  ;

	j = k->next;
	k->next = j->next;
	free(j);
    }

    ch->master = 0;
/*    REMOVE_BIT(ch->specials.affected_by, AFF_CHARM);*/
}



/* Called when a character that follows/is followed dies */
void die_follower(struct char_data *ch)
{
    struct follow_type *j, *k;

    if (ch->master)
	stop_follower(ch);

    for (k=ch->followers; k; k=j) {
	j = k->next;
	stop_follower(k->follower);
    }
}



/* Do NOT call this before having checked if a circle of followers */
/* will arise. CH will follow leader                               */
void add_follower(struct char_data *ch, struct char_data *leader)
{
    struct follow_type *k;

    if(ch->master) {
        log("BUG: Master in add_follower");
        return;
    }

    ch->master = leader;

    CREATE(k, struct follow_type, 1);

    k->follower = ch;
    k->next = leader->followers;
    leader->followers = k;

    act("You now follow $N.", FALSE, ch, 0, leader, TO_CHAR);
    act("$n starts following you.", TRUE, ch, 0, leader, TO_VICT);
    act("$n now follows $N.", TRUE, ch, 0, leader, TO_NOTVICT);
}

void off_light(struct obj_data *obj)
{
    struct obj_info_light *light;

    if(!(light=(struct obj_info_light *)get_obj_info(obj,ITEM_LIGHT))) {
        log("BUG: non-light passed to off_light()");
        return;
    }
    light->brightness=0;
    if(obj->carried_by) {
        obj->carried_by->specials.lights_carried--;
        world[obj->carried_by->in_room].light--;
    } else if(obj->in_room != NOWHERE)
        world[obj->in_room].light--;
}

void on_light(struct obj_data *obj)
{
    struct obj_info_light *light;

    if(!(light=(struct obj_info_light *)get_obj_info(obj,ITEM_LIGHT))) {
        log("BUG: non-light passed to off_light()");
        return;
    }

    light->brightness=1;
    if(obj->carried_by) {
        obj->carried_by->specials.lights_carried++;
        world[obj->carried_by->in_room].light++;
    } else if(obj->in_room != NOWHERE)
        world[obj->in_room].light++;
}

bool can_have_pos(struct char_data *ch,int pos)
{
    /* Naturally, this will be a bit more complicated later */
    return TRUE;
}

int can_wear(struct obj_data *obj,int bit)
{
    struct obj_info_wear *ow;

    if(!(ow=(struct obj_info_wear *)get_obj_info(obj,ITEM_WORN)))
        return 0;

    return(ow->wear_flags & bit);
}

/** These may be subsumed by new functions in force.c sometime **/

void teleport_to(struct char_data *ch,int to_room)
{

    act("$n slowly fades out of existence.", FALSE,ch,0,0,TO_ROOM);
    char_from_room(ch);
    char_to_room(ch, to_room,0);
    act("$n slowly fades into existence.", FALSE,ch,0,0,TO_ROOM);

    do_look(ch, "", 0);
}

/* A routine for ITEM_TELEPORT objects */

bool check_item_teleport(struct char_data *ch,char *arg,int cmd)
{
    struct obj_data *obj=NULL;
    int bits;
    struct obj_info_teleport *tele;

    bits=generic_find(arg,FIND_OBJ_INV|FIND_OBJ_ROOM,ch,(void **)&obj);
    if(!obj)
	return FALSE;

    if(!(tele = (struct obj_info_teleport *)get_obj_info(obj,ITEM_TELEPORT)))
	return FALSE;

    if(cmd!=tele->trigger) /* will be more complicated later */
	return FALSE;

    if(IS_SET(world[ch->in_room].room_flags,NO_MAGIC)) {
	send_to_char("That doesn't seem to do anything!\n\r",ch);
	return FALSE;
    }

    /* Ignore argument for now...eventually we could "throw dust Dbra"..*/
    if(!tele->charges || (IS_SET(world[ch->in_room].room_flags,ARENA)!=IS_SET(world[real_room(tele->to_location)].room_flags,ARENA))) {
	send_to_char("Nothing happens.\n\r\n\r",ch);
	return FALSE;
    }
    act("$p in $n's hands suddenly glows brightly!",FALSE,ch,obj,0,TO_ROOM);
    act("$p in your hands suddenly glows brightly!",FALSE,ch,obj,0,TO_CHAR);
    teleport_to(ch,real_room(tele->to_location));
    if(tele->charges>0) {
	if(!--tele->charges) {
	    act("$p in $n's hands shatters and the pieces disappear in smoke.",TRUE,ch,obj,0,TO_ROOM);
	    act("$p in your hands shatters and the pieces disappear in smoke.",TRUE,ch,obj,0,TO_CHAR);
	    extract_obj(obj);
	}
    }
    return TRUE;
}