/* ************************************************************************
* 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;
}