/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/* ************************************************************************
*   File: spell_parser.c                                Part of CircleMUD *
*  Usage: command interpreter for 'cast' command (spells)                 *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */
/* spell_parser.c split into two files spell_parser1.c and spell_parser2.c*/
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "interpreter.h" 
#include "spells.h"
#include "handler.h"


void    add_event(int plse, int event, int inf1, int inf2, int inf3
	       , int inf4, char *arg, void *subj, void *vict);
int     spell_lev(struct char_data *caster, int spell);
int     stress_die(void);
int find_spell_num(char *arg);
char *find_spell_name(int spl);
char *rev_search_list(int num, struct list_index_type *list);              
/* Global data */
void damage_obj_invent(struct char_data *vict, struct obj_data *obj, int *spell_type);
void damage_obj_equiped(struct char_data *vict,int where, int *spell_type);
extern struct room_data *world;
extern struct char_data *character_list;
extern char	*spell_wear_off_msg[];
struct spell_info_type spell_info[MAX_SPL_LIST];


void	affect_update( void )
{
   static struct affected_type *af, *next_af_dude;
   static struct char_data *i;

   for (i = character_list; i; i = i->next)
      for (af = i->affected; af; af = next_af_dude) {
	 next_af_dude = af->next;
	 if (af->duration >= 1)
	    af->duration--;
	 else if (af->duration == -1)
	    /* No action */
	    af->duration = -1;  /* GODs only! unlimited */
	 else {
	    if ((af->type > 0) && (af->type <= 60)) /* It must be a spell */
	       if (!af->next || (af->next->type != af->type) || 
	           (af->next->duration > 0))
		  if (*spell_wear_off_msg[af->type]) {
		     send_to_char(spell_wear_off_msg[af->type], i);
		     send_to_char("\r\n", i);
		  }

	    affect_remove(i, af);
	 }
      }
}


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;

   assert(ch->master);

   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);
   }

   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 | AFF_GROUP);
}
void	stop_follower_quiet(struct char_data *ch)
{
   struct follow_type *j, *k;

   assert(ch->master);

   if (affected_by_spell(ch, SPELL_CHARM_PERSON))
       affect_from_char(ch, SPELL_CHARM_PERSON);
   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);

   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 | AFF_GROUP);
}



/* 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                               */
int	add_follower(struct char_data *ch, struct char_data *leader)
{
   struct follow_type *k;
   int nfollow=0;

   assert(!ch->master);

   for (k = leader->followers; k; k = k->next)
     nfollow++;
   
   k =0;
   
   if (nfollow > MAX(1,GET_CHR(leader)/3))
     return(0);
   
   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);
   
   return(1);
}



