mirror of
https://github.com/tbamud/tbamud.git
synced 2026-01-08 18:38:50 +01:00
Adding of the appropropriate structs to the plrtoascii utility. Incoorporation of changes from old circlemud CVS snapshot this also gives more info on SYSERRors. cleanup of zmalloc language (shit->tmp) PRF_ROOMFLAGS has been renamed to PRF_SHOWVNUMS for clarity. "Deaf" has been changed to "No_shout" in preference_bits, for clarity. Addition of hindex (help index) command. who command added argument -g and -l to check who are grouped (and leaders) toggle has been expanded, and the commands nohassle, holylight, color, buildwalk, syslog and roomflags were moved to toggles. renaming of some vars in dg files (xxx -> dg_xxx) for clarity. set <player> password can now be used by other people than the first implementor - id check removed. fix of a couple of minor bugs: - crash bug related to freeing a pointer twice - ascii pfiles bug. - host was not updated correctly after switch to ascii pfiles. Todo: make "who #-#", "who #-" and "who -#" all work as "who -l #-#" did before Remove redundant commands which are now toggles. Make script variables save to pfile instead of its own file.
972 lines
25 KiB
C
972 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"
|
|
#include "dg_scripts.h"
|
|
|
|
/* external variables */
|
|
extern int mini_mud;
|
|
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);
|
|
|
|
/* 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", CONFIG_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", CONFIG_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 (!CONFIG_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", CONFIG_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", CONFIG_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 (!CONFIG_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);
|
|
load_mtrigger(mob);
|
|
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", CONFIG_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", CONFIG_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);
|
|
load_otrigger(tobj);
|
|
}
|
|
|