/*
 * static char *rcsid_rune_c =
 *   "$Id: rune.c,v 1.17 1996/12/01 07:59:53 master Exp $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1994 Mark Wedel
    Copyright (C) 1992 Frank Tore Johansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to master@rahul.net
*/

#include <global.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <spells.h>
#include <errno.h>
#ifdef sequent
/* stoopid sequent includes don't do this like they should */
extern char * sys_errlist[];
extern int sys_nerr;
#endif

extern object *objects;

/*  peterm:  
    write_rune:
    op:  rune writer
    dir:  orientation of rune, direction rune's contained spell will
	    be cast in, if applicable
    inspell:  the spell index of the spell in the rune (if any)
    level:  level of casting of the rune
    runename:  name of the rune or message displayed by the rune for
		a rune of marking 

*/

int write_rune(object *op,int dir,int inspell,int level,char *runename) { 
    object *tmp;
    archetype *at=NULL;
    char *buf=(char *)malloc(sizeof(char) * 40);
    
    int nx,ny;

      if(!dir) {
	dir=1;
    } 

    nx=op->x+freearr_x[dir];
    ny=op->y+freearr_y[dir];
    if(blocked(op->map,nx,ny)) {
	new_draw_info(NDI_UNIQUE, 0,op,"Can't make a rune there!");
	return 0;
    }
    for(tmp=get_map_ob(op->map,nx,ny);tmp!=NULL;tmp=tmp->above)
	if(tmp->type==RUNE) break;
    if(tmp){
#if 0
	new_draw_info(NDI_UNIQUE, 0,op,"You only succeed in strengthening that rune.");
	tmp->stats.hp++;
	return 1;
#endif
      new_draw_info(NDI_UNIQUE, 0,op,"You can't write a rune there.");
      return 0;
    }
    if(inspell) {  /* can't have runes of small fireball!!!  */
	if(inspell==-1) { new_draw_info(NDI_UNIQUE, 0,op,"You can't make a rune containing");
			  new_draw_info(NDI_UNIQUE, 0,op,"a spell you don't know.  (idiot!)");
			  return 0;
	}
	at=find_archetype(runename);

	/* What it compares to should probably be expanded.  But basically,
	 * creating a rune of sword should not be allowed. */
	if (at && at->clone.type!=RUNE) {
	     new_draw_info_format(NDI_UNIQUE, 0, op,
		"You can't make a rune of %s", runename);
		return 0;
	}
        /* next it attempts to look up a rune_archetype for this spell
		by doing some string manipulations */
	if(!at) {
		char buf[MAX_BUF];
		char insp[MAX_BUF];
		int i;
		strcpy(insp,spells[inspell].name);
		for(i=0;i<strlen(insp);i++)
			if(insp[i]==' ') insp[i]='_';
		sprintf(buf,"%s%s","rune_",insp);
		at=find_archetype(buf);
	}
		
	if(!at) tmp=get_archetype("generic_rune");
	else
	tmp=arch_to_object(at);

	tmp->stats.sp=inspell; /* the spell it contains */

	sprintf(buf,"You set off a rune of %s",spells[inspell].name);
	tmp->msg=add_string(buf); 
	at=NULL;

  /* the only circumstance in which we remove buf, otherwise we need it.*/
    } else if (level!=-2 && (at=find_archetype(runename))!=NULL) free(buf);
	    /*  the at=find_archetye(runename) was neccessary because
		tmp=get_archetype returns a singulirity, not a null,
		when it cannot find the archetype.  */
		    /* note: if some smartass
		    cast rune of marking, and gives the exact name
		    of a powerful rune, it won't do him any good,
		    because a rune of marking will have level 0
		    and will thus never detonate. */
      else { /* it's a rune of marking */
	level=0;
	tmp=get_archetype("rune_mark"); /* this is a rune of marking */
	at=NULL;
	tmp->msg = add_string((runename?runename:"There is no message"));
      }
    if(at) tmp=get_archetype(runename);
    tmp->stats.Cha = op->level/2;  /* the invisibility parameter */
    tmp->x=nx;
    tmp->y=ny;
    tmp->map = op->map;
    tmp->direction=dir;  /* where any spell will go upon detonation */
    tmp->level=SK_level(op);  /* what level to cast the spell at */
    set_owner(tmp,op);
    insert_ob_in_map(tmp,op->map);
    return 1;

}