void	say_spell(struct char_data *ch, int si, int subcmd )
{
   char splwd[MAX_INPUT_LENGTH];
   char *wrd = 0;

   int	j, offs;
   struct char_data *temp_char;


   struct syllable {
      char	org[10];
      char	new[10];
   };

   struct syllable syls[] = {
      { " ", " " },
      { "ar", "ra"   },
      { "au", "mb"    },
      { "ame", "ido" },
      { "ing", "ose" },
      { "ch", "ck" },
      { "cu", "judi" },
      { "de", "oculo" },
      { "en", "unso" },
      { "th", "dh" },
      { "lo", "hi" },
      { "mor", "zak" },
      { "move", "sido" },
      { "ness", "lacri" },
      { "ning", "illa" },
      { "per", "duda" },
      { "ra", "gru"   },
      { "re", "candus" },
      { "son", "sabru" },
      { "tect", "infra" },
      { "tri", "cula" },
      { "ven", "nofo" },
      { "a", "a" }, { "b", "b" }, { "c", "q" }, { "d", "h" }, { "e", "i" },
      { "f", "y" }, { "g", "o" }, { "h", "e" }, { "i", "u" }, { "j", "y" },
      { "k", "t" }, { "l", "r" }, { "m", "w" }, { "n", "i" }, { "o", "a" },
      { "p", "s" }, { "q", "d" }, { "r", "f" }, { "s", "g" }, { "t", "h" },
      { "u", "j" }, { "v", "v" }, { "w", "n" }, { "x", "x" }, { "y", "l" },
      { "z", "k" }, 
      { "A", "A" }, { "B", "B" }, { "C", "Q" }, { "D", "H" }, { "E", "I" },
      { "F", "Y" }, { "G", "O" }, { "H", "E" }, { "I", "U" }, { "J", "Y" },
      { "K", "T" }, { "L", "R" }, { "M", "W" }, { "N", "I" }, { "O", "A" },
      { "P", "S" }, { "Q", "D" }, { "R", "F" }, { "S", "G" }, { "T", "H" },
      { "U", "J" }, { "V", "V" }, { "W", "N" }, { "X", "X" }, { "Y", "L" },
      { "Z", "K" }, { "", "" }
   };   


   strcpy(buf, "");
   wrd = find_spell_name(si);
   strcpy(splwd, (wrd ? wrd: "Ohm Mane Padme Ohm"));
   offs = 0;
   
   while (*(splwd + offs)) {
      for (j = 0; *(syls[j].org); j++)
	 if (strncmp(syls[j].org, splwd + offs, strlen(syls[j].org)) == 0) {
	    strcat(buf, syls[j].new);
	    if (strlen(syls[j].org))
	       offs += strlen(syls[j].org);
	    else
	       ++offs;
	 }
   }
   sprintf(buf2, "$n utters the words, '%s'", buf);
   sprintf(buf, "$n utters the words, '%s'",  wrd);
   for (temp_char = world[ch->in_room].people; 
	temp_char; 
	temp_char = temp_char->next_in_room)
     if (temp_char != ch &&
	 (!IS_NPC(temp_char) && GET_SKILL(temp_char, si))) {
       act(buf, FALSE, ch, 0, temp_char, TO_VICT);
       
     }
     else 
       act(buf2, FALSE, ch, 0, temp_char, TO_VICT);			      
}



bool saves_spell(struct char_data *ch, sh_int save_type, int vlev)
{
   int	save=0,roll;

   /* Negative apply_saving_throw makes saving throw better! */

   save = ch->specials2.apply_saving_throw[save_type];

   switch (save_type)
       {
       case SAVING_SPELL:
	   save += MAX(0,90 - (100*(GET_LEVEL(ch)-vlev))/105);
	   break;
       case SAVING_BREATH:
	   save += MAX(0,100 - GET_LEVEL(ch)+vlev);
	   break;
       case SAVING_PETRI:
	   save += MAX(0,70 - (100*(GET_LEVEL(ch)-vlev))/118);
	   break;
       case SAVING_ROD:
	   save += MAX(0,80 - (100*(GET_LEVEL(ch)-vlev))/111);
	   break;	   
       case SAVING_PARA:
	   save += MAX(0,60 - (100*(GET_LEVEL(ch)-vlev))/125);
	   break;	   
       }
       
      if (GET_LEVEL(ch) >= LEVEL_BUILDER)
	 return(TRUE);
   save *= vlev;
   save /= MAX(1,GET_LEVEL(ch));
   if (GET_LEVEL(ch) < LEVEL_BUILDER)
       roll =  number(MIN(199,GET_LEVEL(ch)), 200);
   else
       roll = 200;
/*   printf("save type: %d\n",save_type);
   printf("save: %d, roll: %d, Target lev: %d, level: %d\n",save, roll,GET_LEVEL(ch),vlev);
   */ 
   
   return(MAX(1, save) < roll);
}



char	*skip_spaces(char *string)
{
   for (; *string && (*string) == ' '; string++)
      ;

   return(string);
}



