/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************/
/*
MurkMUD++ - A Windows compatible, C++ compatible Merc 2.2 Mud.
\author Jon A. Lambert
\date 01/02/2007
\version 1.5
\remarks
This source code copyright (C) 2005, 2006, 2007 by Jon A. Lambert
All rights reserved.
Use governed by the MurkMUD++ public license found in license.murk++
*/
#include "os.hpp"
#include "config.hpp"
#include "globals.hpp"
#include "utils.hpp"
#include "object.hpp"
#include "room.hpp"
#include "affect.hpp"
#include "objproto.hpp"
#include "mobproto.hpp"
#include "world.hpp"
// temp externs
extern void damage(Character *ch, Character *victim, int dam, int dt);
extern std::string extra_bit_name (int extra_flags);
extern std::string affect_loc_name (int location);
extern bool is_same_group (Character * ach, Character * bch);
/*
* Spell functions.
*/
void Character::spell_acid_blast (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int dam;
dam = dice (lvl, 6);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_armor (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->has_affect(sn))
return;
af.type = sn;
af.duration = 24;
af.modifier = -20;
af.location = APPLY_AC;
af.bitvector = 0;
victim->affect_to_char(&af);
victim->send_to_char ("You feel someone protecting you.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_bless (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->position == POS_FIGHTING || victim->has_affect(sn))
return;
af.type = sn;
af.duration = 6 + lvl;
af.location = APPLY_HITROLL;
af.modifier = lvl / 8;
af.bitvector = 0;
victim->affect_to_char(&af);
af.location = APPLY_SAVING_SPELL;
af.modifier = 0 - lvl / 8;
victim->affect_to_char(&af);
victim->send_to_char ("You feel righteous.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_blindness (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_BLIND) || victim->saves_spell (lvl))
return;
af.type = sn;
af.location = APPLY_HITROLL;
af.modifier = -4;
af.duration = 1 + lvl;
af.bitvector = AFF_BLIND;
victim->affect_to_char(&af);
victim->send_to_char ("You are blinded!\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_burning_hands (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const sh_int dam_each[] = {
0,
0, 0, 0, 0, 14, 17, 20, 23, 26, 29,
29, 29, 30, 30, 31, 31, 32, 32, 33, 33,
34, 34, 35, 35, 36, 36, 37, 37, 38, 38,
39, 39, 40, 40, 41, 41, 42, 42, 43, 43,
44, 44, 45, 45, 46, 46, 47, 47, 48, 48
};
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_call_lightning (int sn, int lvl, void *vo)
{
Character *vch;
if (!is_outside()) {
send_to_char ("You must be out of doors.\r\n");
return;
}
if (!g_world->is_raining()) {
send_to_char ("You need bad weather.\r\n");
return;
}
int dam = dice (lvl / 2, 8);
send_to_char ("God's lightning strikes your foes!\r\n");
act ("$n calls God's lightning to strike $s foes!",
NULL, NULL, TO_ROOM);
CharIter c, next;
for (c = char_list.begin(); c != char_list.end(); c = next) {
vch = *c;
next = ++c;
if (vch->in_room == NULL)
continue;
if (vch->in_room == in_room) {
if (vch != this && (is_npc () ? !vch->is_npc () : vch->is_npc ()))
damage (this, vch, vch->saves_spell (lvl) ? dam / 2 : dam, sn);
continue;
}
if (vch->in_room->area == in_room->area && vch->is_outside()
&& vch->is_awake ())
vch->send_to_char ("Lightning flashes in the sky.\r\n");
}
return;
}
void Character::spell_cause_light (int sn, int lvl, void *vo)
{
damage (this, (Character *) vo, dice (1, 8) + lvl / 3, sn);
return;
}
void Character::spell_cause_critical (int sn, int lvl, void *vo)
{
damage (this, (Character *) vo, dice (3, 8) + lvl - 6, sn);
return;
}
void Character::spell_cause_serious (int sn, int lvl, void *vo)
{
damage (this, (Character *) vo, dice (2, 8) + lvl / 2, sn);
return;
}
void Character::spell_change_sex (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->has_affect(sn))
return;
af.type = sn;
af.duration = 10 * lvl;
af.location = APPLY_SEX;
do {
af.modifier = number_range (0, 2) - victim->sex;
}
while (af.modifier == 0);
af.bitvector = 0;
victim->affect_to_char(&af);
victim->send_to_char ("You feel different.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_charm_person (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim == this) {
send_to_char ("You like yourself even better!\r\n");
return;
}
if (victim->is_affected (AFF_CHARM)
|| is_affected (AFF_CHARM)
|| lvl < victim->level || victim->saves_spell (lvl))
return;
if (victim->master)
victim->stop_follower();
victim->add_follower(this);
af.type = sn;
af.duration = number_fuzzy (lvl / 4);
af.location = 0;
af.modifier = 0;
af.bitvector = AFF_CHARM;
victim->affect_to_char(&af);
act ("Isn't $n just so nice?", NULL, victim, TO_VICT);
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_chill_touch (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const sh_int dam_each[] = {
0,
0, 0, 6, 7, 8, 9, 12, 13, 13, 13,
14, 14, 14, 15, 15, 15, 16, 16, 16, 17,
17, 17, 18, 18, 18, 19, 19, 19, 20, 20,
20, 21, 21, 21, 22, 22, 22, 23, 23, 23,
24, 24, 24, 25, 25, 25, 26, 26, 26, 27
};
Affect af;
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (!victim->saves_spell (lvl)) {
af.type = sn;
af.duration = 6;
af.location = APPLY_STR;
af.modifier = -1;
af.bitvector = 0;
victim->affect_join (&af);
} else {
dam /= 2;
}
damage (this, victim, dam, sn);
return;
}
void Character::spell_colour_spray (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const sh_int dam_each[] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30, 35, 40, 45, 50, 55, 55, 55, 56, 57,
58, 58, 59, 60, 61, 61, 62, 63, 64, 64,
65, 66, 67, 67, 68, 69, 70, 70, 71, 72,
73, 73, 74, 75, 76, 76, 77, 78, 79, 79
};
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_continual_light (int sn, int lvl, void *vo)
{
Object *light;
light = get_obj_index(OBJ_VNUM_LIGHT_BALL)->create_object(0);
light->obj_to_room (in_room);
act ("$n twiddles $s thumbs and $p appears.", light, NULL, TO_ROOM);
act ("You twiddle your thumbs and $p appears.", light, NULL, TO_CHAR);
return;
}
void Character::spell_control_weather (int sn, int lvl, void *vo)
{
int change = dice (lvl / 3, 4);
if (!str_cmp (target_name, "better"))
change *= 1;
else if (!str_cmp (target_name, "worse"))
change *= -1;
else
send_to_char ("Do you want it to get better or worse?\r\n");
g_world->change_weather(change);
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_create_food (int sn, int lvl, void *vo)
{
Object* mushroom = get_obj_index(OBJ_VNUM_MUSHROOM)->create_object(0);
mushroom->value[0] = 5 + lvl;
mushroom->obj_to_room (in_room);
act ("$p suddenly appears.", mushroom, NULL, TO_ROOM);
act ("$p suddenly appears.", mushroom, NULL, TO_CHAR);
return;
}
void Character::spell_create_spring (int sn, int lvl, void *vo)
{
Object* spring = get_obj_index(OBJ_VNUM_SPRING)->create_object(0);
spring->timer = lvl;
spring->obj_to_room (in_room);
act ("$p flows from the ground.", spring, NULL, TO_ROOM);
act ("$p flows from the ground.", spring, NULL, TO_CHAR);
return;
}
void Character::spell_create_water (int sn, int lvl, void *vo)
{
Object *obj = (Object *) vo;
if (obj->item_type != ITEM_DRINK_CON) {
send_to_char ("It is unable to hold water.\r\n");
return;
}
if (obj->value[2] != LIQ_WATER && obj->value[1] != 0) {
send_to_char ("It contains some other liquid.\r\n");
return;
}
int water = std::min (lvl * (g_world->is_raining() ? 4 : 2),
obj->value[0] - obj->value[1]
);
if (water > 0) {
obj->value[2] = LIQ_WATER;
obj->value[1] += water;
if (!is_name ("water", obj->name)) {
obj->name = obj->name + " water";
}
act ("$p is filled.", obj, NULL, TO_CHAR);
}
return;
}
void Character::spell_cure_blindness (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (!victim->has_affect(skill_lookup("blindness")))
return;
victim->affect_strip (skill_lookup("blindness"));
victim->send_to_char ("Your vision returns!\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_cure_critical (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int heal = dice (3, 8) + lvl - 6;
victim->hit = std::min (victim->hit + heal, victim->max_hit);
victim->update_pos();
victim->send_to_char ("You feel better!\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_cure_light (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int heal = dice (1, 8) + lvl / 3;
victim->hit = std::min (victim->hit + heal, victim->max_hit);
victim->update_pos();
victim->send_to_char ("You feel better!\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_cure_poison (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int pois = skill_lookup("poison");
if (victim->has_affect(pois)) {
victim->affect_strip (pois);
act ("$N looks better.", NULL, victim, TO_NOTVICT);
victim->send_to_char ("A warm feeling runs through your body.\r\n");
send_to_char ("Ok.\r\n");
}
return;
}
void Character::spell_cure_serious (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int heal = dice (2, 8) + lvl / 2;
victim->hit = std::min (victim->hit + heal, victim->max_hit);
victim->update_pos();
victim->send_to_char ("You feel better!\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_curse (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_CURSE) || victim->saves_spell (lvl))
return;
af.type = sn;
af.duration = 4 * lvl;
af.location = APPLY_HITROLL;
af.modifier = -1;
af.bitvector = AFF_CURSE;
victim->affect_to_char(&af);
af.location = APPLY_SAVING_SPELL;
af.modifier = 1;
victim->affect_to_char(&af);
victim->send_to_char ("You feel unclean.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_detect_evil (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_DETECT_EVIL))
return;
af.type = sn;
af.duration = lvl;
af.modifier = 0;
af.location = APPLY_NONE;
af.bitvector = AFF_DETECT_EVIL;
victim->affect_to_char(&af);
victim->send_to_char ("Your eyes tingle.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_detect_hidden (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_DETECT_HIDDEN))
return;
af.type = sn;
af.duration = lvl;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_DETECT_HIDDEN;
victim->affect_to_char(&af);
victim->send_to_char ("Your awareness improves.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_detect_invis (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_DETECT_INVIS))
return;
af.type = sn;
af.duration = lvl;
af.modifier = 0;
af.location = APPLY_NONE;
af.bitvector = AFF_DETECT_INVIS;
victim->affect_to_char(&af);
victim->send_to_char ("Your eyes tingle.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_detect_magic (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_DETECT_MAGIC))
return;
af.type = sn;
af.duration = lvl;
af.modifier = 0;
af.location = APPLY_NONE;
af.bitvector = AFF_DETECT_MAGIC;
victim->affect_to_char(&af);
victim->send_to_char ("Your eyes tingle.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_detect_poison (int sn, int lvl, void *vo)
{
Object *obj = (Object *) vo;
if (obj->item_type == ITEM_DRINK_CON || obj->item_type == ITEM_FOOD) {
if (obj->value[3] != 0)
send_to_char ("You smell poisonous fumes.\r\n");
else
send_to_char ("It looks very delicious.\r\n");
} else {
send_to_char ("It doesn't look poisoned.\r\n");
}
return;
}
void Character::spell_dispel_magic (int sn, int lvl, void *vo)
{
send_to_char ("Sorry but this spell has been disabled.\r\n");
return;
}
void Character::spell_dispel_evil (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (!is_npc () && is_evil ())
victim = this;
if (victim->is_good ()) {
act ("God protects $N.", NULL, victim, TO_ROOM);
return;
}
if (victim->is_neutral ()) {
act ("$N does not seem to be affected.", NULL, victim, TO_CHAR);
return;
}
int dam = dice (lvl, 4);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_earthquake (int sn, int lvl, void *vo)
{
send_to_char ("The earth trembles beneath your feet!\r\n");
act ("$n makes the earth tremble and shiver.", NULL, NULL, TO_ROOM);
CharIter c, next;
for (c = char_list.begin(); c != char_list.end(); c = next) {
Character* vch = *c;
next = ++c;
if (vch->in_room == NULL)
continue;
if (vch->in_room == in_room) {
if (vch != this && (is_npc () ? !vch->is_npc () : vch->is_npc ()))
damage (this, vch, lvl + dice (2, 8), sn);
continue;
}
if (vch->in_room->area == in_room->area)
vch->send_to_char ("The earth trembles and shivers.\r\n");
}
return;
}
void Character::spell_enchant_weapon (int sn, int lvl, void *vo)
{
Object *obj = (Object *) vo;
Affect *paf;
if (obj->item_type != ITEM_WEAPON || obj->is_obj_stat(ITEM_MAGIC)
|| !obj->affected.empty())
return;
paf = new Affect();
paf->type = sn;
paf->duration = -1;
paf->location = APPLY_HITROLL;
paf->modifier = lvl / 5;
paf->bitvector = 0;
obj->affected.push_back(paf);
paf = new Affect();
paf->type = -1;
paf->duration = -1;
paf->location = APPLY_DAMROLL;
paf->modifier = lvl / 10;
paf->bitvector = 0;
obj->affected.push_back(paf);
obj->level = number_fuzzy (level - 5);
if (is_good ()) {
SET_BIT (obj->extra_flags, ITEM_ANTI_EVIL);
act ("$p glows blue.", obj, NULL, TO_CHAR);
} else if (is_evil ()) {
SET_BIT (obj->extra_flags, ITEM_ANTI_GOOD);
act ("$p glows red.", obj, NULL, TO_CHAR);
} else {
SET_BIT (obj->extra_flags, ITEM_ANTI_EVIL);
SET_BIT (obj->extra_flags, ITEM_ANTI_GOOD);
act ("$p glows yellow.", obj, NULL, TO_CHAR);
}
send_to_char ("Ok.\r\n");
return;
}
/*
* Drain XP, MANA, HP.
* Caster gains HP.
*/
void Character::spell_energy_drain (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int dam;
if (victim->saves_spell (lvl))
return;
alignment = std::max (-1000, alignment - 200);
if (victim->level <= 2) {
dam = hit + 1;
} else {
victim->gain_exp(0 - number_range (lvl / 2, 3 * lvl / 2));
victim->mana /= 2;
victim->move /= 2;
dam = dice (1, lvl);
hit += dam;
}
damage (this, victim, dam, sn);
return;
}
void Character::spell_fireball (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const sh_int dam_each[] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 30, 35, 40, 45, 50, 55,
60, 65, 70, 75, 80, 82, 84, 86, 88, 90,
92, 94, 96, 98, 100, 102, 104, 106, 108, 110,
112, 114, 116, 118, 120, 122, 124, 126, 128, 130
};
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_flamestrike (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int dam = dice (6, lvl);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_faerie_fire (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_FAERIE_FIRE))
return;
af.type = sn;
af.duration = lvl;
af.location = APPLY_AC;
af.modifier = 2 * lvl;
af.bitvector = AFF_FAERIE_FIRE;
victim->affect_to_char(&af);
victim->send_to_char ("You are surrounded by a pink outline.\r\n");
victim->act ("$n is surrounded by a pink outline.", NULL, NULL, TO_ROOM);
return;
}
void Character::spell_faerie_fog (int sn, int lvl, void *vo)
{
act ("$n conjures a cloud of purple smoke.", NULL, NULL, TO_ROOM);
send_to_char ("You conjure a cloud of purple smoke.\r\n");
CharIter ich;
for (ich = in_room->people.begin(); ich != in_room->people.end(); ich++) {
if (*ich == this || (*ich)->saves_spell (lvl))
continue;
(*ich)->affect_strip (skill_lookup("invis"));
(*ich)->affect_strip (skill_lookup("mass invis"));
(*ich)->affect_strip (skill_lookup("sneak"));
REMOVE_BIT ((*ich)->affected_by, AFF_HIDE);
REMOVE_BIT ((*ich)->affected_by, AFF_INVISIBLE);
REMOVE_BIT ((*ich)->affected_by, AFF_SNEAK);
(*ich)->act ("$n is revealed!", NULL, NULL, TO_ROOM);
(*ich)->send_to_char ("You are revealed!\r\n");
}
return;
}
void Character::spell_fly (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_FLYING))
return;
af.type = sn;
af.duration = lvl + 3;
af.location = 0;
af.modifier = 0;
af.bitvector = AFF_FLYING;
victim->affect_to_char(&af);
victim->send_to_char ("Your feet rise off the ground.\r\n");
victim->act ("$n's feet rise off the ground.", NULL, NULL, TO_ROOM);
return;
}
void Character::spell_gate (int sn, int lvl, void *vo)
{
get_mob_index(MOB_VNUM_VAMPIRE)->create_mobile()->char_to_room(in_room);
return;
}
/*
* Spell for mega1.are from Glop/Erkenbrand.
*/
void Character::spell_general_purpose (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int dam = number_range (25, 100);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_giant_strength (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->has_affect(sn))
return;
af.type = sn;
af.duration = lvl;
af.location = APPLY_STR;
af.modifier = 1 + (lvl >= 18) + (lvl >= 25);
af.bitvector = 0;
victim->affect_to_char(&af);
victim->send_to_char ("You feel stronger.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_harm (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int dam;
dam = std::max (20, victim->hit - dice (1, 4));
if (victim->saves_spell (lvl))
dam = std::min (50, dam / 4);
dam = std::min (100, dam);
damage (this, victim, dam, sn);
return;
}
void Character::spell_heal (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
victim->hit = std::min (victim->hit + 100, victim->max_hit);
victim->update_pos();
victim->send_to_char ("A warm feeling fills your body.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
/*
* Spell for mega1.are from Glop/Erkenbrand.
*/
void Character::spell_high_explosive (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int dam = number_range (30, 120);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_identify (int sn, int lvl, void *vo)
{
Object *obj = (Object *) vo;
char buf[MAX_STRING_LENGTH];
snprintf (buf, sizeof buf,
"Object '%s' is type %s, extra flags %s.\r\nWeight is %d, value is %d, lvl is %d.\r\n",
obj->name.c_str(),
obj->item_type_name().c_str(),
extra_bit_name (obj->extra_flags).c_str(), obj->weight, obj->cost, obj->level);
send_to_char (buf);
switch (obj->item_type) {
case ITEM_SCROLL:
case ITEM_POTION:
snprintf (buf, sizeof buf, "Level %d spells of:", obj->value[0]);
send_to_char (buf);
if (obj->value[1] >= 0 && obj->value[1] < MAX_SKILL) {
send_to_char (" '");
send_to_char (skill_table[obj->value[1]].name);
send_to_char ("'");
}
if (obj->value[2] >= 0 && obj->value[2] < MAX_SKILL) {
send_to_char (" '");
send_to_char (skill_table[obj->value[2]].name);
send_to_char ("'");
}
if (obj->value[3] >= 0 && obj->value[3] < MAX_SKILL) {
send_to_char (" '");
send_to_char (skill_table[obj->value[3]].name);
send_to_char ("'");
}
send_to_char (".\r\n");
break;
case ITEM_WAND:
case ITEM_STAFF:
snprintf (buf, sizeof buf, "Has %d(%d) charges of level %d",
obj->value[1], obj->value[2], obj->value[0]);
send_to_char (buf);
if (obj->value[3] >= 0 && obj->value[3] < MAX_SKILL) {
send_to_char (" '");
send_to_char (skill_table[obj->value[3]].name);
send_to_char ("'");
}
send_to_char (".\r\n");
break;
case ITEM_WEAPON:
snprintf (buf, sizeof buf, "Damage is %d to %d (average %d).\r\n",
obj->value[1], obj->value[2], (obj->value[1] + obj->value[2]) / 2);
send_to_char (buf);
break;
case ITEM_ARMOR:
snprintf (buf, sizeof buf, "Armor class is %d.\r\n", obj->value[0]);
send_to_char (buf);
break;
}
AffIter af;
for (af = obj->pIndexData->affected.begin(); af != obj->pIndexData->affected.end(); af++) {
if ((*af)->location != APPLY_NONE && (*af)->modifier != 0) {
snprintf (buf, sizeof buf, "Affects %s by %d.\r\n",
affect_loc_name ((*af)->location).c_str(), (*af)->modifier);
send_to_char (buf);
}
}
for (af = obj->affected.begin(); af != obj->affected.end(); af++) {
if ((*af)->location != APPLY_NONE && (*af)->modifier != 0) {
snprintf (buf, sizeof buf, "Affects %s by %d.\r\n",
affect_loc_name ((*af)->location).c_str(), (*af)->modifier);
send_to_char (buf);
}
}
return;
}
void Character::spell_infravision (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_INFRARED))
return;
act ("$n's eyes glow red.\r\n", NULL, NULL, TO_ROOM);
af.type = sn;
af.duration = 2 * lvl;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_INFRARED;
victim->affect_to_char(&af);
victim->send_to_char ("Your eyes glow red.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_invis (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_INVISIBLE))
return;
victim->act ("$n fades out of existence.", NULL, NULL, TO_ROOM);
af.type = sn;
af.duration = 24;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_INVISIBLE;
victim->affect_to_char(&af);
victim->send_to_char ("You fade out of existence.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_know_alignment (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
const char *msg;
int ap = victim->alignment;
if (ap > 700)
msg = "$N has an aura as white as the driven snow.";
else if (ap > 350)
msg = "$N is of excellent moral character.";
else if (ap > 100)
msg = "$N is often kind and thoughtful.";
else if (ap > -100)
msg = "$N doesn't have a firm moral commitment.";
else if (ap > -350)
msg = "$N lies to $S friends.";
else if (ap > -700)
msg = "$N's slash DISEMBOWELS you!";
else
msg = "I'd rather just not say anything at all about $N.";
act (msg, NULL, victim, TO_CHAR);
return;
}
void Character::spell_lightning_bolt (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const sh_int dam_each[] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 25, 28,
31, 34, 37, 40, 40, 41, 42, 42, 43, 44,
44, 45, 46, 46, 47, 48, 48, 49, 50, 50,
51, 52, 52, 53, 54, 54, 55, 56, 56, 57,
58, 58, 59, 60, 60, 61, 62, 62, 63, 64
};
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_locate_object (int sn, int lvl, void *vo)
{
std::string buf;
bool found = false;
ObjIter o;
for (o = object_list.begin(); o != object_list.end(); o++) {
Object *in_obj;
if (!can_see_obj(*o) || !is_name (target_name, (*o)->name))
continue;
found = true;
for (in_obj = *o; in_obj->in_obj != NULL; in_obj = in_obj->in_obj) ;
if (in_obj->carried_by != NULL) {
buf = (*o)->short_descr + " carried by " + in_obj->carried_by->describe_to(this) + "\r\n";
} else {
buf = (*o)->short_descr + " in " +
(in_obj->in_room == NULL ? "somewhere" : in_obj->in_room->name.c_str()) +
".\r\n";
}
buf[0] = toupper (buf[0]);
send_to_char (buf);
}
if (!found)
send_to_char ("Nothing like that in hell, earth, or heaven.\r\n");
return;
}
void Character::spell_magic_missile (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const sh_int dam_each[] = {
0,
3, 3, 4, 4, 5, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 14, 14, 14, 14, 14
};
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_mass_invis (int sn, int lvl, void *vo)
{
Affect af;
CharIter gch;
for (gch = in_room->people.begin(); gch != in_room->people.end(); gch++) {
if (!is_same_group (*gch, this) || (*gch)->is_affected (AFF_INVISIBLE))
continue;
(*gch)->act ("$n slowly fades out of existence.", NULL, NULL, TO_ROOM);
(*gch)->send_to_char ("You slowly fade out of existence.\r\n");
af.type = sn;
af.duration = 24;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_INVISIBLE;
(*gch)->affect_to_char(&af);
}
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_null (int sn, int lvl, void *vo)
{
send_to_char ("That's not a spell!\r\n");
return;
}
void Character::spell_pass_door (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_PASS_DOOR))
return;
af.type = sn;
af.duration = number_fuzzy (lvl / 4);
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_PASS_DOOR;
victim->affect_to_char(&af);
victim->act ("$n turns translucent.", NULL, NULL, TO_ROOM);
victim->send_to_char ("You turn translucent.\r\n");
return;
}
void Character::spell_poison (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->saves_spell (lvl))
return;
af.type = sn;
af.duration = lvl;
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = AFF_POISON;
victim->affect_join (&af);
victim->send_to_char ("You feel very sick.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_protection (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_PROTECT))
return;
af.type = sn;
af.duration = 24;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_PROTECT;
victim->affect_to_char(&af);
victim->send_to_char ("You feel protected.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_refresh (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
victim->move = std::min (victim->move + lvl, victim->max_move);
victim->send_to_char ("You feel less tired.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
void Character::spell_remove_curse (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (victim->has_affect(skill_lookup("curse"))) {
victim->affect_strip (skill_lookup("curse"));
victim->send_to_char ("You feel better.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
}
return;
}
void Character::spell_sanctuary (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_SANCTUARY))
return;
af.type = sn;
af.duration = number_fuzzy (lvl / 8);
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_SANCTUARY;
victim->affect_to_char(&af);
victim->act ("$n is surrounded by a white aura.", NULL, NULL, TO_ROOM);
victim->send_to_char ("You are surrounded by a white aura.\r\n");
return;
}
void Character::spell_shield (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->has_affect(sn))
return;
af.type = sn;
af.duration = 8 + lvl;
af.location = APPLY_AC;
af.modifier = -20;
af.bitvector = 0;
victim->affect_to_char(&af);
victim->act ("$n is surrounded by a force shield.", NULL, NULL, TO_ROOM);
victim->send_to_char ("You are surrounded by a force shield.\r\n");
return;
}
void Character::spell_shocking_grasp (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
static const int dam_each[] = {
0,
0, 0, 0, 0, 0, 0, 20, 25, 29, 33,
36, 39, 39, 39, 40, 40, 41, 41, 42, 42,
43, 43, 44, 44, 45, 45, 46, 46, 47, 47,
48, 48, 49, 49, 50, 50, 51, 51, 52, 52,
53, 53, 54, 54, 55, 55, 56, 56, 57, 57
};
lvl = std::min (lvl, (int) (sizeof (dam_each) / sizeof (dam_each[0]) - 1));
lvl = std::max (0, lvl);
int dam = number_range (dam_each[lvl] / 2, dam_each[lvl] * 2);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_sleep (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->is_affected (AFF_SLEEP)
|| lvl < victim->level || victim->saves_spell (lvl))
return;
af.type = sn;
af.duration = 4 + lvl;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_SLEEP;
victim->affect_join (&af);
if (victim->is_awake ()) {
victim->send_to_char ("You feel very sleepy ..... zzzzzz.\r\n");
victim->act ("$n goes to sleep.", NULL, NULL, TO_ROOM);
victim->position = POS_SLEEPING;
}
return;
}
void Character::spell_stone_skin (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (has_affect(sn))
return;
af.type = sn;
af.duration = lvl;
af.location = APPLY_AC;
af.modifier = -40;
af.bitvector = 0;
victim->affect_to_char(&af);
victim->act ("$n's skin turns to stone.", NULL, NULL, TO_ROOM);
victim->send_to_char ("Your skin turns to stone.\r\n");
return;
}
void Character::spell_summon (int sn, int lvl, void *vo)
{
Character *victim;
if ((victim = get_char_world (target_name)) == NULL
|| victim == this
|| victim->in_room == NULL
|| IS_SET (victim->in_room->room_flags, ROOM_SAFE)
|| IS_SET (victim->in_room->room_flags, ROOM_PRIVATE)
|| IS_SET (victim->in_room->room_flags, ROOM_SOLITARY)
|| IS_SET (victim->in_room->room_flags, ROOM_NO_RECALL)
|| victim->level >= lvl + 3
|| victim->fighting != NULL
|| victim->in_room->area != in_room->area
|| (victim->is_npc () && victim->saves_spell (lvl))) {
send_to_char ("You failed.\r\n");
return;
}
victim->act ("$n disappears suddenly.", NULL, NULL, TO_ROOM);
victim->char_from_room();
victim->char_to_room(in_room);
victim->act ("$n arrives suddenly.", NULL, NULL, TO_ROOM);
act ("$N has summoned you!", NULL, victim, TO_VICT);
victim->do_look ("auto");
return;
}
void Character::spell_teleport (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (victim->in_room == NULL
|| IS_SET (victim->in_room->room_flags, ROOM_NO_RECALL)
|| (!is_npc () && victim->fighting != NULL)
|| (victim != this
&& (victim->saves_spell (lvl) || victim->saves_spell (lvl)))) {
send_to_char ("You failed.\r\n");
return;
}
Room *pRoomIndex;
for (;;) {
pRoomIndex = get_room_index (number_range (0, 65535));
if (pRoomIndex != NULL)
if (!IS_SET (pRoomIndex->room_flags, ROOM_PRIVATE)
&& !IS_SET (pRoomIndex->room_flags, ROOM_SOLITARY))
break;
}
victim->act ("$n slowly fades out of existence.", NULL, NULL, TO_ROOM);
victim->char_from_room();
victim->char_to_room(pRoomIndex);
victim->act ("$n slowly fades into existence.", NULL, NULL, TO_ROOM);
victim->do_look ("auto");
return;
}
void Character::spell_ventriloquate (int sn, int lvl, void *vo)
{
std::string speaker;
target_name = one_argument (target_name, speaker);
char buf1[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
snprintf (buf1, sizeof buf1, "%s says '%s'.\r\n", speaker.c_str(), target_name.c_str());
snprintf (buf2, sizeof buf2, "Someone makes %s say '%s'.\r\n", speaker.c_str(), target_name.c_str());
buf1[0] = toupper (buf1[0]);
CharIter vch;
for (vch = in_room->people.begin(); vch != in_room->people.end(); vch++) {
if (!is_name (speaker, (*vch)->name))
(*vch)->send_to_char ((*vch)->saves_spell (lvl) ? buf2 : buf1);
}
return;
}
void Character::spell_weaken (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
Affect af;
if (victim->has_affect(sn) || victim->saves_spell (lvl))
return;
af.type = sn;
af.duration = lvl / 2;
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = 0;
victim->affect_to_char(&af);
victim->send_to_char ("You feel weaker.\r\n");
if (this != victim)
send_to_char ("Ok.\r\n");
return;
}
/*
* This is for muds that _want_ scrolls of recall.
* Ick.
*/
void Character::spell_word_of_recall (int sn, int lvl, void *vo)
{
((Character *) vo)->do_recall ("");
return;
}
/*
* NPC spells.
*/
void Character::spell_acid_breath (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (number_percent () < 2 * lvl && !victim->saves_spell (lvl)) {
Object *obj_lose;
ObjIter o, onext;
for (o = victim->carrying.begin(); o != victim->carrying.end(); o = onext) {
obj_lose = *o;
onext = ++o;
int iWear;
if (number_percent() <= 75)
continue;
switch (obj_lose->item_type) {
case ITEM_ARMOR:
if (obj_lose->value[0] > 0) {
victim->act ("$p is pitted and etched!", obj_lose, NULL, TO_CHAR);
if ((iWear = obj_lose->wear_loc) != WEAR_NONE)
victim->armor -= obj_lose->apply_ac (iWear);
obj_lose->value[0] -= 1;
obj_lose->cost = 0;
if (iWear != WEAR_NONE)
victim->armor += obj_lose->apply_ac (iWear);
}
break;
case ITEM_CONTAINER:
victim->act ("$p fumes and dissolves!", obj_lose, NULL, TO_CHAR);
obj_lose->extract_obj ();
break;
}
}
}
int hpch = std::max (10, hit);
int dam = number_range (hpch / 16 + 1, hpch / 8);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_fire_breath (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (number_percent () < 2 * lvl && !victim->saves_spell (lvl)) {
Object *obj_lose;
ObjIter o, onext;
for (o = victim->carrying.begin(); o != victim->carrying.end(); o = onext) {
obj_lose = *o;
onext = ++o;
const char *msg;
if (number_percent() <= 75)
continue;
switch (obj_lose->item_type) {
default:
continue;
case ITEM_CONTAINER:
msg = "$p ignites and burns!";
break;
case ITEM_POTION:
msg = "$p bubbles and boils!";
break;
case ITEM_SCROLL:
msg = "$p crackles and burns!";
break;
case ITEM_STAFF:
msg = "$p smokes and chars!";
break;
case ITEM_WAND:
msg = "$p sparks and sputters!";
break;
case ITEM_FOOD:
msg = "$p blackens and crisps!";
break;
case ITEM_PILL:
msg = "$p melts and drips!";
break;
}
victim->act (msg, obj_lose, NULL, TO_CHAR);
obj_lose->extract_obj ();
}
}
int hpch = std::max (10, hit);
int dam = number_range (hpch / 16 + 1, hpch / 8);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_frost_breath (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
if (number_percent () < 2 * lvl && !victim->saves_spell (lvl)) {
Object *obj_lose;
ObjIter o, onext;
for (o = victim->carrying.begin(); o != victim->carrying.end(); o = onext) {
obj_lose = *o;
onext = ++o;
const char *msg;
if (number_percent() <= 75)
continue;
switch (obj_lose->item_type) {
default:
continue;
case ITEM_CONTAINER:
case ITEM_DRINK_CON:
case ITEM_POTION:
msg = "$p freezes and shatters!";
break;
}
victim->act (msg, obj_lose, NULL, TO_CHAR);
obj_lose->extract_obj ();
}
}
int hpch = std::max (10, hit);
int dam = number_range (hpch / 16 + 1, hpch / 8);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}
void Character::spell_gas_breath (int sn, int lvl, void *vo)
{
Character *vch;
CharIter rch, next;
for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = next) {
vch = *rch;
next = ++rch;
if (is_npc () ? !vch->is_npc () : vch->is_npc ()) {
int hpch = std::max (10, hit);
int dam = number_range (hpch / 16 + 1, hpch / 8);
if (vch->saves_spell (lvl))
dam /= 2;
damage (this, vch, dam, sn);
}
}
return;
}
void Character::spell_lightning_breath (int sn, int lvl, void *vo)
{
Character *victim = (Character *) vo;
int hpch = std::max (10, hit);
int dam = number_range (hpch / 16 + 1, hpch / 8);
if (victim->saves_spell (lvl))
dam /= 2;
damage (this, victim, dam, sn);
return;
}