/*  move_rune:  peterm
  comments on runes:
    rune->level	    :	    level at which rune will cast its spell.
    rune->hp	    :	    number of detonations before rune goes away
    rune->sp	    :	    index of the spell the rune casts
    rune->msg	    :	    message the rune displays when it goes off
    rune->direction :	    direction it will cast a spell in
    rune->dam	    :	    damage the rune will do if it doesn't cast spells
    rune->attacktype:	    type of damage it does, if not casting spells
    rune->slaying   :       string description of the spell in the rune

*/

void move_rune(object *op) {
    object *tmp;
    int det=0;
    if(!op->level) {return;}  /* runes of level zero cannot detonate. */
    for(tmp=op->above;tmp;tmp=tmp->above)
	if(!QUERY_FLAG(tmp,FLAG_FLYING)&&QUERY_FLAG(tmp,FLAG_ALIVE))
		{det|=1;break;}
    if(det) spring_trap(op,tmp);

    det=op->invisible;
    if(!(RANDOM()%(MAX(1,(op->stats.Cha)))))
	{
	op->invisible=0;
	op->speed_left-=1;
	}
    else
	op->invisible=1;
    if(op->invisible!=det)
    update_object(op);
}


/*  peterm: rune_attack

  function handles those runes which detonate but do not cast spells.  */


void rune_attack(object *op,object *victim)
{
  if(victim)  hit_player(victim,op->stats.dam,op,op->attacktype);
   else  hit_map(op,0,op->attacktype);
}

/*  This function generalizes attacks by runes/traps.  This ought to make
    it possible for runes to attack from the inventory, 
    it'll spring the trap on the victim.  */
   
void spring_trap(object *trap,object *victim)
{  int spell_in_rune;
   object *env;
  trap->stats.hp--;  /*decrement detcount */
  /*  get the spell number from the name in the slaying field, and set
      that as the spell to be cast. */
  if((spell_in_rune=look_up_spell_by_name(NULL,trap->slaying))!=-1) trap->stats.sp=spell_in_rune;
  if(victim) if(victim->type==PLAYER) new_draw_info(NDI_UNIQUE, 0,victim,trap->msg);
  if(!trap->stats.sp) rune_attack(trap,victim); 
  else {  /*  This is necessary if the trap is inside something else */
    remove_ob(trap);
    trap->x=victim->x;trap->y=victim->y;
    insert_ob_in_map(trap,victim->map);
    cast_spell(trap,trap,trap->direction,trap->stats.sp,1,spellNormal,NULL);
  }
  /*  Flash an image of the trap on the map so the poor sod
   *   knows what hit him.  */
  for(env=trap;env!=NULL&&env->env!=NULL;env=env->env);
  trap_show(trap,env);  

  if(!trap->stats.hp) {
    trap->type=98;  /* make the trap impotent */
    trap->stats.food=20;  /* make it stick around until its spells are gone */
    SET_FLAG(trap,FLAG_IS_USED_UP);
  }
}

/*  dispel_rune:  by peterm  
    dispels the target rune, depending on the level of the actor
and the level of the rune  risk flag, if true, means that there is
a chance that the trap/rune will detonate */

int dispel_rune(object *op,int dir,int risk)
{
  object *tmp,*tmp2;
  int searchflag = 1;
   if(out_of_map(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) return 0;
   for(tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);
        tmp!=NULL;  tmp=tmp->above) 
	{
	  if(tmp->type==RUNE) break;
		/* now search tmp's inventory for traps */
	  for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below) {
		if(tmp2->type==RUNE) { 
			tmp=tmp2;
			searchflag=0;
			break;
		}
	  }
	  if(!searchflag) break;
	}
		
  if(tmp==NULL)               /*no rune there. */
    {
	new_draw_info(NDI_UNIQUE, 0,op,"There's no trap there!");
	return 0;
    }
  trap_disarm(op,tmp,risk);
  return 1;
	
}