void spell_damage_equipment(struct char_data *ch, struct char_data *vict, int spell_no, int dam)
{
    int where;
    struct obj_data *dam_obj;
    
/* first loop over item equipped */
    for (where = 0; where < MAX_WEAR ; where++)
	if (vict->equipment[where] && !number(0,6) && (number(0,200) < dam))
	    damage_obj_equiped(vict,where,&spell_no);
    
/* now loop over inventory equipment */
    
    for (dam_obj = vict->inventory; dam_obj ; dam_obj = dam_obj->next_content)
	if (!number(0,6) && (number(0,400) < dam))
	    damage_obj_invent(vict,dam_obj,&spell_no);
}

void damage_obj_equiped(struct char_data *vict,int where, int *spell_type)
{
    struct obj_data *obj;
    
    obj = unequip_char(vict,where);
    
    switch (*spell_type)
	{
	case SPELL_POISON:
	    break;
	case SPELL_FIREBALL:
	case SPELL_FIRE_BREATH:
	case SKILL_IGNEM:
	    act("$p is burned!",FALSE,vict,obj,0,TO_ROOM);
	    if (obj->obj_flags.type_flag == ITEM_SCROLL ||
		obj->obj_flags.type_flag == ITEM_POTION ||
		obj->obj_flags.type_flag == ITEM_NOTE ||
		obj->obj_flags.type_flag == ITEM_WAND ||
		obj->obj_flags.type_flag == ITEM_STAFF)
		obj->obj_flags.value[4] += number(5,12);
	    else
		obj->obj_flags.value[4] += number(0,6);
	    break;
	case SPELL_ACID_BREATH:
	    if (obj->obj_flags.type_flag == ITEM_ARMOR ||
		obj->obj_flags.type_flag == ITEM_WEAPON)
		obj->obj_flags.value[4] += number(0,6);
	    else
		obj->obj_flags.value[4] += number(0,3);
	    act("$p is coroded",FALSE,vict,obj,0,TO_ROOM);
	    break;
	case SPELL_LIGHTNING_BREATH:
	    if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		obj->obj_flags.value[4] += number(1,11);
	    else
		obj->obj_flags.value[4] += number(0,3);
	    act("$p is electrified",FALSE,vict,obj,0,TO_ROOM);
	    break;
	case SPELL_FROST_BREATH:
	    if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		obj->obj_flags.value[4] += number(1,11);
	    else
		obj->obj_flags.value[4] += number(0,3);
	    act("$p is frozen",FALSE,vict,obj,0,TO_ROOM);
	    break;
	default:
	    if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		obj->obj_flags.value[4] += number(0,9);
	    else
		obj->obj_flags.value[4] += number(0,2);
	    act("$p is damaged",FALSE,vict,obj,0,TO_ROOM);
	    break;
	}
    if (obj->obj_flags.value[4] > 10){
	obj_to_room(obj,vict->in_room,FALSE);
	scrap_item(obj);
    }
    else
      equip_char(vict,obj,where);
}
void damage_obj_invent(struct char_data *vict, struct obj_data *obj, int *spell_type)
{
    switch (*spell_type)
	{
	case SPELL_POISON:
	    break;
	case SPELL_FIREBALL:
	case SPELL_FIRE_BREATH:
	case SKILL_IGNEM:
	    act("$p is burned!",FALSE,vict,obj,0,TO_ROOM);
	    if (obj->obj_flags.type_flag == ITEM_SCROLL ||
		obj->obj_flags.type_flag == ITEM_POTION ||
		obj->obj_flags.type_flag == ITEM_NOTE ||
		obj->obj_flags.type_flag == ITEM_WAND ||
		obj->obj_flags.type_flag == ITEM_STAFF)
		obj->obj_flags.value[4] += number(4,11);
	    else
		obj->obj_flags.value[4] += number(0,5);
	    break;
	case SPELL_ACID_BREATH:
	    if (obj->obj_flags.type_flag == ITEM_ARMOR ||
		obj->obj_flags.type_flag == ITEM_WEAPON)
		obj->obj_flags.value[4] += number(0,5);
	    else
		obj->obj_flags.value[4] += number(0,2);
	    act("$p is coroded",FALSE,vict,obj,0,TO_ROOM);
	    break;
	case SPELL_LIGHTNING_BREATH:
	    if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		obj->obj_flags.value[4] += number(0,10);
	    else
		obj->obj_flags.value[4] += number(0,2);
	    act("$p is electrified",FALSE,vict,obj,0,TO_ROOM);
	    break;
	case SPELL_FROST_BREATH:
	    if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		obj->obj_flags.value[4] += number(0,10);
	    else
		obj->obj_flags.value[4] += number(0,2);
	    act("$p is frozen",FALSE,vict,obj,0,TO_ROOM);
	    break;
	default:
	    if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		obj->obj_flags.value[4] += number(0,6);
	    else
		obj->obj_flags.value[4] += number(0,1);
	    act("$p is damaged",FALSE,vict,obj,0,TO_ROOM);
	    break;
	}
    if (obj->obj_flags.value[4] > 10){
	obj_from_char(obj,0);
	obj_to_room(obj,vict->in_room,FALSE);
	scrap_item(obj);
    }

}
void damage_obj_corpse(struct obj_data *corpse, int spell_type, int dam)
{
    struct obj_data *obj, *obj_next;
    for (obj = corpse->contains;obj_next;obj = obj_next){
	obj_next = obj->next_content;
	obj_from_obj(obj);
	switch (spell_type)
	    {
	    case SPELL_POISON:
		break;
	    case SPELL_FIREBALL:
	    case SPELL_FIRE_BREATH:
	    case SKILL_IGNEM:
		act("$p is burned!",FALSE,0,obj,0,TO_ROOM);
		if (obj->obj_flags.type_flag == ITEM_SCROLL ||
		    obj->obj_flags.type_flag == ITEM_POTION ||
		    obj->obj_flags.type_flag == ITEM_NOTE ||
		    obj->obj_flags.type_flag == ITEM_WAND ||
		    obj->obj_flags.type_flag == ITEM_STAFF)
		    obj->obj_flags.value[4] += number(4,11);
		else
		    obj->obj_flags.value[4] += number(0,5);
		break;
	    case SPELL_ACID_BREATH:
		act("$p is coroded!",FALSE,0,obj,0,TO_ROOM);
		if (obj->obj_flags.type_flag == ITEM_ARMOR ||
		    obj->obj_flags.type_flag == ITEM_WEAPON)
		    obj->obj_flags.value[4] += number(0,5);
		else
		    obj->obj_flags.value[4] += number(0,2);
		break;
	    case SPELL_LIGHTNING_BREATH:
		act("$p is electrified!",FALSE,0,obj,0,TO_ROOM);
		if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		    obj->obj_flags.value[4] += number(0,10);
		else
		    obj->obj_flags.value[4] += number(0,2);
		break;
	    case SPELL_FROST_BREATH:
		act("$p is frozen!",FALSE,0,obj,0,TO_ROOM);
		if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		    obj->obj_flags.value[4] += number(0,10);
		else
		    obj->obj_flags.value[4] += number(0,2);
		break;
	    default:
		act("$p is damaged!",FALSE,0,obj,0,TO_ROOM);		
		if (IS_SET(obj->obj_flags.extra_flags,ITEM_FRAGILE))
		    obj->obj_flags.value[4] += number(0,6);
		else
		    obj->obj_flags.value[4] += number(0,1);
		break;
	    }
	
	if (obj->obj_flags.value[4] > 10){
	    obj_to_room(obj,corpse->in_room, FALSE);
	    scrap_item(obj);
	}
	obj_to_obj(obj,corpse);
	
    }

}

bool aff_by_spell(struct char_data *k, int spell)
{
    struct affected_type *af;

    if (k->affected)
	for (af = k->affected;af;af = af->next)
	    if (af->type == spell)
		return(TRUE);

    return(FALSE);
		  

}

int spell_lev(struct char_data *k, int spell)
{
    int level;

    level = spell_info[spell].min_level;
    level *= calc_difficulty(k, spell);
    return level;
}