973 lines
25 KiB
C
973 lines
25 KiB
C
/* ************************************************************************
|
|
* File: magic.c Part of CircleMUD *
|
|
* Usage: low-level functions for magic; spell template code *
|
|
* *
|
|
* 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 "comm.h"
|
|
#include "spells.h"
|
|
#include "handler.h"
|
|
#include "db.h"
|
|
#include "interpreter.h"
|
|
#include "constants.h"
|
|
|
|
|
|
/* external variables */
|
|
extern int mini_mud;
|
|
extern int pk_allowed;
|
|
extern struct spell_info_type spell_info[];
|
|
|
|
/* external functions */
|
|
byte saving_throws(int class_num, int type, int level); /* class.c */
|
|
void clearMemory(struct char_data *ch);
|
|
void weight_change_object(struct obj_data *obj, int weight);
|
|
|
|
/* local functions */
|
|
int mag_materials(struct char_data *ch, int item0, int item1, int item2, int extract, int verbose);
|
|
void perform_mag_groups(int level, struct char_data *ch, struct char_data *tch, int spellnum, int savetype);
|
|
int mag_savingthrow(struct char_data *ch, int type, int modifier);
|
|
void affect_update(void);
|
|
|
|
/*
|
|
* Saving throws are now in class.c as of bpl13.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Negative apply_saving_throw[] values make saving throws better!
|
|
* Then, so do negative modifiers. Though people may be used to
|
|
* the reverse of that. It's due to the code modifying the target
|
|
* saving throw instead of the random number of the character as
|
|
* in some other systems.
|
|
*/
|
|
int mag_savingthrow(struct char_data *ch, int type, int modifier)
|
|
{
|
|
/* NPCs use warrior tables according to some book */
|
|
int class_sav = CLASS_WARRIOR;
|
|
int save;
|
|
|
|
if (!IS_NPC(ch))
|
|
class_sav = GET_CLASS(ch);
|
|
|
|
save = saving_throws(class_sav, type, GET_LEVEL(ch));
|
|
save += GET_SAVE(ch, type);
|
|
save += modifier;
|
|
|
|
/* Throwing a 0 is always a failure. */
|
|
if (MAX(1, save) < rand_number(0, 99))
|
|
return (TRUE);
|
|
|
|
/* Oops, failed. Sorry. */
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
/* affect_update: called from comm.c (causes spells to wear off) */
|
|
void affect_update(void)
|
|
{
|
|
struct affected_type *af, *next;
|
|
struct char_data *i;
|
|
|
|
for (i = character_list; i; i = i->next)
|
|
for (af = i->affected; af; af = next) {
|
|
next = 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 <= MAX_SPELLS))
|
|
if (!af->next || (af->next->type != af->type) ||
|
|
(af->next->duration > 0))
|
|
if (spell_info[af->type].wear_off_msg)
|
|
send_to_char(i, "%s\r\n", spell_info[af->type].wear_off_msg);
|
|
affect_remove(i, af);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* mag_materials:
|
|
* Checks for up to 3 vnums (spell reagents) in the player's inventory.
|
|
*
|
|
* No spells implemented in Circle use mag_materials, but you can use
|
|
* it to implement your own spells which require ingredients (i.e., some
|
|
* heal spell which requires a rare herb or some such.)
|
|
*/
|
|
int mag_materials(struct char_data *ch, int item0, int item1, int item2,
|
|
int extract, int verbose)
|
|
{
|
|
struct obj_data *tobj;
|
|
struct obj_data *obj0 = NULL, *obj1 = NULL, *obj2 = NULL;
|
|
|
|
for (tobj = ch->carrying; tobj; tobj = tobj->next_content) {
|
|
if ((item0 > 0) && (GET_OBJ_VNUM(tobj) == item0)) {
|
|
obj0 = tobj;
|
|
item0 = -1;
|
|
} else if ((item1 > 0) && (GET_OBJ_VNUM(tobj) == item1)) {
|
|
obj1 = tobj;
|
|
item1 = -1;
|
|
} else if ((item2 > 0) && (GET_OBJ_VNUM(tobj) == item2)) {
|
|
obj2 = tobj;
|
|
item2 = -1;
|
|
}
|
|
}
|
|
if ((item0 > 0) || (item1 > 0) || (item2 > 0)) {
|
|
if (verbose) {
|
|
switch (rand_number(0, 2)) {
|
|
case 0:
|
|
send_to_char(ch, "A wart sprouts on your nose.\r\n");
|
|
break;
|
|
case 1:
|
|
send_to_char(ch, "Your hair falls out in clumps.\r\n");
|
|
break;
|
|
case 2:
|
|
send_to_char(ch, "A huge corn develops on your big toe.\r\n");
|
|
break;
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
if (extract) {
|
|
if (item0 < 0)
|
|
extract_obj(obj0);
|
|
if (item1 < 0)
|
|
extract_obj(obj1);
|
|
if (item2 < 0)
|
|
extract_obj(obj2);
|
|
}
|
|
if (verbose) {
|
|
send_to_char(ch, "A puff of smoke rises from your pack.\r\n");
|
|
act("A puff of smoke rises from $n's pack.", TRUE, ch, NULL, NULL, TO_ROOM);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Every spell that does damage comes through here. This calculates the
|
|
* amount of damage, adds in any modifiers, determines what the saves are,
|
|
* tests for save and calls damage().
|
|
*
|
|
* -1 = dead, otherwise the amount of damage done.
|
|
*/
|
|
int mag_damage(int level, struct char_data *ch, struct char_data *victim,
|
|
int spellnum, int savetype)
|
|
{
|
|
int dam = 0;
|
|
|
|
if (victim == NULL || ch == NULL)
|
|
return (0);
|
|
|
|
switch (spellnum) {
|
|
/* Mostly mages */
|
|
case SPELL_MAGIC_MISSILE:
|
|
case SPELL_CHILL_TOUCH: /* chill touch also has an affect */
|
|
if (IS_MAGIC_USER(ch))
|
|
dam = dice(1, 8) + 1;
|
|
else
|
|
dam = dice(1, 6) + 1;
|
|
break;
|
|
case SPELL_BURNING_HANDS:
|
|
if (IS_MAGIC_USER(ch))
|
|
dam = dice(3, 8) + 3;
|
|
else
|
|
dam = dice(3, 6) + 3;
|
|
break;
|
|
case SPELL_SHOCKING_GRASP:
|
|
if (IS_MAGIC_USER(ch))
|
|
dam = dice(5, 8) + 5;
|
|
else
|
|
dam = dice(5, 6) + 5;
|
|
break;
|
|
case SPELL_LIGHTNING_BOLT:
|
|
if (IS_MAGIC_USER(ch))
|
|
dam = dice(7, 8) + 7;
|
|
else
|
|
dam = dice(7, 6) + 7;
|
|
break;
|
|
case SPELL_COLOR_SPRAY:
|
|
if (IS_MAGIC_USER(ch))
|
|
dam = dice(9, 8) + 9;
|
|
else
|
|
dam = dice(9, 6) + 9;
|
|
break;
|
|
case SPELL_FIREBALL:
|
|
if (IS_MAGIC_USER(ch))
|
|
dam = dice(11, 8) + 11;
|
|
else
|
|
dam = dice(11, 6) + 11;
|
|
break;
|
|
|
|
/* Mostly clerics */
|
|
case SPELL_DISPEL_EVIL:
|
|
dam = dice(6, 8) + 6;
|
|
if (IS_EVIL(ch)) {
|
|
victim = ch;
|
|
dam = GET_HIT(ch) - 1;
|
|
} else if (IS_GOOD(victim)) {
|
|
act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR);
|
|
return (0);
|
|
}
|
|
break;
|
|
case SPELL_DISPEL_GOOD:
|
|
dam = dice(6, 8) + 6;
|
|
if (IS_GOOD(ch)) {
|
|
victim = ch;
|
|
dam = GET_HIT(ch) - 1;
|
|
} else if (IS_EVIL(victim)) {
|
|
act("The gods protect $N.", FALSE, ch, 0, victim, TO_CHAR);
|
|
return (0);
|
|
}
|
|
break;
|
|
|
|
|
|
case SPELL_CALL_LIGHTNING:
|
|
dam = dice(7, 8) + 7;
|
|
break;
|
|
|
|
case SPELL_HARM:
|
|
dam = dice(8, 8) + 8;
|
|
break;
|
|
|
|
case SPELL_ENERGY_DRAIN:
|
|
if (GET_LEVEL(victim) <= 2)
|
|
dam = 100;
|
|
else
|
|
dam = dice(1, 10);
|
|
break;
|
|
|
|
/* Area spells */
|
|
case SPELL_EARTHQUAKE:
|
|
dam = dice(2, 8) + level;
|
|
break;
|
|
|
|
} /* switch(spellnum) */
|
|
|
|
|
|
/* divide damage by two if victim makes his saving throw */
|
|
if (mag_savingthrow(victim, savetype, 0))
|
|
dam /= 2;
|
|
|
|
/* and finally, inflict the damage */
|
|
return (damage(ch, victim, dam, spellnum));
|
|
}
|
|
|
|
|
|
/*
|
|
* Every spell that does an affect comes through here. This determines
|
|
* the effect, whether it is added or replacement, whether it is legal or
|
|
* not, etc.
|
|
*
|
|
* affect_join(vict, aff, add_dur, avg_dur, add_mod, avg_mod)
|
|
*/
|
|
|
|
#define MAX_SPELL_AFFECTS 5 /* change if more needed */
|
|
|
|
void mag_affects(int level, struct char_data *ch, struct char_data *victim,
|
|
int spellnum, int savetype)
|
|
{
|
|
struct affected_type af[MAX_SPELL_AFFECTS];
|
|
bool accum_affect = FALSE, accum_duration = FALSE;
|
|
const char *to_vict = NULL, *to_room = NULL;
|
|
int i;
|
|
|
|
|
|
if (victim == NULL || ch == NULL)
|
|
return;
|
|
|
|
for (i = 0; i < MAX_SPELL_AFFECTS; i++) {
|
|
af[i].type = spellnum;
|
|
af[i].bitvector = 0;
|
|
af[i].modifier = 0;
|
|
af[i].location = APPLY_NONE;
|
|
}
|
|
|
|
switch (spellnum) {
|
|
|
|
case SPELL_CHILL_TOUCH:
|
|
af[0].location = APPLY_STR;
|
|
if (mag_savingthrow(victim, savetype, 0))
|
|
af[0].duration = 1;
|
|
else
|
|
af[0].duration = 4;
|
|
af[0].modifier = -1;
|
|
accum_duration = TRUE;
|
|
to_vict = "You feel your strength wither!";
|
|
break;
|
|
|
|
case SPELL_ARMOR:
|
|
af[0].location = APPLY_AC;
|
|
af[0].modifier = -20;
|
|
af[0].duration = 24;
|
|
accum_duration = TRUE;
|
|
to_vict = "You feel someone protecting you.";
|
|
break;
|
|
|
|
case SPELL_BLESS:
|
|
af[0].location = APPLY_HITROLL;
|
|
af[0].modifier = 2;
|
|
af[0].duration = 6;
|
|
|
|
af[1].location = APPLY_SAVING_SPELL;
|
|
af[1].modifier = -1;
|
|
af[1].duration = 6;
|
|
|
|
accum_duration = TRUE;
|
|
to_vict = "You feel righteous.";
|
|
break;
|
|
|
|
case SPELL_BLINDNESS:
|
|
if (MOB_FLAGGED(victim,MOB_NOBLIND) || mag_savingthrow(victim, savetype, 0)) {
|
|
send_to_char(ch, "You fail.\r\n");
|
|
return;
|
|
}
|
|
|
|
af[0].location = APPLY_HITROLL;
|
|
af[0].modifier = -4;
|
|
af[0].duration = 2;
|
|
af[0].bitvector = AFF_BLIND;
|
|
|
|
af[1].location = APPLY_AC;
|
|
af[1].modifier = 40;
|
|
af[1].duration = 2;
|
|
af[1].bitvector = AFF_BLIND;
|
|
|
|
to_room = "$n seems to be blinded!";
|
|
to_vict = "You have been blinded!";
|
|
break;
|
|
|
|
case SPELL_CURSE:
|
|
if (mag_savingthrow(victim, savetype, 0)) {
|
|
send_to_char(ch, "%s", NOEFFECT);
|
|
return;
|
|
}
|
|
|
|
af[0].location = APPLY_HITROLL;
|
|
af[0].duration = 1 + (GET_LEVEL(ch) / 2);
|
|
af[0].modifier = -1;
|
|
af[0].bitvector = AFF_CURSE;
|
|
|
|
af[1].location = APPLY_DAMROLL;
|
|
af[1].duration = 1 + (GET_LEVEL(ch) / 2);
|
|
af[1].modifier = -1;
|
|
af[1].bitvector = AFF_CURSE;
|
|
|
|
accum_duration = TRUE;
|
|
accum_affect = TRUE;
|
|
to_room = "$n briefly glows red!";
|
|
to_vict = "You feel very uncomfortable.";
|
|
break;
|
|
|
|
case SPELL_DETECT_ALIGN:
|
|
af[0].duration = 12 + level;
|
|
af[0].bitvector = AFF_DETECT_ALIGN;
|
|
accum_duration = TRUE;
|
|
to_vict = "Your eyes tingle.";
|
|
break;
|
|
|
|
case SPELL_DETECT_INVIS:
|
|
af[0].duration = 12 + level;
|
|
af[0].bitvector = AFF_DETECT_INVIS;
|
|
accum_duration = TRUE;
|
|
to_vict = "Your eyes tingle.";
|
|
break;
|
|
|
|
case SPELL_DETECT_MAGIC:
|
|
af[0].duration = 12 + level;
|
|
af[0].bitvector = AFF_DETECT_MAGIC;
|
|
accum_duration = TRUE;
|
|
to_vict = "Your eyes tingle.";
|
|
break;
|
|
|
|
case SPELL_INFRAVISION:
|
|
af[0].duration = 12 + level;
|
|
af[0].bitvector = AFF_INFRAVISION;
|
|
accum_duration = TRUE;
|
|
to_vict = "Your eyes glow red.";
|
|
to_room = "$n's eyes glow red.";
|
|
break;
|
|
|
|
case SPELL_INVISIBLE:
|
|
if (!victim)
|
|
victim = ch;
|
|
|
|
af[0].duration = 12 + (GET_LEVEL(ch) / 4);
|
|
af[0].modifier = -40;
|
|
af[0].location = APPLY_AC;
|
|
af[0].bitvector = AFF_INVISIBLE;
|
|
accum_duration = TRUE;
|
|
to_vict = "You vanish.";
|
|
to_room = "$n slowly fades out of existence.";
|
|
break;
|
|
|
|
case SPELL_POISON:
|
|
if (mag_savingthrow(victim, savetype, 0)) {
|
|
send_to_char(ch, "%s", NOEFFECT);
|
|
return;
|
|
}
|
|
|
|
af[0].location = APPLY_STR;
|
|
af[0].duration = GET_LEVEL(ch);
|
|
af[0].modifier = -2;
|
|
af[0].bitvector = AFF_POISON;
|
|
to_vict = "You feel very sick.";
|
|
to_room = "$n gets violently ill!";
|
|
break;
|
|
|
|
case SPELL_PROT_FROM_EVIL:
|
|
af[0].duration = 24;
|
|
af[0].bitvector = AFF_PROTECT_EVIL;
|
|
accum_duration = TRUE;
|
|
to_vict = "You feel invulnerable!";
|
|
break;
|
|
|
|
case SPELL_SANCTUARY:
|
|
af[0].duration = 4;
|
|
af[0].bitvector = AFF_SANCTUARY;
|
|
|
|
accum_duration = TRUE;
|
|
to_vict = "A white aura momentarily surrounds you.";
|
|
to_room = "$n is surrounded by a white aura.";
|
|
break;
|
|
|
|
case SPELL_SLEEP:
|
|
if (!pk_allowed && !IS_NPC(ch) && !IS_NPC(victim))
|
|
return;
|
|
if (MOB_FLAGGED(victim, MOB_NOSLEEP))
|
|
return;
|
|
if (mag_savingthrow(victim, savetype, 0))
|
|
return;
|
|
|
|
af[0].duration = 4 + (GET_LEVEL(ch) / 4);
|
|
af[0].bitvector = AFF_SLEEP;
|
|
|
|
if (GET_POS(victim) > POS_SLEEPING) {
|
|
send_to_char(victim, "You feel very sleepy... Zzzz......\r\n");
|
|
act("$n goes to sleep.", TRUE, victim, 0, 0, TO_ROOM);
|
|
GET_POS(victim) = POS_SLEEPING;
|
|
}
|
|
break;
|
|
|
|
case SPELL_STRENGTH:
|
|
if (GET_ADD(victim) == 100)
|
|
return;
|
|
|
|
af[0].location = APPLY_STR;
|
|
af[0].duration = (GET_LEVEL(ch) / 2) + 4;
|
|
af[0].modifier = 1 + (level > 18);
|
|
accum_duration = TRUE;
|
|
accum_affect = TRUE;
|
|
to_vict = "You feel stronger!";
|
|
break;
|
|
|
|
case SPELL_SENSE_LIFE:
|
|
to_vict = "Your feel your awareness improve.";
|
|
af[0].duration = GET_LEVEL(ch);
|
|
af[0].bitvector = AFF_SENSE_LIFE;
|
|
accum_duration = TRUE;
|
|
break;
|
|
|
|
case SPELL_WATERWALK:
|
|
af[0].duration = 24;
|
|
af[0].bitvector = AFF_WATERWALK;
|
|
accum_duration = TRUE;
|
|
to_vict = "You feel webbing between your toes.";
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If this is a mob that has this affect set in its mob file, do not
|
|
* perform the affect. This prevents people from un-sancting mobs
|
|
* by sancting them and waiting for it to fade, for example.
|
|
*/
|
|
if (IS_NPC(victim) && !affected_by_spell(victim, spellnum))
|
|
for (i = 0; i < MAX_SPELL_AFFECTS; i++)
|
|
if (AFF_FLAGGED(victim, af[i].bitvector)) {
|
|
send_to_char(ch, "%s", NOEFFECT);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the victim is already affected by this spell, and the spell does
|
|
* not have an accumulative effect, then fail the spell.
|
|
*/
|
|
if (affected_by_spell(victim,spellnum) && !(accum_duration||accum_affect)) {
|
|
send_to_char(ch, "%s", NOEFFECT);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < MAX_SPELL_AFFECTS; i++)
|
|
if (af[i].bitvector || (af[i].location != APPLY_NONE))
|
|
affect_join(victim, af+i, accum_duration, FALSE, accum_affect, FALSE);
|
|
|
|
if (to_vict != NULL)
|
|
act(to_vict, FALSE, victim, 0, ch, TO_CHAR);
|
|
if (to_room != NULL)
|
|
act(to_room, TRUE, victim, 0, ch, TO_ROOM);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is used to provide services to mag_groups. This function
|
|
* is the one you should change to add new group spells.
|
|
*/
|
|
void perform_mag_groups(int level, struct char_data *ch,
|
|
struct char_data *tch, int spellnum, int savetype)
|
|
{
|
|
switch (spellnum) {
|
|
case SPELL_GROUP_HEAL:
|
|
mag_points(level, ch, tch, SPELL_HEAL, savetype);
|
|
break;
|
|
case SPELL_GROUP_ARMOR:
|
|
mag_affects(level, ch, tch, SPELL_ARMOR, savetype);
|
|
break;
|
|
case SPELL_GROUP_RECALL:
|
|
spell_recall(level, ch, tch, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Every spell that affects the group should run through here
|
|
* perform_mag_groups contains the switch statement to send us to the right
|
|
* magic.
|
|
*
|
|
* group spells affect everyone grouped with the caster who is in the room,
|
|
* caster last.
|
|
*
|
|
* To add new group spells, you shouldn't have to change anything in
|
|
* mag_groups -- just add a new case to perform_mag_groups.
|
|
*/
|
|
void mag_groups(int level, struct char_data *ch, int spellnum, int savetype)
|
|
{
|
|
struct char_data *tch, *k;
|
|
struct follow_type *f, *f_next;
|
|
|
|
if (ch == NULL)
|
|
return;
|
|
|
|
if (!AFF_FLAGGED(ch, AFF_GROUP))
|
|
return;
|
|
if (ch->master != NULL)
|
|
k = ch->master;
|
|
else
|
|
k = ch;
|
|
for (f = k->followers; f; f = f_next) {
|
|
f_next = f->next;
|
|
tch = f->follower;
|
|
if (IN_ROOM(tch) != IN_ROOM(ch))
|
|
continue;
|
|
if (!AFF_FLAGGED(tch, AFF_GROUP))
|
|
continue;
|
|
if (ch == tch)
|
|
continue;
|
|
perform_mag_groups(level, ch, tch, spellnum, savetype);
|
|
}
|
|
|
|
if ((k != ch) && AFF_FLAGGED(k, AFF_GROUP))
|
|
perform_mag_groups(level, ch, k, spellnum, savetype);
|
|
perform_mag_groups(level, ch, ch, spellnum, savetype);
|
|
}
|
|
|
|
|
|
/*
|
|
* mass spells affect every creature in the room except the caster.
|
|
*
|
|
* No spells of this class currently implemented.
|
|
*/
|
|
void mag_masses(int level, struct char_data *ch, int spellnum, int savetype)
|
|
{
|
|
struct char_data *tch, *tch_next;
|
|
|
|
for (tch = world[IN_ROOM(ch)].people; tch; tch = tch_next) {
|
|
tch_next = tch->next_in_room;
|
|
if (tch == ch)
|
|
continue;
|
|
|
|
switch (spellnum) {
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Every spell that affects an area (room) runs through here. These are
|
|
* generally offensive spells. This calls mag_damage to do the actual
|
|
* damage -- all spells listed here must also have a case in mag_damage()
|
|
* in order for them to work.
|
|
*
|
|
* area spells have limited targets within the room.
|
|
*/
|
|
void mag_areas(int level, struct char_data *ch, int spellnum, int savetype)
|
|
{
|
|
struct char_data *tch, *next_tch;
|
|
const char *to_char = NULL, *to_room = NULL;
|
|
|
|
if (ch == NULL)
|
|
return;
|
|
|
|
/*
|
|
* to add spells to this fn, just add the message here plus an entry
|
|
* in mag_damage for the damaging part of the spell.
|
|
*/
|
|
switch (spellnum) {
|
|
case SPELL_EARTHQUAKE:
|
|
to_char = "You gesture and the earth begins to shake all around you!";
|
|
to_room ="$n gracefully gestures and the earth begins to shake violently!";
|
|
break;
|
|
}
|
|
|
|
if (to_char != NULL)
|
|
act(to_char, FALSE, ch, 0, 0, TO_CHAR);
|
|
if (to_room != NULL)
|
|
act(to_room, FALSE, ch, 0, 0, TO_ROOM);
|
|
|
|
|
|
for (tch = world[IN_ROOM(ch)].people; tch; tch = next_tch) {
|
|
next_tch = tch->next_in_room;
|
|
|
|
/*
|
|
* The skips: 1: the caster
|
|
* 2: immortals
|
|
* 3: if no pk on this mud, skips over all players
|
|
* 4: pets (charmed NPCs)
|
|
*/
|
|
|
|
if (tch == ch)
|
|
continue;
|
|
if (!IS_NPC(tch) && GET_LEVEL(tch) >= LVL_IMMORT)
|
|
continue;
|
|
if (!pk_allowed && !IS_NPC(ch) && !IS_NPC(tch))
|
|
continue;
|
|
if (!IS_NPC(ch) && IS_NPC(tch) && AFF_FLAGGED(tch, AFF_CHARM))
|
|
continue;
|
|
|
|
/* Doesn't matter if they die here so we don't check. -gg 6/24/98 */
|
|
mag_damage(level, ch, tch, spellnum, 1);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Every spell which summons/gates/conjours a mob comes through here.
|
|
*
|
|
* None of these spells are currently implemented in CircleMUD; these
|
|
* were taken as examples from the JediMUD code. Summons can be used
|
|
* for spells like clone, ariel servant, etc.
|
|
*
|
|
* 10/15/97 (gg) - Implemented Animate Dead and Clone.
|
|
*/
|
|
|
|
/*
|
|
* These use act(), don't put the \r\n.
|
|
*/
|
|
const char *mag_summon_msgs[] = {
|
|
"\r\n",
|
|
"$n makes a strange magical gesture; you feel a strong breeze!",
|
|
"$n animates a corpse!",
|
|
"$N appears from a cloud of thick blue smoke!",
|
|
"$N appears from a cloud of thick green smoke!",
|
|
"$N appears from a cloud of thick red smoke!",
|
|
"$N disappears in a thick black cloud!"
|
|
"As $n makes a strange magical gesture, you feel a strong breeze.",
|
|
"As $n makes a strange magical gesture, you feel a searing heat.",
|
|
"As $n makes a strange magical gesture, you feel a sudden chill.",
|
|
"As $n makes a strange magical gesture, you feel the dust swirl.",
|
|
"$n magically divides!",
|
|
"$n animates a corpse!"
|
|
};
|
|
|
|
/*
|
|
* Keep the \r\n because these use send_to_char.
|
|
*/
|
|
const char *mag_summon_fail_msgs[] = {
|
|
"\r\n",
|
|
"There are no such creatures.\r\n",
|
|
"Uh oh...\r\n",
|
|
"Oh dear.\r\n",
|
|
"Gosh durnit!\r\n",
|
|
"The elements resist!\r\n",
|
|
"You failed.\r\n",
|
|
"There is no corpse!\r\n"
|
|
};
|
|
|
|
/* These mobiles do not exist. */
|
|
#define MOB_MONSUM_I 130
|
|
#define MOB_MONSUM_II 140
|
|
#define MOB_MONSUM_III 150
|
|
#define MOB_GATE_I 160
|
|
#define MOB_GATE_II 170
|
|
#define MOB_GATE_III 180
|
|
|
|
/* Defined mobiles. */
|
|
#define MOB_ELEMENTAL_BASE 20 /* Only one for now. */
|
|
#define MOB_CLONE 10
|
|
#define MOB_ZOMBIE 11
|
|
#define MOB_AERIALSERVANT 19
|
|
|
|
|
|
void mag_summons(int level, struct char_data *ch, struct obj_data *obj,
|
|
int spellnum, int savetype)
|
|
{
|
|
struct char_data *mob = NULL;
|
|
struct obj_data *tobj, *next_obj;
|
|
int pfail = 0, msg = 0, fmsg = 0, num = 1, handle_corpse = FALSE, i;
|
|
mob_vnum mob_num;
|
|
|
|
if (ch == NULL)
|
|
return;
|
|
|
|
switch (spellnum) {
|
|
case SPELL_CLONE:
|
|
msg = 10;
|
|
fmsg = rand_number(2, 6); /* Random fail message. */
|
|
mob_num = MOB_CLONE;
|
|
pfail = 50; /* 50% failure, should be based on something later. */
|
|
break;
|
|
|
|
case SPELL_ANIMATE_DEAD:
|
|
if (obj == NULL || !IS_CORPSE(obj)) {
|
|
act(mag_summon_fail_msgs[7], FALSE, ch, 0, 0, TO_CHAR);
|
|
return;
|
|
}
|
|
handle_corpse = TRUE;
|
|
msg = 11;
|
|
fmsg = rand_number(2, 6); /* Random fail message. */
|
|
mob_num = MOB_ZOMBIE;
|
|
pfail = 10; /* 10% failure, should vary in the future. */
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (AFF_FLAGGED(ch, AFF_CHARM)) {
|
|
send_to_char(ch, "You are too giddy to have any followers!\r\n");
|
|
return;
|
|
}
|
|
if (rand_number(0, 101) < pfail) {
|
|
send_to_char(ch, "%s", mag_summon_fail_msgs[fmsg]);
|
|
return;
|
|
}
|
|
for (i = 0; i < num; i++) {
|
|
if (!(mob = read_mobile(mob_num, VIRTUAL))) {
|
|
send_to_char(ch, "You don't quite remember how to make that creature.\r\n");
|
|
return;
|
|
}
|
|
char_to_room(mob, IN_ROOM(ch));
|
|
IS_CARRYING_W(mob) = 0;
|
|
IS_CARRYING_N(mob) = 0;
|
|
SET_BIT(AFF_FLAGS(mob), AFF_CHARM);
|
|
if (spellnum == SPELL_CLONE) {
|
|
/* Don't mess up the prototype; use new string copies. */
|
|
mob->player.name = strdup(GET_NAME(ch));
|
|
mob->player.short_descr = strdup(GET_NAME(ch));
|
|
}
|
|
act(mag_summon_msgs[msg], FALSE, ch, 0, mob, TO_ROOM);
|
|
add_follower(mob, ch);
|
|
}
|
|
if (handle_corpse) {
|
|
for (tobj = obj->contains; tobj; tobj = next_obj) {
|
|
next_obj = tobj->next_content;
|
|
obj_from_obj(tobj);
|
|
obj_to_char(tobj, mob);
|
|
}
|
|
extract_obj(obj);
|
|
}
|
|
}
|
|
|
|
|
|
void mag_points(int level, struct char_data *ch, struct char_data *victim,
|
|
int spellnum, int savetype)
|
|
{
|
|
int healing = 0, move = 0;
|
|
|
|
if (victim == NULL)
|
|
return;
|
|
|
|
switch (spellnum) {
|
|
case SPELL_CURE_LIGHT:
|
|
healing = dice(1, 8) + 1 + (level / 4);
|
|
send_to_char(victim, "You feel better.\r\n");
|
|
break;
|
|
case SPELL_CURE_CRITIC:
|
|
healing = dice(3, 8) + 3 + (level / 4);
|
|
send_to_char(victim, "You feel a lot better!\r\n");
|
|
break;
|
|
case SPELL_HEAL:
|
|
healing = 100 + dice(3, 8);
|
|
send_to_char(victim, "A warm feeling floods your body.\r\n");
|
|
break;
|
|
}
|
|
GET_HIT(victim) = MIN(GET_MAX_HIT(victim), GET_HIT(victim) + healing);
|
|
GET_MOVE(victim) = MIN(GET_MAX_MOVE(victim), GET_MOVE(victim) + move);
|
|
update_pos(victim);
|
|
}
|
|
|
|
|
|
void mag_unaffects(int level, struct char_data *ch, struct char_data *victim,
|
|
int spellnum, int type)
|
|
{
|
|
int spell = 0, msg_not_affected = TRUE;
|
|
const char *to_vict = NULL, *to_room = NULL;
|
|
|
|
if (victim == NULL)
|
|
return;
|
|
|
|
switch (spellnum) {
|
|
case SPELL_HEAL:
|
|
/*
|
|
* Heal also restores health, so don't give the "no effect" message
|
|
* if the target isn't afflicted by the 'blindness' spell.
|
|
*/
|
|
msg_not_affected = FALSE;
|
|
/* fall-through */
|
|
case SPELL_CURE_BLIND:
|
|
spell = SPELL_BLINDNESS;
|
|
to_vict = "Your vision returns!";
|
|
to_room = "There's a momentary gleam in $n's eyes.";
|
|
break;
|
|
case SPELL_REMOVE_POISON:
|
|
spell = SPELL_POISON;
|
|
to_vict = "A warm feeling runs through your body!";
|
|
to_room = "$n looks better.";
|
|
break;
|
|
case SPELL_REMOVE_CURSE:
|
|
spell = SPELL_CURSE;
|
|
to_vict = "You don't feel so unlucky.";
|
|
break;
|
|
default:
|
|
log("SYSERR: unknown spellnum %d passed to mag_unaffects.", spellnum);
|
|
return;
|
|
}
|
|
|
|
if (!affected_by_spell(victim, spell)) {
|
|
if (msg_not_affected)
|
|
send_to_char(ch, "%s", NOEFFECT);
|
|
return;
|
|
}
|
|
|
|
affect_from_char(victim, spell);
|
|
if (to_vict != NULL)
|
|
act(to_vict, FALSE, victim, 0, ch, TO_CHAR);
|
|
if (to_room != NULL)
|
|
act(to_room, TRUE, victim, 0, ch, TO_ROOM);
|
|
|
|
}
|
|
|
|
|
|
void mag_alter_objs(int level, struct char_data *ch, struct obj_data *obj,
|
|
int spellnum, int savetype)
|
|
{
|
|
const char *to_char = NULL, *to_room = NULL;
|
|
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
switch (spellnum) {
|
|
case SPELL_BLESS:
|
|
if (!OBJ_FLAGGED(obj, ITEM_BLESS) &&
|
|
(GET_OBJ_WEIGHT(obj) <= 5 * GET_LEVEL(ch))) {
|
|
SET_BIT(GET_OBJ_EXTRA(obj), ITEM_BLESS);
|
|
to_char = "$p glows briefly.";
|
|
}
|
|
break;
|
|
case SPELL_CURSE:
|
|
if (!OBJ_FLAGGED(obj, ITEM_NODROP)) {
|
|
SET_BIT(GET_OBJ_EXTRA(obj), ITEM_NODROP);
|
|
if (GET_OBJ_TYPE(obj) == ITEM_WEAPON)
|
|
GET_OBJ_VAL(obj, 2)--;
|
|
to_char = "$p briefly glows red.";
|
|
}
|
|
break;
|
|
case SPELL_INVISIBLE:
|
|
if (!OBJ_FLAGGED(obj, ITEM_NOINVIS | ITEM_INVISIBLE)) {
|
|
SET_BIT(GET_OBJ_EXTRA(obj), ITEM_INVISIBLE);
|
|
to_char = "$p vanishes.";
|
|
}
|
|
break;
|
|
case SPELL_POISON:
|
|
if (((GET_OBJ_TYPE(obj) == ITEM_DRINKCON) ||
|
|
(GET_OBJ_TYPE(obj) == ITEM_FOUNTAIN) ||
|
|
(GET_OBJ_TYPE(obj) == ITEM_FOOD)) && !GET_OBJ_VAL(obj, 3)) {
|
|
GET_OBJ_VAL(obj, 3) = 1;
|
|
to_char = "$p steams briefly.";
|
|
}
|
|
break;
|
|
case SPELL_REMOVE_CURSE:
|
|
if (OBJ_FLAGGED(obj, ITEM_NODROP)) {
|
|
REMOVE_BIT(GET_OBJ_EXTRA(obj), ITEM_NODROP);
|
|
if (GET_OBJ_TYPE(obj) == ITEM_WEAPON)
|
|
GET_OBJ_VAL(obj, 2)++;
|
|
to_char = "$p briefly glows blue.";
|
|
}
|
|
break;
|
|
case SPELL_REMOVE_POISON:
|
|
if (((GET_OBJ_TYPE(obj) == ITEM_DRINKCON) ||
|
|
(GET_OBJ_TYPE(obj) == ITEM_FOUNTAIN) ||
|
|
(GET_OBJ_TYPE(obj) == ITEM_FOOD)) && GET_OBJ_VAL(obj, 3)) {
|
|
GET_OBJ_VAL(obj, 3) = 0;
|
|
to_char = "$p steams briefly.";
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (to_char == NULL)
|
|
send_to_char(ch, "%s", NOEFFECT);
|
|
else
|
|
act(to_char, TRUE, ch, obj, 0, TO_CHAR);
|
|
|
|
if (to_room != NULL)
|
|
act(to_room, TRUE, ch, obj, 0, TO_ROOM);
|
|
else if (to_char != NULL)
|
|
act(to_char, TRUE, ch, obj, 0, TO_ROOM);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mag_creations(int level, struct char_data *ch, int spellnum)
|
|
{
|
|
struct obj_data *tobj;
|
|
obj_vnum z;
|
|
|
|
if (ch == NULL)
|
|
return;
|
|
/* level = MAX(MIN(level, LVL_IMPL), 1); - Hm, not used. */
|
|
|
|
switch (spellnum) {
|
|
case SPELL_CREATE_FOOD:
|
|
z = 10;
|
|
break;
|
|
default:
|
|
send_to_char(ch, "Spell unimplemented, it would seem.\r\n");
|
|
return;
|
|
}
|
|
|
|
if (!(tobj = read_object(z, VIRTUAL))) {
|
|
send_to_char(ch, "I seem to have goofed.\r\n");
|
|
log("SYSERR: spell_creations, spell %d, obj %d: obj not found",
|
|
spellnum, z);
|
|
return;
|
|
}
|
|
obj_to_char(tobj, ch);
|
|
act("$n creates $p.", FALSE, ch, tobj, 0, TO_ROOM);
|
|
act("You create $p.", FALSE, ch, tobj, 0, TO_CHAR);
|
|
}
|
|
|