int trap_see(object *op,object *trap) {
  char buf[MAX_BUF];
  int chance;
#ifndef ALLOW_SKILLS 
  int trapworth=0;  
#endif

  chance = RANDOM()%100;
  
  /*  decide if we see the rune or not */
  if((trap->stats.Cha==1) || (chance >
        MIN(95,MAX(5,((int)((float) (op->map->difficulty 
	+ trap->level + trap->stats.Cha-op->level)/10.0 * 50.0))))))
  {
      sprintf(buf,"You spot a %s!",trap->name);
      new_draw_info(NDI_UNIQUE, 0,op,buf);

#ifndef ALLOW_SKILLS /* this stuff is handled by find_trap skill code -b.t. */ 
      trapworth = trap->stats.Cha * trap->level;
      if(trap->stats.Cha>1) add_exp(op,trapworth);  /* finding a trap awards exp */
      trap->stats.Cha=1;  /* unhide the rune/trap */
#endif
      return 1;
  }
  return 0;
}

int trap_show(object *trap, object *where) {
  object *tmp2;

  if(where==NULL) return 0;
  tmp2=get_archetype("runedet");
  tmp2->face=&new_faces[trap->arch->faces[0]];
  tmp2->x=where->x;tmp2->y=where->y;tmp2->map=where->map;
  insert_ob_in_map(tmp2,where->map);
  return 1;

}

#ifndef sqr
#define sqr(x) ((x)*(x))
#endif
int trap_disarm(object *disarmer, object *trap, int risk) {

  int trapworth;  /* need to compute the experience worth of the trap
                     before we kill it */
  /* this formula awards a more reasonable amount of exp */
  trapworth =  MAX(1,trap->level)  * disarmer->map->difficulty *
	sqr(MAX(trap->stats.dam,spells[trap->stats.sp].sp)) /
	disarmer->level;

    if(!(RANDOM()%(MAX(2,
		       MIN(20,trap->level-disarmer->level 
			   +5 - disarmer->stats.Dex/2)))))
        {
            new_draw_info(NDI_UNIQUE, 0,disarmer,"You successfuly disarm it!");
            remove_ob(trap);
            free_object(trap);
	    /* If it is your own trap, (or any players trap), don't you don't
	     * get exp for it.
	     */
#ifdef ALLOW_SKILLS 
	    if (trap->owner && trap->owner->type!=PLAYER && risk)
		return trapworth;
	    else return 1; /* give minimal exp and say success */
#else
	    if(!trap->owner) add_exp(disarmer,trapworth);
            else if(trap->owner && trap->owner->type!=PLAYER && risk)
	      add_exp(disarmer,trapworth);
	    return 1;
#endif
        }
    else
        {
            new_draw_info(NDI_UNIQUE, 0,disarmer,"You fail to disarm the trap.");
	    if(! (RANDOM()% (MAX(2,disarmer->level-trap->level 
				 + disarmer->stats.Dex/2-6)))
	       &&risk) {
		new_draw_info(NDI_UNIQUE, 0,disarmer,"In fact, you set it off!");
		spring_trap(trap,disarmer);
	    }
            return 0;
        }
}


/*  traps need to be adjusted for the difficulty of the map.  The
default traps are too strong for wimpy level 1 players, and 
unthreatening to anyone of high level */

void trap_adjust(object *trap, int difficulty)
{ int i;
  /*  first we set the sp value of the trap if it has a spell in it. */
  if(trap->slaying) {
	trap->stats.sp = look_up_spell_name(trap->slaying);
	trap->stats.dam = 0;
  }
  if(trap->stats.sp ==-1) trap->stats.sp = 0;

  /* now we set the trap level to match the difficulty of the level */
  /* the formula below will give a level from 1 to (2*difficulty) with */
  /* a peak probability at difficulty */

  trap->level = MAX(1,RANDOM()%difficulty + RANDOM()%difficulty);

  /* set the hiddenness of the trap, similar formula to above */
  trap->stats.Cha = RANDOM()%20 + RANDOM()%difficulty + RANDOM()%difficulty;

  /* set the damage of the trap if it's not a spellcasting trap 
	we get 0-4 pts of damage per level of difficulty of the map in
        the trap*/

  if(trap->stats.sp == 0) {
	trap->stats.dam = 0;
	for(i=0;i<difficulty;i++) trap->stats.dam+=RANDOM()%5;
  }
    

  /*  the poison trap special case */
  if(trap->attacktype & AT_POISON) trap->stats.dam = MAX(1,RANDOM()%difficulty);  

  /*  so we get an appropriate amnt of exp for AT_DEATH traps */
  if(trap->attacktype & AT_DEATH) trap->stats.dam = 127;

}
