/* ************************************************************************
* File: spell_parser.c Part of CircleMUD *
* Usage: top-level magic routines; outside points of entry to magic sys. *
* *
* All rights reserved. See license.doc for complete information. *
* *
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
************************************************************************ */
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "spells.h"
#include "handler.h"
#include "comm.h"
#include "db.h"
struct spell_info_type spell_info[TOP_SPELL_DEFINE + 1];
#define SINFO spell_info[spellnum]
extern struct room_data *world;
/* local functions */
void say_spell(struct char_data * ch, int spellnum, struct char_data * tch, struct obj_data * tobj);
void spello(int spl, int max_mana, int min_mana, int mana_change, int minpos, int targets, int violent, int routines);
int mag_manacost(struct char_data * ch, int spellnum);
ACMD(do_focus);
void unused_spell(int spl);
void mag_assign_spells(void);
/*
* This arrangement is pretty stupid, but the number of skills is limited by
* the playerfile. We can arbitrarily increase the number of skills by
* increasing the space in the playerfile. Meanwhile, this should provide
* ample slots for skills.
*/
typedef struct spell_entry spell_entry;
const spell_entry spells[] =
{
{"!RESERVED!", 0, 0, 0}, /* 0 - reserved */
/* SPELLS */
{"!UNUSED!", 0, 0, 0}, /* 1 */
{"Teleport", CLASS_TRUFFLE_B, 70000, 0},
{"!UNUSED!", 0, 0, 0},
{"solar flare", CLASS_HUMAN_B | CLASS_SAIYAN_B | CLASS_BIODROID_B, 225, 3},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"majin", CLASS_MAJIN_B, 700, 3},
{"!UNUSED!", 0, 0, 0},
{"multiform", CLASS_HUMAN_B | CLASS_NAMEK_B, 100, 3},
{"!UNUSED!", 0, 0, 0}, /* 10 */
{"control weather", 0, 1, 3},
{"create food", 0, 1, 3},
{"create water", 0, 1, 3},
{"remove blindness", CLASS_KAI_B, 225, 4},
{"cure critic", 0, 1, 4},
{"hasshuken", CLASS_SAIYAN_B | CLASS_HALF_BREED_B, 70, 2},
{"curse", CLASS_demon_B, 100, 3},
{"!UNUSED!", 0, 0, 0},
{"Super Sense", CLASS_HUMAN_B | CLASS_SAIYAN_B | CLASS_HALF_BREED | CLASS_NAMEK_B | CLASS_KAI_B, 100, 2},
{"!UNUSED!", 0, 0, 0}, /* 20 */
{"!UNUSED!", _KAI_B, 100, 2},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"saibamen", 0, 1, 0},
{"!UNUSED!", 0, 0, 0},
{"heal", CLASS_NAMEK_B | CLASS_KAI_B | CLASS_MAJIN_B, 38, 4},
{"invisibility", CLASS_KONATSU_B, 300, 3},
{"!UNUSED!", 0, 0, 0}, /* 30 */
{"locate object", 0, 1, 3},
{"!UNUSED!", 0, 0, 0},
{"poison", CLASS_MUTANT_B, 1, 0},
{"protection", 0, 1, 1},
{"remove curse", CLASS_KAI_B, 1, 3},
{"barrier", CLASS_HUMAN_B | CLASS_HALF_BREED_B | CLASS_ANDROID_B | CLASS_SAIYAN_B | CLASS_NAMEK_B | CLASS_ICER_B | CLASS_ANDROID_B | CLASS_MAJIN_B, 300, 1},
{"!UNUSED!", 0, 0, 0},
{"yoikominminken", CLASS_HUMAN_B, 200, 3},
{"senzu bean", CLASS_NAMEK_B, 2000, 0},
{"summon", CLASS_HUMAN_B | CLASS_HALF_BREED_B | CLASS_SAIYAN_B | CLASS_ANDROID_B | CLASS_NAMEK_B | CLASS_KAI_B | CLASS_demon_B | CLASS_MUTANT_B | CLASS_ICER_B | CLASS_MAJIN_B | CLASS_BIODROID_B, 70000, 3}, /* 40 */
{"ventriloquate", 0, 1, 3},
{"word of recall", 0, 1, 3},
{"remove poison", CLASS_KAI_B, 100, 4},
{"detect life", 0, 1, 2},
{"animate dead", 0, 1, 3},
{"dispel good", 0, 1, 3},
{"group ki shield", CLASS_TRUFFLE_B, 200, 1},
{"group heal", CLASS_MUTANT_B | CLASS_TRUFFLE_B | CLASS_MAJIN_B, 400, 4},
{"group recall", 0, 1, 3},
{"infravision", CLASS_ICER_B | CLASS_BIODROID_B | CLASS_KONATSU_B | CLASS_ANDROID_B, 35, 2}, /* 50 */
{"waterwalk", 0, 1, 2}, /* 50 */
{"mind freeze", CLASS_KAI_B | CLASS_TRUFFLE_B, 600, 3},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"kino tsurugi", CLASS_SAIYAN_B, 100, 2},
{"reproduce", CLASS_NAMEK_B, 150, 3}, /* 60 */
{"kyodaika", CLASS_NAMEK_B, 100, 2},
{"blademorph", CLASS_CHANGELING_B, 32, 2},
{"mutate skin", CLASS_MUTANT_B, 200, 2},
{"form sword", CLASS_KONATSU_B, 50, 3},
{"!UNUSED!", 0, 0, 0},/* 64 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 69 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 74 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 79 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 84 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 89 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 94 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 99 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 104 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 109 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 114 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 119 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 124 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0}, /* 129 */
/* SKILLS */
{"backstab", CLASS_KONATSU_B, 300, 0}, /* 130 */
{"sweep", CLASS_ALL_B, 100, 0},
{"hide", 0, 0, 1},
{"zanzokenelbow", CLASS_ALL_B, 100, 0},
{"pick lock", 0, 0, 3},
{"!UNUSED!", 0, 0, 0},
{"rescue", CLASS_ALL_B, 65, 3},
{"swiftness", CLASS_HUMAN_B | CLASS_SAIYAN_B | CLASS_NAMEK_B | CLASS_KONATSU_B, 100, 1},
{"Mug", CLASS_KONATSU_B, 7500, 3},
{"sense", CLASS_HUMAN_B | CLASS_HALF_BREED_B | CLASS_KONATSU_B | CLASS_BIODROID_B | CLASS_KAI_B | CLASS_TRUFFLE_B, 30, 3}, /* 140 */
{"tailwhip", CLASS_ICER_B, 200, 0},
{"hellsflash", CLASS_ANDROID_B, 750, 0},
{"repair", CLASS_ANDROID_B, 100, 4},
{"kaioken", CLASS_SAIYAN_B | CLASS_KAI_B, 200, 2},
{"knee", CLASS_ALL_B, 25, 0},/* 144 */
{"elbow", CLASS_ALL_B, 25, 0},
{"roundhouse", CLASS_ALL_B, 25, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"ingest", CLASS_MAJIN_B, 300, 3},
{"!UNUSED!", 0, 0, 0},
{"scan", CLASS_ALL_B, 0, 3}, /* 51 */
{"absorb", CLASS_ANDROID_B | CLASS_BIODROID_B, 300, 3},
{"selfdestruct", CLASS_ANDROID_B | CLASS_BIODROID_B | CLASS_HUMAN_B | CLASS_TRUFFLE_B, 100, 3},/* 150 */
{"powersense", CLASS_ALL_B, 50, 3}, /* 155 */
{"stealth", CLASS_KONATSU_B, 50, 3},
{"regenerate", CLASS_TRUFFLE_B | CLASS_MAJIN_B | CLASS_NAMEK_B | CLASS_BIODROID_B, 350, 4},
{"kamehameha", CLASS_BIODROID_B | CLASS_HALF_BREED_B | CLASS_SAIYAN_B | CLASS_HUMAN_B, 100, 0},
{"big bang", CLASS_SAIYAN_B, 300, 0},
{"final flash", CLASS_SAIYAN_B, 500, 0}, /* 160 */
{"choukamehameha", CLASS_BIODROID_B | CLASS_HALF_BREED_B | CLASS_SAIYAN_B, 1000, 0},
{"kienzan", CLASS_ANDROID_B | CLASS_ICER_B | CLASS_HUMAN_B, 500, 0},
{"masenko", CLASS_HALF_BREED_B, 250, 0},
{"renzokou energy dan", CLASS_ALL_B, 400, 0},
{"makankosappo", CLASS_NAMEK_B, 450, 0},/* 165 */
{"kishot", CLASS_ALL_B, 45, 0},
{"kikoho", CLASS_HUMAN_B | CLASS_HALF_BREED_B, 300, 0},
{"death ball", CLASS_ICER_B, 2000, 0},
{"kousengan", CLASS_ICER_B | CLASS_NAMEK_B, 175, 0},
{"hellsflash", CLASS_ANDROID_B, 750, 0},
{"Combatyoik", CLASS_HUMAN_B, 600, 0},
{"burning attack", CLASS_HALF_BREED_B, 2000, 0}, /* 172 */
{"Spirit Ball", CLASS_HUMAN_B, 2000, 0},
{"DanceofSevenDragons", CLASS_HALF_BREED_B | CLASS_SAIYAN_B, 5000, 0},/* 175 */
{"Twin Blade Slash", CLASS_KONATSU_B, 1500, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 180 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 185 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 190 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},/* 195 */
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0},
{"!UNUSED!", 0, 0, 0}, /* 200 */
/* OBJECT SPELLS AND NPC SPELLS/SKILLS */
{"identify", 0, 0, 0}, /* 201 */
{"scouter", 0, 0, 0},
{"scout", 0, 0, 0},
{"frost breath", 0, 0, 0},
{"acid breath", 0, 0, 0},
{"lightning breath", 0, 0, 0},
{"\n", 0, 0, 0} /* the end */
};
struct syllable {
const char *org;
const char *news;
};
struct syllable syls[] = {
{" ", " "},
{"ar", "abra"},
{"ate", "i"},
{"cau", "kada"},
{"SOLAR_FLARE", "nose"},
{"bur", "mosa"},
{"cu", "judi"},
{"de", "oculo"},
{"dis", "mar"},
{"ect", "kamina"},
{"en", "uns"},
{"gro", "cra"},
{"light", "dies"},
{"lo", "hi"},
{"magi", "kari"},
{"mon", "bar"},
{"mor", "zak"},
{"move", "sido"},
{"ness", "lacri"},
{"ning", "illa"},
{"per", "duda"},
{"ra", "gru"},
{"re", "candus"},
{"son", "sabru"},
{"tect", "infra"},
{"tri", "cula"},
{"ven", "nofo"},
{"word of", "inset"},
{"a", "i"}, {"b", "v"}, {"c", "q"}, {"d", "m"}, {"e", "o"}, {"f", "y"}, {"g", "t"},
{"h", "p"}, {"i", "u"}, {"j", "y"}, {"k", "t"}, {"l", "r"}, {"m", "w"}, {"n", "b"},
{"o", "a"}, {"p", "s"}, {"q", "d"}, {"r", "f"}, {"s", "g"}, {"t", "h"}, {"u", "e"},
{"v", "z"}, {"w", "x"}, {"x", "n"}, {"y", "l"}, {"z", "k"}, {"", ""}
};
int mag_manacost(struct char_data * ch, int spellnum)
{
int mana;
mana = MAX(SINFO.mana_max - (SINFO.mana_change *
(GET_LEVEL(ch) - SINFO.min_level[(int) GET_CLASS(ch)])),
SINFO.mana_min);
return mana;
}
/* say_spell erodes buf, buf1, buf2 */
void say_spell(struct char_data * ch, int spellnum, struct char_data * tch,
struct obj_data * tobj)
{
char lbuf[256];
struct char_data *i;
int j, ofs = 0;
*buf = '\0';
strcpy(lbuf, spells[spellnum].name);
while (*(lbuf + ofs)) {
for (j = 0; *(syls[j].org); j++) {
if (!strncmp(syls[j].org, lbuf + ofs, strlen(syls[j].org))) {
strcat(buf, syls[j].news);
ofs += strlen(syls[j].org);
}
}
}
if (tch != NULL && tch->in_room == ch->in_room) {
if (tch == ch)
sprintf(lbuf, "$n concentrates and gathers $s energy.");
else
sprintf(lbuf, "$n gathers energy and stares at $N.");
} else if (tobj != NULL &&
((tobj->in_room == ch->in_room) || (tobj->carried_by == ch)))
sprintf(lbuf, "$n gathers their Ki as they stare at $p.");
else
sprintf(lbuf, "$n concentrates and gathers $s energy.");
sprintf(buf1, lbuf, spells[spellnum]);
sprintf(buf2, lbuf, buf);
for (i = world[ch->in_room].people; i; i = i->next_in_room) {
if (i == ch || i == tch || !i->desc || !AWAKE(i))
continue;
if (GET_CLASS(ch) == GET_CLASS(i))
perform_act(buf1, ch, tobj, tch, i);
else
perform_act(buf2, ch, tobj, tch, i);
}
if (tch != NULL && tch != ch && tch->in_room == ch->in_room) {
sprintf(buf1, "$n stares at you and starts gathering energy.");
act(buf1, FALSE, ch, NULL, tch, TO_VICT);
}
}
const char *skill_name(int num)
{
int i = 0;
if (num <= 0) {
if (num == -1)
return "UNUSED";
else
return "UNDEFINED";
}
while (num && spells[i].name[0] != '\n') {
num--;
i++;
}
if (spells[i].name[0] != '\n')
return spells[i].name;
else
return "UNDEFINED";
}
int find_skill_num(char *name)
{
int index = 0, ok;
char *temp, *temp2;
char first[256], first2[256];
while (spells[++index].name[0] != '\n') {
if (is_abbrev(name, spells[index].name))
return index;
ok = 1;
/* It won't be changed, but other uses of this function elsewhere may. */
temp = any_one_arg((char*)(spells[index].name), first);
temp2 = any_one_arg(name, first2);
while (*first && *first2 && ok) {
if (!is_abbrev(first2, first))
ok = 0;
temp = any_one_arg(temp, first);
temp2 = any_one_arg(temp2, first2);
}
if (ok && !*first2)
return index;
}
return -1;
}
/*
* This function is the very heart of the entire magic system. All
* invocations of all types of magic -- objects, spoken and unspoken PC
* and NPC spells, the works -- all come through this function eventually.
* This is also the entry point for non-spoken or unrestricted spells.
* Spellnum 0 is legal but silently ignored here, to make callers simpler.
*/
int call_magic(struct char_data * focuser, struct char_data * cvict,
struct obj_data * ovict, int spellnum, int level, int focustype)
{
int savetype;
if (spellnum < 1 || spellnum > TOP_SPELL_DEFINE)
return 0;
if (ROOM_FLAGGED(focuser->in_room, ROOM_NOMAGIC)) {
send_to_char("Your ki fizzles out and dies.\r\n", focuser);
act("$n's energy fizzles out and dies.", FALSE, focuser, 0, 0, TO_ROOM);
return 0;
}
if (ROOM_FLAGGED(focuser->in_room, ROOM_PEACEFUL) &&
(SINFO.violent || IS_SET(SINFO.routines, MAG_DAMAGE))) {
send_to_char("A flash of white light fills the room, dispelling your "
"violent energy!\r\n", focuser);
act("White light from no particular source suddenly fills the room, "
"then vanishes.", FALSE, focuser, 0, 0, TO_ROOM);
return 0;
}
/* determine the type of saving throw */
switch (focustype) {
case focus_STAFF:
case focus_SENZU:
case focus_SAIBAKIT:
case focus_SCOUTER:
savetype = SAVING_ROD;
break;
case focus_SPELL:
savetype = SAVING_SPELL;
break;
default:
savetype = SAVING_BREATH;
break;
}
/*
* Hm, target could die here. Wonder if we should move this down lower to
* give the other spells a chance to go off first? -gg 6/24/98
*/
if (IS_SET(SINFO.routines, MAG_DAMAGE))
if (mag_damage(level, focuser, cvict, spellnum, savetype) == -1)
return 1;
if (IS_SET(SINFO.routines, MAG_AFFECTS))
mag_affects(level, focuser, cvict, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_UNAFFECTS))
mag_unaffects(level, focuser, cvict, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_POINTS))
mag_points(level, focuser, cvict, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_ALTER_OBJS))
mag_alter_objs(level, focuser, ovict, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_GROUPS))
mag_groups(level, focuser, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_MASSES))
mag_masses(level, focuser, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_AREAS))
mag_areas(level, focuser, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_SUMMONS))
mag_summons(level, focuser, ovict, spellnum, savetype);
if (IS_SET(SINFO.routines, MAG_CREATIONS))
mag_creations(level, focuser, spellnum);
if (IS_SET(SINFO.routines, MAG_MANUAL))
switch (spellnum) {
case SPELL_MAJIN: MANUAL_SPELL(spell_MAJIN); break;
case SPELL_CREATE_WATER: MANUAL_SPELL(spell_create_water); break;
case SPELL_BURNING_SOULS: MANUAL_SPELL(spell_BURNING_SOULS); break;
case SPELL_IDENTIFY: MANUAL_SPELL(spell_IDENTIFY); break;
case SPELL_LOCATE_OBJECT: MANUAL_SPELL(spell_locate_object); break;
case SPELL_SUMMON: MANUAL_SPELL(spell_summon); break;
case SPELL_WORD_OF_RECALL: MANUAL_SPELL(spell_recall); break;
case SPELL_TELEPORT: MANUAL_SPELL(spell_teleport); break;
case SPELL_POWER_SENSE: MANUAL_SPELL(spell_POWER_SENSE); break;
}
return 1;
}
/*
* mag_objectmagic: This is the entry-point for all magic items. This should
* only be called by the 'PLANT', 'use', 'SWALLOW', etc. routines.
*
* For reference, object values 0-3:
* STAFF - [0] level [1] max charges [2] num charges [3] spell num
* SCOUTER - [0] level [1] max charges [2] num charges [3] spell num
* SENZU - [0] level [1] spell num [2] spell num [3] spell num
* SAIBAKIT - [0] level [1] spell num [2] spell num [3] spell num
*
* Staves and SCOUTERs will default to level 14 if the level is not specified;
* the DikuMUD format did not specify STAFF and SCOUTER levels in the world
* files (this is a CircleMUD enhancement).
*/
void mag_objectmagic(struct char_data * ch, struct obj_data * obj,
char *argument)
{
int i, k;
struct char_data *tch = NULL, *next_tch;
struct obj_data *tobj = NULL;
one_argument(argument, arg);
k = generic_find(arg, FIND_CHAR_ROOM | FIND_OBJ_INV | FIND_OBJ_ROOM |
FIND_OBJ_EQUIP, ch, &tch, &tobj);
switch (GET_OBJ_TYPE(obj)) {
case ITEM_STAFF:
act("You begin using $p.", FALSE, ch, obj, 0, TO_CHAR);
if (obj->action_description)
act(obj->action_description, FALSE, ch, obj, 0, TO_ROOM);
else
act("$n uses $p.", FALSE, ch, obj, 0, TO_ROOM);
if (GET_OBJ_VAL(obj, 2) <= 0) {
act("It seems powerless.", FALSE, ch, obj, 0, TO_CHAR);
act("Nothing seems to happen.", FALSE, ch, obj, 0, TO_ROOM);
} else {
GET_OBJ_VAL(obj, 2)--;
WAIT_STATE(ch, PULSE_VIOLENCE);
for (tch = world[ch->in_room].people; tch; tch = next_tch) {
next_tch = tch->next_in_room;
if (ch == tch)
continue;
if (GET_OBJ_VAL(obj, 0))
call_magic(ch, tch, NULL, GET_OBJ_VAL(obj, 3),
GET_OBJ_VAL(obj, 0), focus_STAFF);
else
call_magic(ch, tch, NULL, GET_OBJ_VAL(obj, 3),
DEFAULT_STAFF_LVL, focus_STAFF);
}
}
break;
case ITEM_SCOUTER:
if (k == FIND_CHAR_ROOM) {
if (tch == ch) {
act("You point $p at yourself.", FALSE, ch, obj, 0, TO_CHAR);
act("$n points $p at $mself.", FALSE, ch, obj, 0, TO_ROOM);
} else {
act("You point $p at $N and press a button.", FALSE, ch, obj, tch, TO_CHAR);
if (obj->action_description != NULL)
act(obj->action_description, FALSE, ch, obj, tch, TO_ROOM);
else
act("$n points $p at $N and presses a button.", TRUE, ch, obj, tch, TO_ROOM);
}
} else if (tobj != NULL) {
act("You point $p at $P and press a button.", FALSE, ch, obj, tobj, TO_CHAR);
if (obj->action_description != NULL)
act(obj->action_description, FALSE, ch, obj, tobj, TO_ROOM);
else
act("$n points $p at $P and presses a button.", TRUE, ch, obj, tobj, TO_ROOM);
} else {
act("At what should $p be pointed?", FALSE, ch, obj, NULL, TO_CHAR);
return;
}
if (GET_OBJ_VAL(obj, 2) <= 0) {
act("It seems powerless.", FALSE, ch, obj, 0, TO_CHAR);
act("Nothing seems to happen.", FALSE, ch, obj, 0, TO_ROOM);
return;
}
GET_OBJ_VAL(obj, 2)--;
WAIT_STATE(ch, PULSE_VIOLENCE);
if (GET_OBJ_VAL(obj, 0))
call_magic(ch, tch, tobj, GET_OBJ_VAL(obj, 3),
GET_OBJ_VAL(obj, 0), focus_SCOUTER);
else
call_magic(ch, tch, tobj, GET_OBJ_VAL(obj, 3),
DEFAULT_SCOUTER_LVL, focus_SCOUTER);
break;
case ITEM_SENZU:
if (*arg) {
if (!k) {
act("There is nothing to here to affect with $p.", FALSE,
ch, obj, NULL, TO_CHAR);
return;
}
} else
tch = ch;
act("You swallow $p.", TRUE, ch, obj, 0, TO_CHAR);
if (obj->action_description)
act(obj->action_description, FALSE, ch, obj, NULL, TO_ROOM);
else
act("$n swallows $p.", FALSE, ch, obj, NULL, TO_ROOM);
WAIT_STATE(ch, PULSE_VIOLENCE);
for (i = 1; i < 4; i++)
if (!(call_magic(ch, tch, tobj, GET_OBJ_VAL(obj, i),
GET_OBJ_VAL(obj, 0), focus_SENZU)))
break;
if (obj != NULL)
extract_obj(obj);
break;
case ITEM_SAIBAKIT:
tch = ch;
act("You plant $p.", FALSE, ch, obj, NULL, TO_CHAR);
if (obj->action_description)
act(obj->action_description, FALSE, ch, obj, NULL, TO_ROOM);
else
act("$n plants $p.", TRUE, ch, obj, NULL, TO_ROOM);
WAIT_STATE(ch, PULSE_VIOLENCE);
for (i = 1; i < 4; i++)
if (!(call_magic(ch, ch, NULL, GET_OBJ_VAL(obj, i),
GET_OBJ_VAL(obj, 0), focus_SAIBAKIT)))
break;
if (obj != NULL)
extract_obj(obj);
break;
default:
log("SYSERR: Unknown object_type %d in mag_objectmagic.",
GET_OBJ_TYPE(obj));
break;
}
}
/*
* focus_spell is used generically to focus any spoken spell, assuming we
* already have the target char/obj and spell number. It checks all
* restrictions, etc., prints the words, etc.
*
* Entry point for NPC focuss. Recommended entry point for spells focus
* by NPCs via specprocs.
*/
int focus_spell(struct char_data * ch, struct char_data * tch,
struct obj_data * tobj, int spellnum)
{
if (spellnum < 0 || spellnum > TOP_SPELL_DEFINE) {
log("SYSERR: focus_spell trying to call spellnum %d/%d.\n", spellnum,
TOP_SPELL_DEFINE);
return 0;
}
if (GET_POS(ch) < SINFO.min_position) {
switch (GET_POS(ch)) {
case POS_SLEEPING:
send_to_char("You dream about great powers.\r\n", ch);
break;
case POS_RESTING:
send_to_char("You cannot concentrate while resting.\r\n", ch);
break;
case POS_SITTING:
send_to_char("You can't do this sitting!\r\n", ch);
break;
case POS_FIGHTING:
send_to_char("Impossible! You can't concentrate enough!\r\n", ch);
break;
default:
send_to_char("You can't do much of anything like this!\r\n", ch);
break;
}
return 0;
}
if (AFF_FLAGGED(ch, AFF_MAJIN) && (ch->master == tch)) {
send_to_char("You you might hurt your master!\r\n", ch);
return 0;
}
if ((tch != ch) && IS_SET(SINFO.targets, TAR_SELF_ONLY)) {
send_to_char("You can only use this upon yourself!\r\n", ch);
return 0;
}
if ((tch == ch) && IS_SET(SINFO.targets, TAR_NOT_SELF)) {
send_to_char("You cannot use this on yourself!\r\n", ch);
return 0;
}
if (IS_SET(SINFO.routines, MAG_GROUPS) && !AFF_FLAGGED(ch, AFF_GROUP)) {
send_to_char("You can't use this if you're not in a group!\r\n",ch);
return 0;
}
send_to_char(OK, ch);
say_spell(ch, spellnum, tch, tobj);
return (call_magic(ch, tch, tobj, spellnum, GET_LEVEL(ch), focus_SPELL));
}
/*
* do_focus is the entry point for PC-focused spells. It parses the arguments,
* determines the spell number and finds a target, throws the die to see if
* the spell can be focus, checks for sufficient mana and subtracts it, and
* passes control to focus_spell().
*/
ACMD(do_focus)
{
struct char_data *tch = NULL;
struct obj_data *tobj = NULL;
char *s, *t;
int mana, spellnum, i, target = 0;
if (IS_NPC(ch))
return;
/* get: blank, spell name, target name */
s = strtok(argument, "'");
if (s == NULL) {
send_to_char("Focus What Where?\r\n", ch);
return;
}
s = strtok(NULL, "'");
if (s == NULL) {
send_to_char("Need to be in symbols: ''\r\n", ch);
return;
}
t = strtok(NULL, "\0");
/* spellnum = search_block(s, spells, 0); */
spellnum = find_skill_num(s);
if ((spellnum < 1) || (spellnum > MAX_SPELLS)) {
send_to_char("Focus what?!?\r\n", ch);
return;
}
if (GET_LEVEL(ch) < SINFO.min_level[(int) GET_CLASS(ch)]) {
send_to_char("You do not know how to do that!\r\n", ch);
return;
}
if (GET_SKILL(ch, spellnum) == 0) {
send_to_char("You are unfamiliar with that technique.\r\n", ch);
return;
}
/* Find the target */
if (t != NULL) {
one_argument(strcpy(arg, t), t);
skip_spaces(&t);
}
if (IS_SET(SINFO.targets, TAR_IGNORE)) {
target = TRUE;
} else if (t != NULL && *t) {
if (!target && (IS_SET(SINFO.targets, TAR_CHAR_ROOM))) {
if ((tch = get_char_room_vis(ch, t)) != NULL)
target = TRUE;
}
if (!target && IS_SET(SINFO.targets, TAR_CHAR_WORLD))
if ((tch = get_char_vis(ch, t)))
target = TRUE;
if (!target && IS_SET(SINFO.targets, TAR_OBJ_INV))
if ((tobj = get_obj_in_list_vis(ch, t, ch->carrying)))
target = TRUE;
if (!target && IS_SET(SINFO.targets, TAR_OBJ_EQUIP)) {
for (i = 0; !target && i < NUM_WEARS; i++)
if (GET_EQ(ch, i) && isname(t, GET_EQ(ch, i)->name)) {
tobj = GET_EQ(ch, i);
target = TRUE;
}
}
if (!target && IS_SET(SINFO.targets, TAR_OBJ_ROOM))
if ((tobj = get_obj_in_list_vis(ch, t, world[ch->in_room].contents)))
target = TRUE;
if (!target && IS_SET(SINFO.targets, TAR_OBJ_WORLD))
if ((tobj = get_obj_vis(ch, t)))
target = TRUE;
} else { /* if target string is empty */
if (!target && IS_SET(SINFO.targets, TAR_FIGHT_SELF))
if (FIGHTING(ch) != NULL) {
tch = ch;
target = TRUE;
}
if (!target && IS_SET(SINFO.targets, TAR_FIGHT_VICT))
if (FIGHTING(ch) != NULL) {
tch = FIGHTING(ch);
target = TRUE;
}
/* if no target specified, and the spell isn't violent, default to self */
if (!target && IS_SET(SINFO.targets, TAR_CHAR_ROOM) &&
!SINFO.violent) {
tch = ch;
target = TRUE;
}
if (!target) {
sprintf(buf, "Upon %s should the technique be used?\r\n",
IS_SET(SINFO.targets, TAR_OBJ_ROOM | TAR_OBJ_INV | TAR_OBJ_WORLD) ?
"what" : "who");
send_to_char(buf, ch);
return;
}
}
if (target && (tch == ch) && SINFO.violent) {
send_to_char("You shouldn't focus that on yourself -- could be bad for your health!\r\n", ch);
return;
}
if (!target) {
send_to_char("Cannot find the target of your technique!\r\n", ch);
return;
}
mana = mag_manacost(ch, spellnum);
if ((mana > 0) && (GET_MANA(ch) < mana) && (GET_LEVEL(ch) < LVL_IMMORT)) {
send_to_char("You haven't the energy to use that technique!\r\n", ch);
return;
}
/* You throws the dice and you takes your chances.. 101% is total failure */
if (number(0, 101) > GET_SKILL(ch, spellnum)) {
WAIT_STATE(ch, PULSE_VIOLENCE);
if (!tch || !skill_message(0, ch, tch, spellnum))
send_to_char("You lost your concentration!\r\n", ch);
if (mana > 0)
GET_MANA(ch) = MAX(0, MIN(GET_MAX_MANA(ch), GET_MANA(ch) - (mana / 2)));
if (SINFO.violent && tch && IS_NPC(tch))
hit(tch, ch, TYPE_UNDEFINED);
} else { /* focus spell returns 1 on success; subtract mana & set waitstate */
if (focus_spell(ch, tch, tobj, spellnum)) {
WAIT_STATE(ch, PULSE_VIOLENCE);
if (mana > 0)
GET_MANA(ch) = MAX(0, MIN(GET_MAX_MANA(ch), GET_MANA(ch) - mana));
}
}
}
void spell_level(int spell, int chclass, long long level)
{
int bad = 0;
if (spell < 0 || spell > TOP_SPELL_DEFINE) {
log("SYSERR: attempting assign to illegal spellnum %d/%d", spell, TOP_SPELL_DEFINE);
return;
}
if (chclass < 0 || chclass >= NUM_CLASSES) {
log("SYSERR: assigning '%s' to illegal class %d/%d.", skill_name(spell),
chclass, NUM_CLASSES - 1);
bad = 1;
}
if (level < 1 || level > LVL_IMPL) {
log("SYSERR: assigning '%s' to illegal level %Ld/%Ld.", skill_name(spell),
level, LVL_IMPL);
bad = 1;
}
if (!bad)
spell_info[spell].min_level[chclass] = level;
}
/* Assign the spells on boot up */
void spello(int spl, int max_mana, int min_mana, int mana_change, int minpos,
int targets, int violent, int routines)
{
int i;
for (i = 0; i < NUM_CLASSES; i++)
spell_info[spl].min_level[i] = LVL_IMMORT;
spell_info[spl].mana_max = max_mana;
spell_info[spl].mana_min = min_mana;
spell_info[spl].mana_change = mana_change;
spell_info[spl].min_position = minpos;
spell_info[spl].targets = targets;
spell_info[spl].violent = violent;
spell_info[spl].routines = routines;
}
void unused_spell(int spl)
{
int i;
for (i = 0; i < NUM_CLASSES; i++)
spell_info[spl].min_level[i] = LVL_IMPL + 1;
spell_info[spl].mana_max = 0;
spell_info[spl].mana_min = 0;
spell_info[spl].mana_change = 0;
spell_info[spl].min_position = 0;
spell_info[spl].targets = 0;
spell_info[spl].violent = 0;
spell_info[spl].routines = 0;
}
#define skillo(skill) spello(skill, 0, 0, 0, 0, 0, 0, 0);
/*
* Arguments for spello calls:
*
* spellnum, maxmana, minmana, manachng, minpos, targets, violent?, routines.
*
* spellnum: Number of the spell. Usually the symbolic name as defined in
* spells.h (such as SPELL_HEAL).
*
* maxmana : The maximum mana this spell will take (i.e., the mana it
* will take when the player first gets the spell).
*
* minmana : The minimum mana this spell will take, no matter how high
* level the focuser is.
*
* manachng: The change in mana for the spell from level to level. This
* number should be positive, but represents the reduction in mana cost as
* the focuser's level increases.
*
* minpos : Minimum position the focuser must be in for the spell to work
* (usually fighting or standing). targets : A "list" of the valid targets
* for the spell, joined with bitwise OR ('|').
*
* violent : TRUE or FALSE, depending on if this is considered a violent
* spell and should not be focus in PEACEFUL rooms or on yourself. Should be
* set on any spell that inflicts damage, is considered aggressive (i.e.
* MAJIN, curse), or is otherwise nasty.
*
* routines: A list of magic routines which are associated with this spell
* if the spell uses spell templates. Also joined with bitwise OR ('|').
*
* See the CircleMUD documentation for a more detailed description of these
* fields.
*/
/*
* NOTE: SPELL LEVELS ARE NO LONGER ASSIGNED HERE AS OF Circle 3.0 bpl9.
* In order to make this cleaner, as well as to make adding new classes
* much easier, spell levels are now assigned in class.c. You only need
* a spello() call to define a new spell; to decide who gets to use a spell
* or skill, look in class.c. -JE 5 Feb 1996
*/
void mag_assign_spells(void)
{
int i;
/* Do not change the loop below */
for (i = 1; i <= TOP_SPELL_DEFINE; i++)
unused_spell(i);
/* Do not change the loop above */
spello(SPELL_ANIMATE_DEAD, 35, 10, 3, POS_STANDING,
TAR_OBJ_ROOM, FALSE, MAG_SUMMONS);
spello(SPELL_armor, 30, 15, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_TSURUGI, 95, 72, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_MUTATE, 125, 100, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_KYODAIKA, 105, 83, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_BLADEMORPH, 105, 83, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_ZANZOKEN, 65, 25, 3, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS | MAG_ALTER_OBJS);
spello(SPELL_HASSHUKEN, 65, 25, 3, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS | MAG_ALTER_OBJS);
spello(SPELL_SOLAR_FLARE, 35, 25, 1, POS_STANDING,
TAR_CHAR_ROOM | TAR_NOT_SELF, FALSE, MAG_AFFECTS);
spello(SPELL_MAJIN, 75, 50, 2, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_NOT_SELF, TRUE, MAG_MANUAL);
spello(SPELL_MULTIFORM, 300, 210, 5, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_SUMMONS);
spello(SPELL_REPRODUCE, 250, 190, 5, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_SUMMONS);
spello(SPELL_SAIBA, 300, 210, 5, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_SUMMONS);
spello(SPELL_CONTROL_WEATHER, 75, 25, 5, POS_STANDING,
TAR_IGNORE, FALSE, MAG_MANUAL);
spello(SPELL_CREATE_FOOD, 30, 5, 4, POS_STANDING,
TAR_IGNORE, FALSE, MAG_CREATIONS);
spello(SPELL_CREATE_WATER, 30, 5, 4, POS_STANDING,
TAR_OBJ_INV | TAR_OBJ_EQUIP, FALSE, MAG_MANUAL);
spello(SPELL_CURE_SOLAR_FLARE, 30, 5, 2, POS_STANDING,
TAR_CHAR_ROOM, FALSE, MAG_UNAFFECTS);
spello(SPELL_CURE_CRITIC, 30, 10, 2, POS_FIGHTING,
TAR_CHAR_ROOM, FALSE, MAG_POINTS);
spello(SPELL_CURSE, 80, 50, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_OBJ_INV, TRUE, MAG_AFFECTS | MAG_ALTER_OBJS);
spello(SPELL_DETECT_ALIGN, 20, 10, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_DETECT_INVISIBILTY, 20, 10, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_DETECT_MAGIC, 20, 10, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_DETECT_POISON, 15, 5, 1, POS_STANDING,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL);
spello(SPELL_DISPEL_GOOD, 40, 25, 3, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_FIGHT_VICT, TRUE, MAG_DAMAGE);
spello(SPELL_BURNING_SOULS, 150, 100, 10, POS_STANDING,
TAR_OBJ_INV | TAR_OBJ_EQUIP, FALSE, MAG_MANUAL);
spello(SPELL_GROUP_armor, 50, 30, 2, POS_STANDING,
TAR_IGNORE, FALSE, MAG_GROUPS);
spello(SPELL_GROUP_HEAL, 80, 60, 5, POS_STANDING,
TAR_IGNORE, FALSE, MAG_GROUPS);
spello(SPELL_SWORD, 30, 5, 4, POS_STANDING,
TAR_IGNORE, FALSE, MAG_CREATIONS);
spello(SPELL_HEAL, 60, 40, 3, POS_FIGHTING,
TAR_CHAR_ROOM, FALSE, MAG_POINTS | MAG_UNAFFECTS);
spello(SPELL_SENZU, 60, 40, 3, POS_FIGHTING,
TAR_CHAR_ROOM, FALSE, MAG_POINTS | MAG_UNAFFECTS);
spello(SPELL_INFRAVISION, 25, 10, 1, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_INVISIBLE, 35, 25, 1, POS_STANDING,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_AFFECTS | MAG_ALTER_OBJS);
spello(SPELL_LOCATE_OBJECT, 25, 20, 1, POS_STANDING,
TAR_OBJ_WORLD, FALSE, MAG_MANUAL);
spello(SPELL_POISON, 50, 20, 3, POS_STANDING,
TAR_CHAR_ROOM | TAR_NOT_SELF | TAR_OBJ_INV, TRUE, MAG_AFFECTS | MAG_ALTER_OBJS);
spello(SPELL_PROT_FROM_EVIL, 40, 10, 3, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_REMOVE_CURSE, 45, 25, 5, POS_STANDING,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_EQUIP, FALSE,
MAG_UNAFFECTS | MAG_ALTER_OBJS);
spello(SPELL_BARRIER, 110, 85, 5, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_YOIKOMINMINKEN, 40, 25, 5, POS_STANDING,
TAR_CHAR_ROOM, TRUE, MAG_AFFECTS);
spello(SPELL_SUMMON, 75, 50, 3, POS_STANDING,
TAR_CHAR_WORLD | TAR_NOT_SELF, FALSE, MAG_MANUAL);
spello(SPELL_TELEPORT, 75, 50, 3, POS_STANDING,
TAR_CHAR_ROOM, FALSE, MAG_MANUAL);
spello(SPELL_WORD_OF_RECALL, 20, 10, 2, POS_FIGHTING,
TAR_CHAR_ROOM, FALSE, MAG_MANUAL);
spello(SPELL_REMOVE_POISON, 40, 8, 4, POS_STANDING,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_UNAFFECTS | MAG_ALTER_OBJS);
spello(SPELL_SENSE_LIFE, 20, 10, 2, POS_STANDING,
TAR_CHAR_ROOM | TAR_SELF_ONLY, FALSE, MAG_AFFECTS);
spello(SPELL_MIND_FREEZE, 250, 30, 2, POS_FIGHTING,
TAR_CHAR_ROOM | TAR_FIGHT_VICT, TRUE, MAG_AFFECTS);
/* NON-focusable spells should appear here */
spello(SPELL_IDENTIFY, 0, 0, 0, 0,
TAR_CHAR_ROOM | TAR_OBJ_INV | TAR_OBJ_ROOM, FALSE, MAG_MANUAL);
spello(SPELL_POWER_SENSE, 0, 0, 0, 0,
TAR_CHAR_ROOM, FALSE, MAG_MANUAL);
/*
* Declaration of skills - this actually doesn't do anything except
* set it up so that immortals can use these skills by default. The
* min level to use the skill for other classes is set up in class.c.
*/
skillo(SKILL_BACKSTAB);
skillo(SKILL_SWEEP);
skillo(SKILL_HIDE);
skillo(SKILL_PICK_LOCK);
skillo(SKILL_PUNCH);
skillo(SKILL_RESCUE);
skillo(SKILL_SWIFTNESS);
skillo(SKILL_MUG);
skillo(SKILL_SENSE);
skillo(SKILL_TAILWHIP);
skillo(SKILL_REPAIR);
skillo(SKILL_KAIOKEN);
skillo(SKILL_FLY);
skillo(SKILL_POWERUP);
skillo(SKILL_INGEST);
skillo(SKILL_POWERDOWN);
skillo(SKILL_SCAN);
skillo(SKILL_ABSORB);
skillo(SKILL_DESTRUCT);
skillo(SKILL_POWERSENSE);
skillo(SKILL_STEALTH);
}