/**************************************************************************** * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | * * -----------------------------------------------------------| \\._.// * * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) * * -----------------------------------------------------------| ).:.( * * SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} * * -----------------------------------------------------------| / ' ' \ * * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~* * Scryn, Swordbearer, Rennard, Tricops, and Gorog. | * * ------------------------------------------------------------------------ * * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe. * * ------------------------------------------------------------------------ * * Spell handling module * ****************************************************************************/ #include "stdafx.h" #include "smaug.h" #include "SysData.h" #include "skill.h" #include "mobiles.h" #include "objects.h" #include "rooms.h" #include "deity.h" #include "area.h" #include "races.h" #include "class.h" #include "Exits.h" #include "SmaugWizDoc.h" #include "descriptor.h" #include "character.h" // Local functions. void say_spell (CCharacter *ch, int sn); CCharacter *make_poly_mob (CCharacter *ch, int vnum); ch_ret spell_affect (int sn, int level, CCharacter *ch, void *vo); ch_ret spell_affectchar (int sn, int level, CCharacter *ch, void *vo); BOOL CanMorph (CCharacter& Ch, const char* morph); /* * Is immune to a damage type */ BOOL is_immune (CCharacter *ch, short damtype) { switch (damtype) { case SD_FIRE: if (ch->IsImmuneFire ()) return TRUE; case SD_COLD: if (ch->IsImmuneCold ()) return TRUE; case SD_ELECTRICITY: if (ch->IsImmuneElectricity ()) return TRUE; case SD_ENERGY: if (ch->IsImmuneEnergy ()) return TRUE; case SD_ACID: if (ch->IsImmuneAcid ()) return TRUE; case SD_POISON: if (ch->IsImmunePoison ()) return TRUE; case SD_DRAIN: if (ch->IsImmuneEnergyDrain ()) return TRUE; } return FALSE; } // Lookup a skill by name, only stopping at skills the player has. int ch_slookup (CCharacter *ch, const char *name) { if (ch->IsNpc ()) return SkillTable.Lookup (name); for (int sn=0; sn < SkillTable.GetCount (); sn++) { if (! SkillTable.GetName (sn)) break; if (ch->GetPcData ()->learned[sn] > 0 && ch->GetLevel () >= SkillTable.GetClassLevel (sn, ch->GetClass ()) && LOWER (name [0]) == LOWER (SkillTable.GetName (sn) [0]) && ! str_prefix (name, SkillTable.GetName (sn))) return sn; } return -1; } // Lookup a personal skill int personal_lookup (CCharacter *ch, const char *name) { return -1; } int find_spell (CCharacter *ch, const char *name, BOOL know) { if (ch->IsNpc () || ! know) return SkillTable.Bsearch (name, gsn_first_spell, gsn_first_skill-1); else return SkillTable.ChBsearch (ch, name, gsn_first_spell, gsn_first_skill - 1); } int find_skill (CCharacter *ch, const char *name, BOOL know) { if (ch->IsNpc () || ! know) return SkillTable.Bsearch (name, gsn_first_skill, gsn_first_weapon-1); else return SkillTable.ChBsearch (ch, name, gsn_first_skill, gsn_first_weapon-1); } int find_weapon (CCharacter *ch, const char *name, BOOL know) { if (ch->IsNpc () || ! know) return SkillTable.Bsearch (name, gsn_first_weapon, gsn_first_tongue-1); else return SkillTable.ChBsearch (ch, name, gsn_first_weapon, gsn_first_tongue-1); } int find_tongue (CCharacter *ch, const char *name, BOOL know) { if (ch->IsNpc () || ! know) return SkillTable.Bsearch (name, gsn_first_tongue, gsn_top_sn-1); else return SkillTable.ChBsearch (ch, name, gsn_first_tongue, gsn_top_sn-1); } // Lookup a skill by slot number. // Used for object loading. int slot_lookup (int slot) { extern BOOL fBootDb; int sn; if (slot <= 0) return -1; for (sn = 0; sn < SkillTable.GetCount (); sn++) if (slot == SkillTable.GetSlot (sn)) return sn; if (fBootDb) { bug ("Slot_lookup: bad slot %d.", slot); abort (); } return -1; } // Fancy message handling for a successful casting -Thoric void successful_casting (CSkill *skill, CCharacter *ch, CCharacter *victim, CObjData *obj) { short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION); short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT); short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME); if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) { chit = chitroom; chitme = chitroom; } if (ch && ch != victim) { if (skill->ValidHitCharMsg ()) act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->GetType () == SKILL_SPELL) act (chit, "Ok.", ch, NULL, NULL, TO_CHAR); } if (ch && skill->ValidHitRoomMsg ()) act (chitroom, skill->GetHitRoomMsg (), ch, obj, victim, TO_NOTVICT); if (ch && victim && skill->ValidHitVictMsg ()) { if (ch != victim) act (chitme, skill->GetHitVictMsg (), ch, obj, victim, TO_VICT); else act (chitme, skill->GetHitVictMsg (), ch, obj, victim, TO_CHAR); } else if (ch && ch == victim && skill->GetType () == SKILL_SPELL) act (chitme, "Ok.", ch, NULL, NULL, TO_CHAR); } // Fancy message handling for a failed casting -Thoric void failed_casting (CSkill *skill, CCharacter *ch, CCharacter *victim, CObjData *obj) { short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION); short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT); short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME); if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) { chit = chitroom; chitme = chitroom; } if (ch && ch != victim) { if (skill->ValidMissCharMsg ()) act (chit, skill->GetMissCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->GetType () == SKILL_SPELL) act (chit, "You failed.", ch, NULL, NULL, TO_CHAR); } if (ch && skill->ValidMissRoomMsg () && str_cmp (skill->GetMissRoomMsg (), "supress")) act (chitroom, skill->GetMissRoomMsg (), ch, obj, victim, TO_NOTVICT); if (ch && victim && skill->ValidMissVictMsg ()) { if (ch != victim) act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_VICT); else act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_CHAR); } else if (ch && ch == victim) { if (skill->ValidMissCharMsg ()) act (chitme, skill->GetMissCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->GetType () == SKILL_SPELL) act (chitme, "You failed.", ch, NULL, NULL, TO_CHAR); } } // Fancy message handling for being immune to something -Thoric void immune_casting (CSkill *skill, CCharacter *ch, CCharacter *victim, CObjData *obj) { short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION); short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT); short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME); if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) { chit = chitroom; chitme = chitroom; } if (ch && ch != victim) { if (skill->ValidImmuneCharMsg ()) act (chit, skill->GetImmuneCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->ValidHitCharMsg ()) act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->GetType () == SKILL_SPELL || skill->GetType () == SKILL_SKILL) act (chit, "That appears to have no effect.", ch, NULL, NULL, TO_CHAR); } if (ch && skill->ValidImmuneRoomMsg ()) act (chitroom, skill->GetImmuneRoomMsg (), ch, obj, victim, TO_NOTVICT); else if (ch && skill->ValidMissRoomMsg ()) act (chitroom, skill->GetMissRoomMsg (), ch, obj, victim, TO_NOTVICT); if (ch && victim && skill->ValidImmuneVictMsg ()) { if (ch != victim) act (chitme, skill->GetImmuneVictMsg (), ch, obj, victim, TO_VICT); else act (chitme, skill->GetImmuneVictMsg (), ch, obj, victim, TO_CHAR); } else if (ch && victim && skill->ValidMissVictMsg ()) { if (ch != victim) act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_VICT); else act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_CHAR); } else if (ch && ch == victim) { if (skill->ValidImmuneCharMsg ()) act (chit, skill->GetImmuneCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->ValidMissCharMsg ()) act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR); else if (skill->GetType () == SKILL_SPELL || skill->GetType () == SKILL_SKILL) act (chit, "That appears to have no affect.", ch, NULL, NULL, TO_CHAR); } } // Utter mystical words for an sn. void say_spell (CCharacter *ch, int sn) { char buf [MAX_STRING_LENGTH]; char buf2 [MAX_STRING_LENGTH]; CCharacter *rch; char *pName; int iSyl; int length; CSkill *skill = SkillTable.GetValidSkill (sn); struct syl_type { char *Old; char *New; }; static const struct syl_type syl_table [] = { { " ", " " }, { "ar", "abra" }, { "au", "kada" }, { "bless", "fido" }, { "blind", "nose" }, { "bur", "mosa" }, { "cu", "judi" }, { "de", "oculo" }, { "en", "unso" }, { "light", "dies" }, { "lo", "hi" }, { "mor", "zak" }, { "move", "sido" }, { "ness", "lacri" }, { "ning", "illa" }, { "per", "duda" }, { "polymorph", "iaddahs" }, { "ra", "gru" }, { "re", "candus" }, { "son", "sabru" }, { "tect", "infra" }, { "tri", "cula" }, { "ven", "nofo" }, { "a", "a" }, { "b", "b" }, { "c", "q" }, { "d", "e" }, { "e", "z" }, { "f", "y" }, { "g", "o" }, { "h", "p" }, { "i", "u" }, { "j", "y" }, { "k", "t" }, { "l", "r" }, { "m", "w" }, { "n", "i" }, { "o", "a" }, { "p", "s" }, { "q", "d" }, { "r", "f" }, { "s", "g" }, { "t", "h" }, { "u", "j" }, { "v", "z" }, { "w", "x" }, { "x", "n" }, { "y", "l" }, { "z", "k" }, { "", "" } }; buf [0] = '\0'; for (pName = skill->GetName (); *pName != 0; pName += length) { for (iSyl=0; (length = strlen (syl_table [iSyl].Old)) != 0; iSyl++) { if (! str_prefix (syl_table [iSyl].Old, pName)) { strcat (buf, syl_table [iSyl].New); break; } } if (length == 0) length = 1; } sprintf (buf2, "$n utters the words, '%s'.", buf); sprintf (buf, "$n utters the words, '%s'.", skill->GetName ()); rch = ch->GetInRoom ()->first_person; for (; rch; rch = rch->GetNextInRoom ()) { if (rch != ch) act (AT_MAGIC, ch->GetClass () == rch->GetClass () ? buf : buf2, ch, NULL, rch, TO_VICT); } } // Make adjustments to saving throw based in RIS -Thoric int ris_save (CCharacter *ch, int chance, int ris) { short modifier; modifier = 10; if (ch->IsImmune (ris)) modifier -= 10; if (ch->CanResist (ris)) modifier -= 2; if (ch->IsSusceptible (ris)) modifier += 2; if (modifier <= 0) return 1000; if (modifier == 10) return chance; return (chance * modifier) / 10; } // Fancy dice expression parsing complete with order of operations, // simple exponent support, dice support as well as a few extra // variables: L = level, H = hp, M = mana, V = move, S = str, X = dex // I = int, W = wis, C = con, A = cha, U = luck, A = age // // Used for spell dice parsing, ie: 3d8+L-6 int rd_parse (CCharacter *ch, int level, char *exp) { int x, lop = 0, gop = 0, eop = 0; char operation; char *sexp [2]; int total = 0, len = 0; // take care of nulls coming in if (! exp || ! strlen (exp)) return 0; // get rid of brackets if they surround the entire expresion // if ((*exp == '(') && !index (exp+1,'(') && exp[strlen (exp)-1] == ')') if ((*exp == '(') && !strchr (exp+1,'(') && exp[strlen (exp)-1] == ')') { exp [strlen (exp)-1] = '\0'; exp++; } // check if the expresion is just a number len = strlen (exp); if (len == 1 && isalpha (exp [0])) switch (exp [0]) { case 'L': case 'l': return level; case 'H': case 'h': return ch->GetHp (); case 'M': case 'm': return ch->GetMana (); case 'V': case 'v': return ch->GetMove (); case 'S': case 's': return ch->GetCurrentStrength (); case 'I': case 'i': return ch->GetIntelligence (); case 'W': case 'w': return ch->GetWisdom (); case 'X': case 'x': return ch->GetDexterity (); case 'C': case 'c': return ch->GetConstitution (); case 'A': case 'a': return get_curr_cha (ch); case 'U': case 'u': return get_curr_lck (ch); case 'Y': case 'y': return ch->GetAge (); } for (x = 0; x < len; ++x) if (! isdigit (exp [x]) && ! isspace (exp [x])) break; if (x == len) return (atoi (exp)); // break it into 2 parts for (x = 0; x < (int) strlen (exp); ++x) switch (exp [x]) { case '^': if (! total) eop = x; break; case '-': case '+': if (!total) lop = x; break; case '*': case '/': case '%': case 'd': case 'D': case '<': case '>': case '{': case '}': case '=': if (!total) gop = x; break; case '(': ++total; break; case ')': --total; break; } if (lop) then x = lop; else if (gop) then x = gop; else x = eop; operation = exp [x]; exp [x] = '\0'; sexp [0] = exp; sexp [1] = (char*) (exp+x+1); // work it out total = rd_parse (ch, level, sexp [0]); switch (operation) { case '-': total -= rd_parse (ch, level, sexp [1]); break; case '+': total += rd_parse (ch, level, sexp [1]); break; case '*': total *= rd_parse (ch, level, sexp [1]); break; case '/': total /= rd_parse (ch, level, sexp [1]); break; case '%': total %= rd_parse (ch, level, sexp [1]); break; case 'd': case 'D': total = dice (total, rd_parse (ch, level, sexp [1])); break; case '<': total = (total < rd_parse (ch, level, sexp [1])); break; case '>': total = (total > rd_parse (ch, level, sexp [1])); break; case '=': total = (total == rd_parse (ch, level, sexp [1])); break; case '{': total = UMIN (total, rd_parse (ch, level, sexp [1])); break; case '}': total = UMAX (total, rd_parse (ch, level, sexp [1])); break; case '^': { int y = rd_parse (ch, level, sexp[1]); int z = total; for (x = 1; x < y; ++x, z *= total); total = z; break; } } return total; } // wrapper function so as not to destroy exp int dice_parse (CCharacter *ch, int level, char *exp) { char buf [MAX_INPUT_LENGTH]; strcpy (buf, exp); return rd_parse (ch, level, buf); } // Compute a saving throw. // Negative apply's make saving throw better. BOOL saves_poison_death (int level, CCharacter *victim) { int save; save = 50 + (victim->GetLevel () - level - victim->saving_poison_death) * 5; save = URANGE (5, save, 95); return chance (victim, save); } BOOL saves_wands (int level, CCharacter *victim) { int save; if (victim->IsImmuneMagic ()) return TRUE; save = 50 + (victim->GetLevel () - level - victim->saving_wand) * 5; save = URANGE (5, save, 95); return chance (victim, save); } BOOL saves_para_petri (int level, CCharacter *victim) { int save; save = 50 + (victim->GetLevel () - level - victim->saving_para_petri) * 5; save = URANGE (5, save, 95); return chance (victim, save); } BOOL saves_breath (int level, CCharacter *victim) { int save; save = 50 + (victim->GetLevel () - level - victim->saving_breath) * 5; save = URANGE (5, save, 95); return chance (victim, save); } BOOL saves_spell_staff (int level, CCharacter *victim) { int save; if (victim->IsImmuneMagic ()) return TRUE; if (victim->IsNpc () && level > 10) level -= 5; save = 50 + (victim->GetLevel () - level - victim->saving_spell_staff) * 5; save = URANGE (5, save, 95); return chance (victim, save); } // Process the spell's required components, if any -Thoric // ----------------------------------------------- // T### check for item of type ### // V##### check for item of vnum ##### // Kword check for item with keyword 'word' // G##### check if player has ##### amount of gold // H#### check if player has #### amount of hitpoints // // Special operators: // ! spell fails if player has this // + don't consume this component // @ decrease component's value[0], and extract if it reaches 0 // # decrease component's value[1], and extract if it reaches 0 // $ decrease component's value[2], and extract if it reaches 0 // % decrease component's value[3], and extract if it reaches 0 // ^ decrease component's value[4], and extract if it reaches 0 // & decrease component's value[5], and extract if it reaches 0 BOOL process_spell_components (CCharacter *ch, int sn) { CSkill *skill = SkillTable.GetValidSkill (sn); char *comp = skill->GetComponents (); char *check; char arg [MAX_INPUT_LENGTH]; BOOL consume, fail, found; int val, value; CObjData *obj; POSITION pos; // if no components necessary, then everything is cool if (! comp || comp [0] == '\0') return TRUE; while (comp [0] != '\0') { comp = one_argument (comp, arg); consume = TRUE; fail = found = FALSE; val = -1; switch (arg [1]) { default: check = arg+1; break; case '!': check = arg+2; fail = TRUE; break; case '+': check = arg+2; consume = FALSE; break; case '@': check = arg+2; val = 0; break; case '#': check = arg+2; val = 1; break; case '$': check = arg+2; val = 2; break; case '%': check = arg+2; val = 3; break; case '^': check = arg+2; val = 4; break; case '&': check = arg+2; val = 5; break; } value = atoi (check); obj = NULL; switch (UPPER (arg [0])) { case 'T': pos = ch->GetHeadCarryPos (); while (obj = ch->GetNextCarrying (pos)) if (obj->item_type == value) { if (fail) { ch->SendText ("Something disrupts the casting of " "this spell...\n\r"); return FALSE; } found = TRUE; break; } break; case 'V': pos = ch->GetHeadCarryPos (); while (obj = ch->GetNextCarrying (pos)) if (obj->pIndexData->vnum == value) { if (fail) { ch->SendText ("Something disrupts the casting of " "this spell...\n\r"); return FALSE; } found = TRUE; break; } break; case 'K': pos = ch->GetHeadCarryPos (); while (obj = ch->GetNextCarrying (pos)) if (nifty_is_name (check, obj->GetName ())) { if (fail) { ch->SendText ("Something disrupts the casting of " "this spell...\n\r"); return FALSE; } found = TRUE; break; } break; case 'G': if (ch->GetGold () >= value) if (fail) { ch->SendText ("Something disrupts the casting of this " "spell...\n\r"); return FALSE; } else { if (consume) { set_char_color (AT_GOLD, ch); ch->SendText ("You feel a little lighter...\n\r"); ch->AddGold (-value); } continue; } break; case 'H': if (ch->GetHp () >= value) if (fail) { ch->SendText ("Something disrupts the casting of this " "spell...\n\r"); return FALSE; } else { if (consume) { set_char_color (AT_BLOOD, ch); ch->SendText ("You feel a little weaker...\n\r"); ch->AddHp (-value); update_pos (ch); } continue; } break; } // having this component would make the spell fail... if we get // here, then the caster didn't have that component if (fail) continue; if (! found) { ch->SendText ("Something is missing...\n\r"); return FALSE; } if (obj) { if (val >=0 && val < 6) { separate_obj (obj); if (obj->value[val] <= 0) { act (AT_MAGIC, "$p disappears in a puff of smoke!", ch, obj, NULL, TO_CHAR ); act (AT_MAGIC, "$p disappears in a puff of smoke!", ch, obj, NULL, TO_ROOM ); extract_obj( obj ); return FALSE; } else if (--obj->value[val] == 0) { act (AT_MAGIC, "$p glows briefly, then disappears in a " "puff of smoke!", ch, obj, NULL, TO_CHAR); act (AT_MAGIC, "$p glows briefly, then disappears in a " "puff of smoke!", ch, obj, NULL, TO_ROOM); extract_obj (obj); } else act (AT_MAGIC, "$p glows briefly and a whisp of smoke " "rises from it.", ch, obj, NULL, TO_CHAR); } else if (consume) { separate_obj (obj); act (AT_MAGIC, "$p glows brightly, then disappears in a " "puff of smoke!", ch, obj, NULL, TO_CHAR); act (AT_MAGIC, "$p glows brightly, then disappears in a " "puff of smoke!", ch, obj, NULL, TO_ROOM); extract_obj (obj); } else { int count = obj->count; obj->count = 1; act (AT_MAGIC, "$p glows briefly.", ch, obj, NULL, TO_CHAR); obj->count = count; } } } return TRUE; } int pAbort; // Locate targets. void *locate_targets (CCharacter *ch, char *arg, int sn, CCharacter **victim, CObjData **obj) { CSkill *skill = SkillTable.GetValidSkill (sn); void *vo = NULL; *victim = NULL; *obj = NULL; switch (skill->GetTarget ()) { default: bug ("Do_cast: bad target for sn %d.", sn); return &pAbort; case TAR_IGNORE: break; case TAR_CHAR_OFFENSIVE: if (arg [0] == '\0') { if ((*victim = ch->GetFightWho ()) == NULL) { ch->SendText ("Cast the spell on whom?\n\r"); return &pAbort; } } else { if ((*victim = get_char_room (ch, arg)) == NULL) { ch->SendText ("They aren't here.\n\r"); return &pAbort; } } if (is_safe (ch, *victim)) return &pAbort; if (ch == *victim) { if (skill->IsNoSelf ()) { ch->SendText ("You can't cast this on yourself!\n\r"); return &pAbort; } ch->SendText ("Cast this on yourself? Okay...\n\r"); } if (! ch->IsNpc ()) { if (! (*victim)->IsNpc ()) { if (get_timer (ch, TIMER_PKILLED) > 0) { ch->SendText ("You have been killed in the last 5 minutes.\n\r"); return &pAbort; } if (get_timer (*victim, TIMER_PKILLED) > 0) { ch->SendText ("This player has been killed in the last 5 minutes.\n\r"); return &pAbort; } if (ch->IsNice () && ch != *victim) { ch->SendText ( "You are too nice to attack another player.\n\r"); return &pAbort; } if (*victim != ch) ch->SendText ( "You really shouldn't do this to another player...\n\r"); } if (ch->IsCharmed () && ch->GetMaster () == *victim) { ch->SendText ("You can't do that on your own follower.\n\r"); return &pAbort; } } check_illegal_pk (ch, *victim); if (ch != *victim) then adjust_favor (ch, 4, 1); vo = (void *) *victim; break; case TAR_CHAR_DEFENSIVE: if (arg [0] == '\0') *victim = ch; else { if ((*victim = get_char_room (ch, arg)) == NULL) { ch->SendText ("They aren't here.\n\r"); return &pAbort; } } if (ch == *victim && SkillTable.GetValidSkill (sn)->IsNoSelf ()) { ch->SendText ("You can't cast this on yourself!\n\r"); return &pAbort; } vo = (void *) *victim; break; case TAR_CHAR_SELF: if (arg [0] != '\0' && ! nifty_is_name (arg, ch->GetName ())) { ch->SendText ("You cannot cast this spell on another.\n\r"); return &pAbort; } vo = (void *) ch; break; case TAR_OBJ_INV: if (arg [0] == '\0') { ch->SendText ("What should the spell be cast upon?\n\r"); return &pAbort; } if ((*obj = get_obj_carry (ch, arg)) == NULL) { ch->SendText ("You are not carrying that.\n\r"); return &pAbort; } vo = (void *) *obj; break; } return vo; } // The kludgy global is for spells who want more stuff from command line. char *target_name; char *ranged_target_name = NULL; // Cast a spell. Multi-caster and component support by Thoric void do_cast (CCharacter *ch, char *argument) { char arg1 [MAX_INPUT_LENGTH]; char arg2 [MAX_INPUT_LENGTH]; static char staticbuf [MAX_INPUT_LENGTH]; CCharacter *victim; CObjData *obj; void *vo = NULL; int mana; int blood; int sn; ch_ret retcode = rNONE; BOOL bDontWait = FALSE; CSkill *skill = NULL; struct timeval time_used; switch (ch->GetSubstate ()) { default: // no ordering charmed mobs to cast spells if (ch->IsNpc () && (ch->IsCharmed () || ch->IsPossessed ())) { ch->SendText ("You can't seem to do that right now...\n\r"); return; } if (ch->GetInRoom ()->IsNoMagic ()) { set_char_color (AT_MAGIC, ch); ch->SendText ("You failed.\n\r"); return; } target_name = one_argument (argument, arg1); one_argument (target_name, arg2); delete ranged_target_name; ranged_target_name = str_dup (target_name); if (arg1 [0] == '\0') { ch->SendText ("Cast which what where?\n\r"); return; } // Regular mortal spell casting if (ch->GetTrustLevel () < LEVEL_GOD) { if ((sn = find_spell (ch, arg1, TRUE)) < 0 || (!ch->IsNpc () && ch->GetLevel () < SkillTable.GetClassLevel (sn, ch->GetClass ()))) { ch->SendText ("You can't do that.\n\r"); return; } if ((skill=SkillTable.GetValidSkill (sn)) == NULL) { ch->SendText ("You can't do that right now...\n\r"); return; } } else { // Godly "spell builder" spell casting with debugging messages if ((sn=SkillTable.Lookup (arg1)) < 0) { ch->SendText ("We didn't create that yet...\n\r"); return; } if (sn >= MAX_SKILL) { ch->SendText ("Hmm... that might hurt.\n\r"); return; } if ((skill=SkillTable.GetValidSkill (sn)) == NULL) { ch->SendText ("Somethis is severely wrong with that one...\n\r"); return; } if (skill->GetType () != SKILL_SPELL) { ch->SendText ("That isn't a spell.\n\r"); return; } if (! skill->GetSpellFunction ()) { ch->SendText ("We didn't finish that one yet...\n\r"); return; } } // Something else removed by Merc -Thoric // Band-aid alert! !IS_NPC check -- Blod if (ch->GetPosition () < skill->GetMinimumPosition () && !ch->IsNpc ()) { switch (ch->GetPosition ()) { default: ch->SendText ("You can't concentrate enough.\n\r"); break; case POS_SITTING: ch->SendText ("You can't summon enough energy sitting down.\n\r"); break; case POS_RESTING: ch->SendText ("You're too relaxed to cast that spell.\n\r"); break; case POS_FIGHTING: case POS_DEFENSIVE: case POS_AGGRESSIVE: case POS_BERSERK: if (skill->GetMinimumPosition () <= POS_EVASIVE) ch->SendText ("This fighting style is too demanding " "for that!\n\r"); else ch->SendText ("You can't concentrate enough while fighting!\n\r"); break; case POS_EVASIVE: ch->SendText ("You can't concentrate enough while fighting!\n\r"); break; case POS_SLEEPING: ch->SendText ("You dream about great feats of magic.\n\r"); break; } return; } if (skill->GetSpellFunction () == spell_null) { ch->SendText ("That's not a spell!\n\r"); return; } if (!skill->GetSpellFunction ()) { ch->SendText ("You cannot cast that... yet.\n\r"); return; } if (! ch->IsNpc () // fixed by Thoric && ! ch->IsImmortal () && skill->GetGuild () != CLASS_NONE && (! ch->GetPcData ()->GetClan () || skill->GetGuild () != ch->GetPcData ()->GetClan ()->m_Class)) { ch->SendText ("That is only available to members of a certain guild.\n\r"); return; } mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (), 100 / (2 + ch->GetLevel () - skill->GetClassLevel (ch->GetClass ()))); // Locate targets. vo = locate_targets (ch, arg2, sn, &victim, &obj); if (vo == &pAbort) return; if (! ch->IsNpc () && victim && ! victim->IsNpc () && victim->CanPkill () && ! ch->CanPkill () && ! in_arena (ch) && ! in_arena (victim)) { set_char_color (AT_MAGIC, ch); ch->SendText ("The gods will not permit you to cast spells on " "that character.\n\r"); return; } // Vampire spell casting -Thoric blood = UMAX (1, (mana+4) / 8); // NPCs don't have PCDatas. -- Altrag if (ch->IsVampire ()) { if (ch->GetPcData ()->condition [COND_BLOODTHIRST] < blood) { ch->SendText ("You don't have enough blood power.\n\r"); return; } } else if (! ch->IsNpc () && ch->GetMana () < mana) { ch->SendText ("You don't have enough mana.\n\r"); return; } if (skill->GetParticipants () <= 1) break; // multi-participant spells -Thoric add_timer (ch, TIMER_DO_FUN, UMIN (skill->GetBeats () / 10, 3), do_cast, 1); act (AT_MAGIC, "You begin to chant...", ch, NULL, NULL, TO_CHAR); act (AT_MAGIC, "$n begins to chant...", ch, NULL, NULL, TO_ROOM); sprintf (staticbuf, "%s %s", arg2, target_name); ch->dest_buf = str_dup (staticbuf); ch->tempnum = sn; return; case SUB_TIMER_DO_ABORT: delete ch->alloc_ptr; ch->alloc_ptr = NULL; if (SkillTable.IsValid ((sn = ch->tempnum))) { if ((skill = SkillTable.GetValidSkill (sn)) == NULL) { ch->SendText ("Something went wrong...\n\r"); bug ("do_cast: SUB_TIMER_DO_ABORT: bad sn %d", sn); return; } mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (), 100 / (2 + ch->GetLevel () - skill->GetClassLevel (ch->GetClass ()))); blood = UMAX (1, (mana+4) / 8); if (ch->IsVampire ()) gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 3)); else if (ch->IsMortal ()) // so imms dont lose mana ch->AddMana (-mana / 3); } set_char_color (AT_MAGIC, ch); ch->SendText ("You stop chanting...\n\r"); // should add chance of backfire here return; case 1: sn = ch->tempnum; if ((skill = SkillTable.GetValidSkill (sn)) == NULL) { ch->SendText ("Something went wrong...\n\r"); bug ("do_cast: substate 1: bad sn %d", sn); return; } if (! ch->alloc_ptr || ! SkillTable.IsValid (sn) || skill->GetType () != SKILL_SPELL) { ch->SendText ("Something cancels out the spell!\n\r"); bug ("do_cast: ch->dest_buf NULL or bad sn (%d)", sn); return; } mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (), 100 / (2 + ch->GetLevel () - skill->GetClassLevel (ch->GetClass ()))); blood = UMAX (1, (mana+4) / 8); strcpy (staticbuf, (char*) ch->alloc_ptr); target_name = one_argument (staticbuf, arg2); delete ch->alloc_ptr; ch->alloc_ptr = NULL; ch->SetSubstate (SUB_NONE); if (skill->GetParticipants () > 1) { int cnt = 1; CTimerData *t; CCharacter *tmp = ch->GetInRoom ()->first_person; for (; tmp; tmp = tmp->GetNextInRoom ()) if (tmp != ch && (t = get_timerptr (tmp, TIMER_DO_FUN)) != NULL && t->GetCount () >= 1 && t->GetDoFun () == do_cast && tmp->tempnum == sn && tmp->alloc_ptr && !str_cmp ((char*)tmp->alloc_ptr, staticbuf)) ++cnt; if (cnt >= skill->GetParticipants ()) { tmp = ch->GetInRoom ()->first_person; for (; tmp; tmp = tmp->GetNextInRoom ()) { if (tmp != ch && (t = get_timerptr (tmp, TIMER_DO_FUN)) != NULL && t->GetCount () >= 1 && t->GetDoFun () == do_cast && tmp->tempnum == sn && tmp->alloc_ptr && !str_cmp ((char*) tmp->alloc_ptr, staticbuf)) { extract_timer (tmp, t); act (AT_MAGIC, "Channeling your energy into $n, you " "help cast the spell!", ch, NULL, tmp, TO_VICT); act (AT_MAGIC, "$N channels $S energy into you!", ch, NULL, tmp, TO_CHAR); act (AT_MAGIC, "$N channels $S energy into $n!", ch, NULL, tmp, TO_NOTVICT); learn_from_success (tmp, sn); if (ch->IsVampire ()) gain_condition (tmp, COND_BLOODTHIRST, - blood); else tmp->AddMana (-mana); tmp->SetSubstate (SUB_NONE); tmp->tempnum = -1; delete tmp->alloc_ptr; tmp->alloc_ptr = NULL; } } bDontWait = TRUE; ch->SendText ("You concentrate all the energy into a burst " "of mystical words!\n\r"); vo = locate_targets (ch, arg2, sn, &victim, &obj); if (vo == &pAbort) return; } else { set_char_color (AT_MAGIC, ch); ch->SendText ("There was not enough power for the spell to " "succeed...\n\r"); if (ch->IsVampire ()) gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2)); else if (ch->IsMortal ()) // so imms dont lose mana ch->AddMana (-mana / 2); learn_from_failure (ch, sn); return; } } } // uttering those magic words unless casting "ventriloquate" if (str_cmp (skill->GetName (), "ventriloquate")) say_spell (ch, sn); if (! bDontWait) WAIT_STATE (ch, skill->GetBeats ()); // Getting ready to cast... check for spell components -Thoric if (! process_spell_components (ch, sn)) { if (ch->IsVampire ()) gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2)); else if (ch->IsMortal ()) // so imms dont lose mana ch->AddMana (-mana / 2); learn_from_failure (ch, sn); return; } if (! ch->IsNpc () && (number_percent () + skill->GetDifficulty () * 5) > ch->GetPcData ()->learned [sn]) { // Some more interesting loss of concentration messages -Thoric switch (number_bits (2)) { case 0: // too busy if (ch->GetFightData ()) ch->SendText ("This round of battle is too hectic to " "concentrate properly.\n\r"); else ch->SendText ("You lost your concentration.\n\r"); break; case 1: // irritation if (number_bits (2) == 0) { switch (number_bits (2)) { case 0: ch->SendText ("A tickle in your nose prevents you from keeping your concentration.\n\r"); break; case 1: ch->SendText ("An itch on your leg keeps you from properly casting your spell.\n\r"); break; case 2: ch->SendText ("Something in your throat prevents you from uttering the proper phrase.\n\r"); break; case 3: ch->SendText ("A twitch in your eye disrupts your concentration for a moment.\n\r"); break; } } else ch->SendText ("Something distracts you, and you lose your concentration.\n\r"); break; case 2: // not enough time if (ch->GetFightData ()) ch->SendText ("There wasn't enough time this round to " "complete the casting.\n\r"); else ch->SendText ("You lost your concentration.\n\r"); break; case 3: ch->SendText ("You get a mental block mid-way through the " "casting.\n\r"); break; } if (ch->IsVampire ()) gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2)); else if (ch->IsMortal ()) // so imms dont lose mana ch->AddMana (-mana / 2); learn_from_failure (ch, sn); return; } else { if (ch->IsVampire ()) gain_condition (ch, COND_BLOODTHIRST, - blood); else ch->AddMana (-mana); // check for immunity to magic if victim is known... // and it is a TAR_CHAR_DEFENSIVE/SELF spell // otherwise spells will have to check themselves if ((skill->GetTarget () == TAR_CHAR_DEFENSIVE || skill->GetTarget () == TAR_CHAR_SELF) && victim && victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); retcode = rSPELL_FAILED; } else { start_timer (&time_used); retcode = (*skill->GetSpellFunction ()) (sn, ch->GetLevel (), ch, vo); end_timer (&time_used); update_userec (&time_used, &skill->GetUseRec ()); } } if (retcode == rCHAR_DIED || retcode == rERROR || char_died (ch)) return; // learning if (retcode != rSPELL_FAILED) learn_from_success (ch, sn); else learn_from_failure (ch, sn); // favor adjustments if (victim && victim != ch && ! victim->IsNpc () && skill->GetTarget () == TAR_CHAR_DEFENSIVE) adjust_favor (ch, 7, 1); if (victim && victim != ch && ! ch->IsNpc () && skill->GetTarget () == TAR_CHAR_DEFENSIVE ) adjust_favor (victim, 13, 1); if (victim && victim != ch && ! ch->IsNpc () && skill->GetTarget () == TAR_CHAR_OFFENSIVE) adjust_favor (ch, 4, 1); // Fixed up a weird mess here, and added double safeguards -Thoric if (skill->GetTarget () == TAR_CHAR_OFFENSIVE && victim && ! char_died (victim) && victim != ch) { CCharacter *vch, *vch_next; for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) { vch_next = vch->GetNextInRoom (); if (vch == victim) { if (victim->GetMaster () != ch && ! victim->GetFightData ()) retcode = multi_hit (victim, ch, TYPE_UNDEFINED); break; } } } } // Cast spells at targets using a magical object. ch_ret obj_cast_spell (int sn, int level, CCharacter *ch, CCharacter *victim, CObjData *obj) { void *vo; ch_ret retcode = rNONE; int levdiff = ch->GetLevel () - level; CSkill *skill = SkillTable.GetValidSkill (sn); struct timeval time_used; if (sn == -1) return retcode; if (! skill || ! skill->GetSpellFunction ()) { bug ("Obj_cast_spell: bad sn %d.", sn); return rERROR; } if (ch->GetInRoom ()->IsNoMagic ()) { set_char_color (AT_MAGIC, ch); ch->SendText ("Nothing seems to happen...\n\r"); return rNONE; } if (ch->GetInRoom ()->IsSafe () && skill->GetTarget () == TAR_CHAR_OFFENSIVE) { set_char_color( AT_MAGIC, ch ); ch->SendText ("Nothing seems to happen...\n\r"); return rNONE; } // Basically this was added to cut down on level 5 players using level // 40 scrolls in battle too often ;) -Thoric if ((skill->GetTarget () == TAR_CHAR_OFFENSIVE || number_bits (7) == 1) // 1/128 chance if non-offensive && skill->GetType () != SKILL_HERB && ! chance (ch, 95 + levdiff)) { switch (number_bits (2)) { case 0: failed_casting (skill, ch, victim, NULL); break; case 1: act (AT_MAGIC, "The $t spell backfires!", ch, skill->GetName (), victim, TO_CHAR); if (victim) act (AT_MAGIC, "$n's $t spell backfires!", ch, skill->GetName (), victim, TO_VICT); act (AT_MAGIC, "$n's $t spell backfires!", ch, skill->GetName (), victim, TO_NOTVICT); return damage (ch, ch, number_range (1, level), TYPE_UNDEFINED); case 2: failed_casting (skill, ch, victim, NULL); break; case 3: act (AT_MAGIC, "The $t spell backfires!", ch, skill->GetName (), victim, TO_CHAR); if (victim) act (AT_MAGIC, "$n's $t spell backfires!", ch, skill->GetName (), victim, TO_VICT); act (AT_MAGIC, "$n's $t spell backfires!", ch, skill->GetName (), victim, TO_NOTVICT); return damage (ch, ch, number_range (1, level), TYPE_UNDEFINED); } return rNONE; } target_name = ""; switch (skill->GetTarget ()) { default: bug ("Obj_cast_spell: bad target for sn %d.", sn); return rERROR; case TAR_IGNORE: vo = NULL; if (victim) target_name = NCCP victim->GetName (); else if (obj) target_name = NCCP obj->GetName (); break; case TAR_CHAR_OFFENSIVE: if (victim != ch) { if (! victim) victim = ch->GetFightWho (); if (! victim || ! victim->IsNpc () && !in_arena (victim)) { ch->SendText ("You can't do that.\n\r"); return rNONE; } } if (ch != victim && is_safe (ch, victim)) return rNONE; vo = (void*) victim; break; case TAR_CHAR_DEFENSIVE: if (victim == NULL) victim = ch; vo = (void*) victim; if (skill->GetType () != SKILL_HERB && victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rNONE; } break; case TAR_CHAR_SELF: vo = (void*) ch; if (skill->GetType () != SKILL_HERB && ch->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rNONE; } break; case TAR_OBJ_INV: if (obj == NULL) { ch->SendText ("You can't do that.\n\r"); return rNONE; } vo = (void*) obj; break; } delete ranged_target_name; ranged_target_name = str_dup (target_name); start_timer (&time_used); retcode = (*skill->GetSpellFunction ()) (sn, level, ch, vo); end_timer (&time_used); update_userec (&time_used, &skill->GetUseRec ()); if (retcode == rSPELL_FAILED) retcode = rNONE; if (retcode == rCHAR_DIED || retcode == rERROR) return retcode; if (char_died (ch)) return rCHAR_DIED; if (skill->GetTarget () == TAR_CHAR_OFFENSIVE && victim != ch && !char_died (victim)) { CCharacter *vch; CCharacter *vch_next; for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) { vch_next = vch->GetNextInRoom (); if (victim == vch && !victim->GetFightData () && victim->GetMaster () != ch) { retcode = multi_hit (victim, ch, TYPE_UNDEFINED); break; } } } return retcode; } // Spell functions. ch_ret spell_acid_blast (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; int dam = dice (level, 6); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_blindness (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; int tmp; CSkill *skill = SkillTable.GetValidSkill (sn); if (skill->IsPkSensitive () && ! ch->IsNpc () && ! victim->IsNpc ()) tmp = level/2; else tmp = level; if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsBlind () || saves_spell_staff (tmp, victim)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } af.type = sn; af.location = APPLY_HITROLL; af.modifier = -4; af.duration = (short) ((1 + (level / 3)) * DUR_CONV); af.bitvector = AFF_BLIND; affect_to_char (victim, &af); set_char_color (AT_MAGIC, victim); victim->SendText ("You are blinded!\n\r"); if (ch != victim) { act (AT_MAGIC, "You weave a spell of blindness around $N.", ch, NULL, victim, TO_CHAR ); act (AT_MAGIC, "$n weaves a spell of blindness about $N.", ch, NULL, victim, TO_NOTVICT ); } return rNONE; } ch_ret spell_burning_hands (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_call_lightning (int sn, int level, CCharacter *ch, void *vo) { CCharacter *vch; CCharacter *vch_next; BOOL ch_died; ch_ret retcode = rNONE; if (ch->IsIndoors ()) { ch->SendText ("You must be out of doors.\n\r"); return rSPELL_FAILED; } if (weather_info.sky < SKY_RAINING) { ch->SendText ("You need bad weather.\n\r"); return rSPELL_FAILED; } int dam = dice (level/2, 8); set_char_color (AT_MAGIC, ch); ch->SendText ("God's lightning strikes your foes!\n\r"); act (AT_MAGIC, "$n calls God's lightning to strike $s foes!", ch, NULL, NULL, TO_ROOM); ch_died = FALSE; for (vch = first_char; vch; vch = vch_next) { vch_next = vch->GetNext (); if (! vch->GetInRoom ()) continue; if (vch->GetInRoom () == ch->GetInRoom ()) { if (! vch->IsNpc () && vch->IsWizInvis () && vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL) continue; if (vch != ch && (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ())) retcode = damage (ch, vch, saves_spell_staff (level, vch) ? dam/2 : dam, sn); if (retcode == rCHAR_DIED || char_died (ch)) ch_died = TRUE; continue; } if (! ch_died && vch->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea () && vch->IsOutside () && vch->IsAwake ()) { if (number_bits (3) == 0) vch->SendColor ("&BLightning flashes in the sky.\n\r"); } } if (ch_died) return rCHAR_DIED; else return rNONE; } ch_ret spell_cause_light (int sn, int level, CCharacter *ch, void *vo) { return damage (ch, (CCharacter *) vo, dice (1, 8) + level / 3, sn); } ch_ret spell_cause_critical (int sn, int level, CCharacter *ch, void *vo) { return damage (ch, (CCharacter *) vo, dice (3, 8) + level - 6, sn); } ch_ret spell_cause_serious (int sn, int level, CCharacter *ch, void *vo) { return damage (ch, (CCharacter *) vo, dice (2, 8) + level / 2, sn); } ch_ret spell_change_sex (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (is_affected (victim, sn)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } af.type = sn; af.duration = (short) (10 * level * DUR_CONV); af.location = APPLY_SEX; do { af.modifier = number_range (0, 2) - victim->GetSex (); } while (af.modifier == 0); af.bitvector = -1; affect_to_char (victim, &af); set_char_color (AT_MAGIC, victim); victim->SendText ("You feel different.\n\r"); if (ch != victim) ch->SendText ("Ok.\n\r"); return rNONE; } ch_ret spell_charm_person (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; int chance; char buf [MAX_STRING_LENGTH]; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == ch) { ch->SendText ("You like yourself even better!\n\r"); return rSPELL_FAILED; } if (victim->IsImmuneMagic () || victim->IsImmuneCharm ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! victim->IsNpc () && !ch->IsNpc ()) { ch->SendText ("I don't think so...\n\r"); victim->SendText ("You feel charmed...\n\r"); return rSPELL_FAILED; } chance = ris_save (victim, level, RIS_CHARM); if (victim->IsCharmed () || chance == 1000 || ch->IsCharmed () || level < victim->GetLevel () || circle_follow (victim, ch) || saves_spell_staff (chance, victim)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->GetMaster ()) stop_follower (victim); add_follower (victim, ch); af.type = sn; af.duration = (short)((number_fuzzy ((level + 1) / 3) + 1) * DUR_CONV); af.location = 0; af.modifier = 0; af.bitvector = AFF_CHARM; affect_to_char (victim, &af); act (AT_MAGIC, "Isn't $n just so nice?", ch, NULL, victim, TO_VICT); act (AT_MAGIC, "$N's eyes glaze over...", ch, NULL, victim, TO_ROOM); if (ch != victim) ch->SendText ("Ok.\n\r"); sprintf (buf, "%s has charmed %s.", ch->GetName (), victim->GetName ()); gpDoc->LogString (buf, LOG_PLAYER, ch->GetLevel ()); if (victim->IsNpc ()) { start_hating (victim, ch); start_hunting (victim, ch); } return rNONE; } ch_ret spell_chill_touch (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38 }; CAffectData af; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (! saves_spell_staff (level, victim)) { af.type = sn; af.duration = 14; af.location = APPLY_STR; af.modifier = -1; af.bitvector = -1; affect_join (victim, &af); } else dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_colour_spray (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 80, 80, 81, 82, 82, 83, 83, 84, 85, 85, 86, 86, 87, 88, 88, 89, 89, 90, 91, 91 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_control_weather (int sn, int level, CCharacter *ch, void *vo) { CSkill *skill = SkillTable.GetValidSkill (sn); if (! str_cmp (target_name, "better")) weather_info.change += dice (level / 3, 4); else if (!str_cmp (target_name, "worse")) weather_info.change -= dice (level / 3, 4); else { ch->SendText ("Do you want it to get better or worse?\n\r"); return rSPELL_FAILED; } successful_casting (skill, ch, NULL, NULL); return rNONE; } ch_ret spell_create_food (int sn, int level, CCharacter *ch, void *vo) { CObjData *mushroom; mushroom = create_object (OIdxTable.GetObj (OBJ_VNUM_MUSHROOM), 0); mushroom->value [0] = 5 + level; act (AT_MAGIC, "$p suddenly appears.", ch, mushroom, NULL, TO_ROOM); act (AT_MAGIC, "$p suddenly appears.", ch, mushroom, NULL, TO_CHAR); mushroom = obj_to_room (mushroom, ch->GetInRoom ()); return rNONE; } ch_ret spell_create_water (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj = (CObjData *) vo; int water; if (obj->item_type != ITEM_DRINK_CON) { ch->SendText ("It is unable to hold water.\n\r"); return rSPELL_FAILED; } if (obj->value [2] != LIQ_WATER && obj->value [1] != 0) { ch->SendText ("It contains some other liquid.\n\r"); return rSPELL_FAILED; } water = UMIN (level * (weather_info.sky >= SKY_RAINING ? 4 : 2), obj->value [0] - obj->value[1]); if (water > 0) { separate_obj (obj); obj->value [2] = LIQ_WATER; obj->value [1] += water; if (! is_name ("water", obj->GetName ())) { char buf [MAX_STRING_LENGTH]; sprintf (buf, "%s water", obj->GetName ()); obj->SetName (buf); } act (AT_MAGIC, "$p is filled.", ch, obj, NULL, TO_CHAR); } return rNONE; } ch_ret spell_cure_blindness (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); set_char_color (AT_MAGIC, ch); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! is_affected (victim, gsn_blindness)) { if (ch != victim) ch->SendText ( "You work your cure, but it has no apparent effect.\n\r"); else ch->SendText ("You don't seem to be blind.\n\r"); return rSPELL_FAILED; } affect_strip (victim, gsn_blindness); set_char_color (AT_MAGIC, victim); victim->SendText ("Your vision returns!\n\r"); if (ch != victim) ch->SendText ("You work your cure, restoring vision.\n\r"); return rNONE; } // Next 3 are Paladin spells, until we update smaugspells -- Blodkai, 4/97 ch_ret spell_sacral_divinity (int sn, int level, CCharacter *ch, void *vo) { CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); if (ch->GetAlignment () < 350) { act (AT_MAGIC, "Your prayer goes unanswered.", ch, NULL, NULL, TO_CHAR); return rSPELL_FAILED; } if (ch->IsImmuneMagic ()) { immune_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } if (ch->HasSanctuary ()) return rSPELL_FAILED; af.type = sn; af.duration = level * 3; af.location = APPLY_AFFECT; af.modifier = 0; af.bitvector = AFF_SANCTUARY; affect_to_char (ch, &af); act (AT_MAGIC, "A shroud of glittering light slowly wraps itself about $n.", ch, NULL, NULL, TO_ROOM); act (AT_MAGIC, "A shroud of glittering light slowly wraps itself around you.", ch, NULL, NULL, TO_CHAR ); return rNONE; } ch_ret spell_expurgation (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter*) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! is_affected (victim, gsn_poison)) return rSPELL_FAILED; affect_strip (victim, gsn_poison); act (AT_MAGIC, "You speak an ancient prayer, begging your god for purification.", ch, NULL, NULL, TO_CHAR); act (AT_MAGIC, "$n speaks an ancient prayer begging $s god for purification.", ch, NULL, NULL, TO_ROOM); return rNONE; } ch_ret spell_bethsaidean_touch (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter*) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! is_affected (victim, gsn_blindness)) return rSPELL_FAILED; affect_strip (victim, gsn_blindness); set_char_color (AT_MAGIC, victim); victim->SendText ("Your sight is restored!\n\r"); if (ch != victim) { act (AT_MAGIC, "$n lays $s hands over your eyes and concentrates...", ch, NULL, victim, TO_VICT); act (AT_MAGIC, "$n lays $s hands over $N's eyes and concentrates...", ch, NULL, victim, TO_NOTVICT); act (AT_MAGIC, "Laying your hands on $N's eyes, you pray to life $S blindness.", ch, NULL, victim, TO_CHAR); } return rNONE; } ch_ret spell_cure_poison (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (is_affected (victim, gsn_poison)) { affect_strip (victim, gsn_poison); set_char_color (AT_MAGIC, victim); victim->SendText ("A warm feeling runs through your body.\n\r"); victim->SetMentalState (URANGE (-100, victim->GetMentalState (), -10)); if (ch != victim) { act (AT_MAGIC, "A flush of health washes over $N.", ch, NULL, victim, TO_NOTVICT); act (AT_MAGIC, "You lift the poison from $N's body.", ch, NULL, victim, TO_CHAR); } return rNONE; } else { set_char_color (AT_MAGIC, ch); if (ch != victim) ch->SendText ( "You work your cure, but it has no apparent effect.\n\r"); else ch->SendText ("You don't seem to be poisoned.\n\r"); } return rSPELL_FAILED; } ch_ret spell_curse (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsCursed () || saves_spell_staff (level, victim)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } af.type = sn; af.duration = (short) ((4*level) * DUR_CONV); af.location = APPLY_HITROLL; af.modifier = -1; af.bitvector = AFF_CURSE; affect_to_char (victim, &af); af.location = APPLY_SAVING_SPELL; af.modifier = 1; affect_to_char (victim, &af); set_char_color (AT_MAGIC, victim); victim->SendText ("You feel unclean.\n\r"); if (ch != victim) { act (AT_MAGIC, "You utter a curse upon $N.", ch, NULL, victim, TO_CHAR ); act (AT_MAGIC, "$n utters a curse upon $N.", ch, NULL, victim, TO_NOTVICT ); } return rNONE; } ch_ret spell_detect_poison (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj = (CObjData *) vo; set_char_color (AT_MAGIC, ch); if (obj->item_type == ITEM_DRINK_CON || obj->item_type == ITEM_FOOD || obj->item_type == ITEM_COOK) { if (obj->item_type == ITEM_COOK && obj->value [2] == 0) ch->SendText ("It looks undercooked.\n\r"); else if (obj->value [3] != 0) ch->SendText ("You smell poisonous fumes.\n\r"); else ch->SendText ("It looks very delicious.\n\r"); } else ch->SendText ("It doesn't look poisoned.\n\r"); return rNONE; } ch_ret spell_dispel_evil (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (! ch->IsNpc () && ch->IsEvil ()) victim = ch; CString acts = SysData.GetSupremeEntity (); acts += " protects $N."; if (victim->IsGood ()) { act (AT_MAGIC, acts, ch, NULL, victim, TO_ROOM); return rSPELL_FAILED; } if (victim->IsNeutral ()) { act (AT_MAGIC, "$N does not seem to be affected.", ch, NULL, victim, TO_CHAR); return rSPELL_FAILED; } if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } int dam = dice (level, 4); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_dispel_magic (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; int affected_by, cnt; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! victim->GetAffectFlags ().IsEmpty () && ch == victim) { set_char_color (AT_MAGIC, ch); ch->SendText ("You pass your hands around your body...\n\r"); while (! victim->m_AffectList.IsEmpty ()) affect_remove (victim, victim->m_AffectList.RemoveTail ()); victim->SetAffectFlags (RaceTable.GetAffects (victim->GetRace ())); return rNONE; } else if (victim->GetAffectFlags () == RaceTable.GetAffects (victim->GetRace ()) || level < victim->GetLevel () || saves_spell_staff (level, victim)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! victim->IsNpc ()) { ch->SendText ("You can't do that... yet.\n\r"); return rSPELL_FAILED; } cnt = 0; for (;;) { affected_by = number_range (1, MAX_AFFECTED_BY); if (victim->IsAffected (affected_by)) break; if (cnt++ > 30) { failed_casting (skill, ch, victim, NULL); return rNONE; } } victim->ClrAffBit (affected_by); successful_casting (skill, ch, victim, NULL); return rNONE; } ch_ret spell_earthquake (int sn, int level, CCharacter *ch, void *vo) { CCharacter *vch; CCharacter *vch_next; BOOL ch_died; ch_ret retcode; CSkill *skill = SkillTable.GetValidSkill (sn); ch_died = FALSE; retcode = rNONE; if (ch->GetInRoom ()->IsSafe ()) { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } act (AT_MAGIC, "The earth trembles beneath your feet!", ch, NULL, NULL, TO_CHAR); act (AT_MAGIC, "$n makes the earth tremble and shiver.", ch, NULL, NULL, TO_ROOM); for (vch = first_char; vch; vch = vch_next) { vch_next = vch->GetNext (); if (! vch->GetInRoom ()) continue; if (vch->GetInRoom () == ch->GetInRoom ()) { if (! vch->IsNpc () && vch->IsWizInvis () && vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL) continue; // Added victim immune if floating or flying. This change // suggested by anrothan@locklin.org (ashenlands mud) - RCP BOOL bDoDamage = vch != ch && (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) && ! vch->IsFloating () && ! vch->IsFlying (); if (bDoDamage) { retcode = damage (ch, vch, level + dice (2, 8), sn); if (retcode == rCHAR_DIED || char_died (ch)) { ch_died = TRUE; continue; } if (char_died (vch)) continue; } } if (!ch_died && vch->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()) { if (number_bits (3) == 0) vch->SendColor ("&BThe earth trembles and shivers.\n\r"); } } if (ch_died) return rCHAR_DIED; else return rNONE; } ch_ret spell_enchant_weapon (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj = (CObjData *) vo; CAffectData *paf; if (obj->item_type != ITEM_WEAPON || obj->IsMagic () || obj->HasAffects ()) { act (AT_MAGIC, "Your magic twists and winds around $p but cannot take hold.", ch, obj, NULL, TO_CHAR); act (AT_MAGIC, "$n's magic twists and winds around $p but cannot take hold.", ch, obj, NULL, TO_NOTVICT ); return rSPELL_FAILED; } // Bug fix here. -- Alty separate_obj (obj); paf = new CAffectData; paf->type = -1; paf->duration = -1; paf->location = APPLY_HITROLL; paf->modifier = level / 15; paf->bitvector = -1; obj->AddAffect (paf); paf = new CAffectData; paf->type = -1; paf->duration = -1; paf->location = APPLY_DAMROLL; paf->modifier = level / 15; paf->bitvector = -1; obj->AddAffect (paf); if (ch->IsGood ()) { obj->SetAntiEvil (); act (AT_BLUE, "$p gleams with flecks of blue energy.", ch, obj, NULL, TO_CHAR); act (AT_BLUE, "$p gleams with flecks of blue energy.", ch, obj, NULL, TO_CHAR); } else if (ch->IsEvil ()) { obj->SetAntiGood (); act (AT_BLOOD, "A crimson stain flows slowly over $p.", ch, obj, NULL, TO_CHAR); act (AT_BLOOD, "A crimson stain flows slowly over $p.", ch, obj, NULL, TO_ROOM); } else { obj->SetAntiEvil (); obj->SetAntiGood (); act (AT_YELLOW, "$p glows with a disquieting light.", ch, obj, NULL, TO_CHAR); act (AT_YELLOW, "$p glows with a disquieting light.", ch, obj, NULL, TO_ROOM); } return rNONE; } // Drain XP, MANA, HP. // Caster gains HP. ch_ret spell_energy_drain (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } int chance = ris_save (victim, victim->GetLevel (), RIS_DRAIN); if (chance == 1000 || saves_spell_staff (chance, victim)) { failed_casting (skill, ch, victim, NULL); // SB return rSPELL_FAILED; } ch->SetAlignment (UMAX (-1000, ch->GetAlignment () - 200)); int dam; if (victim->GetLevel () <= 2) dam = ch->GetHp () + 1; else { gain_exp (victim, 0 - number_range (level / 2, 3 * level / 2)); victim->SetMana (victim->GetMana () / 2); victim->SetMove (victim->GetMove () / 2); dam = dice (1, level); ch->AddHp (dam); } if (ch->GetHp () > ch->GetMaxHp ()) ch->SetHp (ch->GetMaxHp ()); return damage (ch, victim, dam, sn); } ch_ret spell_fireball (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_flamestrike (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; int dam = dice (6, 8); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_faerie_fire (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsFaerieFire ()) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } af.type = sn; af.duration = (short) (level * DUR_CONV); af.location = APPLY_AC; af.modifier = 2 * level; af.bitvector = AFF_FAERIE_FIRE; affect_to_char (victim, &af); act (AT_PINK, "You are surrounded by a pink outline.", victim, NULL, NULL, TO_CHAR); act (AT_PINK, "$n is surrounded by a pink outline.", victim, NULL, NULL, TO_ROOM); return rNONE; } ch_ret spell_faerie_fog (int sn, int level, CCharacter *ch, void *vo) { act (AT_MAGIC, "$n conjures a cloud of purple smoke.", ch, NULL, NULL, TO_ROOM); act (AT_MAGIC, "You conjure a cloud of purple smoke.", ch, NULL, NULL, TO_CHAR); CCharacter *ich = ch->GetInRoom ()->first_person; for ( ; ich; ich = ich->GetNextInRoom ()) { if (! ich->IsNpc () && ich->IsWizInvis ()) continue; if (ich == ch || saves_spell_staff (level, ich)) continue; affect_strip (ich, gsn_invis); affect_strip (ich, gsn_mass_invis); affect_strip (ich, gsn_sneak); ich->ClrHide (); ich->ClrInvis (); ich->ClrSneak (); act (AT_MAGIC, "$n is revealed!", ich, NULL, NULL, TO_ROOM); act (AT_MAGIC, "You are revealed!", ich, NULL, NULL, TO_CHAR); } return rNONE; } ch_ret spell_gate (int sn, int level, CCharacter *ch, void *vo) { CMobIndexData *pMob = MobTable.GetMob (MOB_VNUM_VAMPIRE); if (! pMob) { bug ("Spell_gate: Vampire vnum %d doesn't exist.", MOB_VNUM_VAMPIRE); return rSPELL_FAILED; } create_mobile (pMob)->SendToRoom (ch->GetInRoom ()); return rNONE; } ch_ret spell_harm (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } int dam = UMAX (20, victim->GetHp () - dice (1,4)); if (saves_spell_staff (level, victim)) dam = UMIN (50, dam / 4); dam = UMIN (100, dam); return damage (ch, victim, dam, sn); } ch_ret spell_identify (int sn, int level, CCharacter *ch, void *vo) { // Modified by Scryn to work on mobs/players/objs // Made it show short descrs instead of keywords, seeing as you need // to know the keyword anyways, we may as well make it look nice -- Alty CObjData *obj; CCharacter *victim; CAffectData *paf; CSkill *sktmp; CSkill *skill = SkillTable.GetValidSkill (sn); if (target_name [0] == '\0') { ch->SendText ("What should the spell be cast upon?\n\r"); return rSPELL_FAILED; } if ((obj = get_obj_carry (ch, target_name))) { set_char_color (AT_LBLUE, ch); ch->SendColorf ("\n\rObject '%s' is %s", obj->GetShortDescr (), aoran (item_type_name (obj))); if (obj->item_type != ITEM_LIGHT && obj->wear_flags-1 > 0) ch->SendTextf (", with wear location: %s\n\r", flag_string (obj->wear_flags-1, w_flags)); else ch->SendTextf (".\n\r"); ch->SendTextf ("Special properties:%s%s.\n\r" "Its weight is %d, value is %d, and level is %d.\n\r", obj->AntiClassNames (), ExtraBitName (obj->m_ExtraFlags), obj->weight, obj->cost, obj->level); set_char_color (AT_MAGIC, ch); switch (obj->item_type) { case ITEM_CONTAINER: ch->SendColorf ("%s appears to be %s.\n\r", capitalize (obj->GetShortDescr ()), obj->value [0] < 76 ? "of a small capacity" : obj->value [0] < 150 ? "of a small to medium capacity" : obj->value [0] < 300 ? "of a medium capacity" : obj->value [0] < 550 ? "of a medium to large capacity" : obj->value [0] < 751 ? "of a large capacity" : "of a giant capacity"); break; case ITEM_PILL: case ITEM_SCROLL: case ITEM_POTION: ch->SendTextf ("Level %d spells of:", obj->value [0]); if (obj->value [1] >= 0 && (sktmp=SkillTable.GetValidSkill (obj->value [1]))) ch->SendTextf (" '%s'", sktmp->GetName ()); if (obj->value [2] >= 0 && (sktmp=SkillTable.GetValidSkill (obj->value [2]))) ch->SendTextf (" '%s'", sktmp->GetName ()); if (obj->value [3] >= 0 && (sktmp=SkillTable.GetValidSkill (obj->value [3]))) ch->SendTextf (" '%s'", sktmp->GetName ()); ch->SendText (".\n\r"); break; case ITEM_SALVE: ch->SendTextf ("Has %d(%d) applications of level %d", obj->value [1], obj->value [2], obj->value [0]); if (obj->value [4] >= 0 && (sktmp = SkillTable.GetValidSkill (obj->value [4]))) ch->SendTextf (" '%s'", sktmp->GetName ()); if (obj->value [5] >= 0 && (sktmp = SkillTable.GetValidSkill (obj->value [5]))) ch->SendTextf (" '%s'", sktmp->GetName ()); ch->SendText (".\n\r"); break; case ITEM_WAND: case ITEM_STAFF: ch->SendTextf ("Has %d (%d) charges of level %d", obj->value[1], obj->value[2], obj->value[0]); if (obj->value[3] >= 0 && (sktmp = SkillTable.GetValidSkill (obj->value[3]))) ch->SendTextf (" '%s'", sktmp->GetName ()); ch->SendText (".\n\r"); break; case ITEM_WEAPON: ch->SendTextf ("Damage is %d to %d (average %d)%s\n\r", obj->value[1], obj->value[2], (obj->value[1] + obj->value[2]) / 2, obj->IsPoisoned () ? ", and is poisonous." : "." ); break; case ITEM_ARMOR: ch->SendTextf ("Armor class is %d.\n\r", obj->value [0]); break; } CAffectList &IList = obj->pIndexData->AffList; POSITION apos = IList.GetHeadPosition (); while (apos) IList.GetNext (apos)->ShowAffect (ch); CAffectList &AList = obj->AffList; apos = AList.GetHeadPosition (); while (apos) AList.GetNext (apos)->ShowAffect (ch); return rNONE; } else if ((victim = get_char_room (ch, target_name)) != NULL) { if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsNpc ()) { set_char_color (AT_BLOOD, ch); ch->SendTextf ("%s appears to be between level %d and %d.\n\r", victim->GetName (), victim->GetLevel () - (victim->GetLevel () % 5), victim->GetLevel () - (victim->GetLevel () % 5) + 5); } else { set_char_color (AT_GREEN, ch); ch->SendColorf ("%s appears to be level %d.\n\r", victim->GetName (), victim->GetLevel ()); } ch->SendTextf ("%s looks like %s, and follows the ways of the %s.\n\r", victim->GetName (), aoran (victim->GetRaceName ()), victim->GetClassName ()); if ((chance (ch, 50) && ch->GetLevel () >= victim->GetLevel () + 10) || ch->IsImmortal ()) { ch->SendColorf ("%s appears to be affected by: ", victim->GetName ()); if (victim->m_AffectList.IsEmpty ()) { ch->SendText ("nothing.\n\r"); return rNONE; } POSITION pos = victim->m_AffectList.GetHeadPosition (); while (pos) { paf = victim->m_AffectList.GetNext (pos); if (sktmp = SkillTable.GetValidSkill (paf->type)) { ch->SendTextf ("%s%s.", pos ? "and " : "", sktmp->GetName ()); } ch->SendText ("\n\r"); } } } else { ch->SendTextf ("&gYou can't find %s!\n\r", target_name); return rSPELL_FAILED; } return rNONE; } ch_ret spell_invis (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim; CSkill *skill = SkillTable.GetValidSkill (sn); // Modifications on 1/2/96 to work on player/object - Scryn if (target_name [0] == '\0') victim = ch; else victim = get_char_room (ch, target_name); if (victim) { CAffectData af; if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsInvis ()) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } act (AT_MAGIC, "$n fades out of existence.", victim, NULL, NULL, TO_ROOM); af.type = sn; af.duration = (short)(((level / 4) + 12) * DUR_CONV); af.location = APPLY_NONE; af.modifier = 0; af.bitvector = AFF_INVISIBLE; affect_to_char (victim, &af); act (AT_MAGIC, "You fade out of existence.", victim, NULL, NULL, TO_CHAR); return rNONE; } else { CObjData *obj; obj = get_obj_carry (ch, target_name); if (obj) { separate_obj (obj); // Fix multi-invis bug --Blod if (obj->IsInvisible () || chance (ch, 40 + level / 10)) { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } obj->SetInvisible (); act (AT_MAGIC, "$p fades out of existence.", ch, obj, NULL, TO_CHAR); return rNONE; } } ch->SendTextf ("You can't find %s!\n\r", target_name); return rSPELL_FAILED; } ch_ret spell_know_alignment (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; char *msg; CSkill *skill = SkillTable.GetValidSkill (sn); if (! victim) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } int ap = victim->GetAlignment (); 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 would just as soon kill you as look at you."; else msg = "I'd rather just not say anything at all about $N."; act (AT_MAGIC, msg, ch, NULL, victim, TO_CHAR); return rNONE; } ch_ret spell_lightning_bolt (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 64, 65, 65, 66, 66, 67, 68, 68, 69, 69, 70, 71, 71, 72, 72, 73, 73, 74, 75, 75 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_locate_object (int sn, int level, CCharacter *ch, void *vo) { char buf [MAX_INPUT_LENGTH]; CObjData *obj; CObjData *in_obj; int cnt; BOOL bFound = FALSE; CParseinfo Inf; while (obj = Inf.ParseAreaLists (AllAreasList)) { if (! can_see_obj (ch, *obj) || !nifty_is_name (target_name, obj->GetName ())) continue; if (obj->IsPrototype () && ch->IsMortal ()) continue; for (cnt=0, in_obj=obj; in_obj->in_obj && cnt < 100; in_obj = in_obj->in_obj, ++cnt); if (cnt >= MAX_NEST) { bug ("spell_locate_obj: object [%d] %s is nested more than " "%d times!", obj->pIndexData->vnum, obj->GetShortDescr (), MAX_NEST); continue; } if (in_obj->carried_by) { if (in_obj->carried_by->IsImmortal () && ! in_obj->carried_by->IsNpc () && (ch->GetTrustLevel () < in_obj->carried_by->GetPcData ()->wizinvis) && in_obj->carried_by->IsWizInvis ()) continue; sprintf (buf, "%s carried by %s.\n\r", obj_short (obj), PERS (in_obj->carried_by, ch)); } else { sprintf (buf, "%s in %s.\n\r", obj_short (obj), in_obj->in_room == NULL ? "somewhere" : in_obj->in_room->GetName ()); } buf [0] = UPPER (buf [0]); set_char_color (AT_MAGIC, ch); ch->SendColor (buf); bFound = TRUE; } if (! bFound) { ch->SendText ("Nothing like that exists.\n\r"); return rSPELL_FAILED; } return rNONE; } ch_ret spell_magic_missile (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); /* What's this? You can't save vs. magic missile! -Thoric if (saves_spell (level, victim)) dam /= 2; */ return damage (ch, victim, dam, sn); } ch_ret spell_pass_door (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->CanPass ()) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } af.type = sn; af.duration = (short)(number_fuzzy (level / 4) * DUR_CONV); af.location = APPLY_NONE; af.modifier = 0; af.bitvector = AFF_PASS_DOOR; affect_to_char (victim, &af); act (AT_MAGIC, "$n turns translucent.", victim, NULL, NULL, TO_ROOM); act (AT_MAGIC, "You turn translucent.", victim, NULL, NULL, TO_CHAR); return rNONE; } ch_ret spell_poison (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CAffectData af; BOOL first = TRUE; int chance = ris_save (victim, level, RIS_POISON); if (chance == 1000 || saves_poison_death (chance, victim)) { set_char_color (AT_MAGIC, ch); ch->SendText ("Your magic fails to take hold.\n\r"); return rSPELL_FAILED; } if (victim->IsPoisoned ()) first = FALSE; af.type = sn; af.duration = (short)(level * DUR_CONV); af.location = APPLY_STR; af.modifier = -2; af.bitvector = AFF_POISON; affect_join (victim, &af); set_char_color (AT_GREEN, victim); victim->SendText ("You feel very sick.\n\r"); victim->SetMentalState (URANGE (victim->IsPkiller () ? 10 : 20, victim->GetMentalState () + (first ? 5 : 0), 100)); if (ch != victim) { act (AT_GREEN, "$N shivers as your poison spreads through $S body.", ch, NULL, victim, TO_CHAR ); act (AT_GREEN, "$N shivers as $n's poison spreads through $S body.", ch, NULL, victim, TO_NOTVICT ); } return rNONE; } ch_ret spell_remove_curse (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj; CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (is_affected (victim, gsn_curse)) { affect_strip (victim, gsn_curse); set_char_color (AT_MAGIC, victim); victim->SendText ("The weight of your curse is lifted.\n\r"); if (ch != victim) { act (AT_MAGIC, "You dispel the curses afflicting $N.", ch, NULL, victim, TO_CHAR ); act (AT_MAGIC, "$n's dispels the curses afflicting $N.", ch, NULL, victim, TO_NOTVICT ); } } else if (victim->IsCarrying ()) { POSITION pos = victim->GetHeadCarryPos (); while (obj = victim->GetNextCarrying (pos)) if (! obj->in_obj && (obj->IsNoRemove () || obj->IsNoDrop ())) { if (obj->IsNoRemove ()) obj->ClrNoRemove (); if (obj->IsNoDrop ()) obj->ClrNoDrop (); set_char_color (AT_MAGIC, victim); victim->SendText ("You feel a burden released.\n\r"); if (ch != victim) { act (AT_MAGIC, "You dispel the curses afflicting $N.", ch, NULL, victim, TO_CHAR ); act (AT_MAGIC, "$n's dispels the curses afflicting $N.", ch, NULL, victim, TO_NOTVICT ); } return rNONE; } } return rNONE; } ch_ret spell_remove_trap (int sn, int level, CCharacter *ch, void *vo) { CObjData *trap; int retcode; CSkill *skill = SkillTable.GetValidSkill (sn); if (! target_name || target_name [0] == '\0') { ch->SendText ("Remove trap on what?\n\r"); return rSPELL_FAILED; } if (ch->GetInRoom ()->IsEmpty ()) { ch->SendText ("You can't find that here.\n\r"); return rNONE; } BOOL bFound = FALSE; CObjData *obj = NULL; POSITION pos = ch->GetInRoom ()->GetHeadContentPos (); while (obj = ch->GetInRoom ()->GetNextContent (pos)) if (can_see_obj (ch, *obj) && nifty_is_name (target_name, obj->GetName ())) { bFound = TRUE; break; } if (! bFound) { ch->SendText ("You can't find that here.\n\r"); return rSPELL_FAILED; } if ((trap = get_trap (obj)) == NULL) { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } if (chance (ch, 70 + ch->GetWisdom ())) { ch->SendText ("Ooops!\n\r"); retcode = spring_trap (ch, trap); if (retcode == rNONE) retcode = rSPELL_FAILED; return retcode; } extract_obj (trap); successful_casting (skill, ch, NULL, NULL); return rNONE; } ch_ret spell_shocking_grasp (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) 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, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (saves_spell_staff (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_sleep (int sn, int level, CCharacter *ch, void *vo) { CAffectData af; int retcode; int chance; int tmp; CCharacter *victim; CSkill *skill = SkillTable.GetValidSkill (sn); if ((victim = get_char_room (ch, target_name)) == NULL) { ch->SendText ("They aren't here.\n\r"); return rSPELL_FAILED; } if (! victim->IsNpc () && victim->GetFightData ()) { ch->SendText ("You cannot sleep a fighting player.\n\r"); return rSPELL_FAILED; } if (is_safe (ch, victim)) return rSPELL_FAILED; if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (skill->IsPkSensitive () && !ch->IsNpc () && ! victim->IsNpc ()) tmp = level / 2; else tmp = level; if (victim->IsSleeping () || (chance = ris_save (victim, tmp, RIS_SLEEP)) == 1000 || level < victim->GetLevel () || (victim != ch && victim->GetInRoom ()->IsSafe ()) || saves_spell_staff (chance, victim)) { failed_casting (skill, ch, victim, NULL); if (ch == victim) return rSPELL_FAILED; if (! victim->GetFightData ()) { retcode = multi_hit (victim, ch, TYPE_UNDEFINED); if (retcode == rNONE) retcode = rSPELL_FAILED; return retcode; } } af.type = sn; af.duration = (short)((4 + level) * DUR_CONV); af.location = APPLY_NONE; af.modifier = 0; af.bitvector = AFF_SLEEP; affect_join (victim, &af); // Added by Narn at the request of Dominus. if (! victim->IsNpc ()) { sprintf (log_buf, "%s has cast sleep on %s.", ch->GetName (), victim->GetName ()); gpDoc->LogString (log_buf, LOG_PLAYER, ch->GetLevel ()); to_channel (log_buf, CHANNEL_MONITOR, "Monitor", UMAX (LEVEL_IMMORTAL, ch->GetLevel ())); } if (victim->IsAwake ()) { act (AT_MAGIC, "You feel very sleepy ..... zzzzzz.", victim, NULL, NULL, TO_CHAR); act (AT_MAGIC, "$n goes to sleep.", victim, NULL, NULL, TO_ROOM); victim->SetPosition (POS_SLEEPING); } if (victim->IsNpc ()) start_hating (victim, ch); return rNONE; } ch_ret spell_summon (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); char buf [MAX_STRING_LENGTH]; if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if (CRoom.IsNoAstral () || VRoom.IsSafe () || VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoSummon () || VRoom.IsNoRecall () || victim->GetLevel () >= level + 3 || victim->GetFightData () || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || !in_hard_range (victim, CRoom.GetArea ()) || (!ch->IsNpc () && !ch->CanPkill () && victim->IsPkiller ()) || ((CRoom.GetArea ()->IsNoPkill ()) && victim->IsPkiller ()) || (!ch->IsNpc () && !victim->IsNpc () && victim->IsNoSummon ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (CRoom.GetArea () != victim->GetInRoom ()->GetArea ()) { if (((ch->IsNpc () != victim->IsNpc ()) && chance (ch, 30)) || ((ch->IsNpc () == victim->IsNpc ()) && chance (ch, 60))) { failed_casting (skill, ch, victim, NULL); set_char_color (AT_MAGIC, victim); victim->SendText ("You feel a strange pulling sensation...\n\r"); return rSPELL_FAILED; } } if (!ch->IsNpc ()) { act (AT_MAGIC, "You feel a wave of nausea overcome you...", ch, NULL, NULL, TO_CHAR); act (AT_MAGIC, "$n collapses, stunned!", ch, NULL, NULL, TO_ROOM); ch->SetPosition (POS_STUNNED); sprintf (buf, "%s summoned %s to room %d.", ch->GetName (), victim->GetName (), CRoom.vnum); gpDoc->LogString (buf, LOG_PLAYER, ch->GetLevel ()); to_channel (buf, CHANNEL_MONITOR, "Monitor", UMAX (LEVEL_IMMORTAL, ch->GetLevel ())); } act (AT_MAGIC, "$n disappears suddenly.", victim, NULL, NULL, TO_ROOM); victim->RemoveFromRoom (); victim->SendToRoom (&CRoom); act (AT_MAGIC, "$n arrives suddenly.", victim, NULL, NULL, TO_ROOM); act (AT_MAGIC, "$N has summoned you!", victim, NULL, ch, TO_CHAR); do_look (victim, "auto"); return rNONE; } // Travel via the astral plains to quickly travel to desired location // -Thoric ch_ret spell_astral_walk (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || CRoom.IsNoRecall () || victim->GetLevel () >= level + 15 || (victim->CanPkill () && ! ch->IsNpc () && ! ch->CanPkill ()) || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || !in_hard_range (ch, victim->GetInRoom ()->GetArea ()) || (victim->GetInRoom ()->GetArea ()->IsNoPkill () && ch->IsPkiller ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (skill->ValidHitCharMsg ()) act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR); if (skill->ValidHitVictMsg ()) act (AT_MAGIC, skill->GetHitVictMsg (), ch, NULL, victim, TO_VICT); if (skill->ValidHitRoomMsg ()) act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM); else act (AT_MAGIC, "$n disappears in a flash of light!", ch, NULL, NULL, TO_ROOM); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); if (skill->ValidHitDest ()) act (AT_MAGIC, skill->GetHitDest (), ch, NULL, victim, TO_NOTVICT); else act (AT_MAGIC, "$n appears in a flash of light!", ch, NULL, NULL, TO_ROOM); do_look (ch, "auto"); return rNONE; } ch_ret spell_teleport (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = (CCharacter*) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->GetInRoom ()->IsNoRecall () || (!ch->IsNpc () && victim->GetFightData ()) || (victim != ch && (saves_spell_staff (level, victim) || saves_wands (level, victim)))) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData *pRoomIndex; do { pRoomIndex = RoomTable.GetRoom (number_range (0, 2097152000)); if (pRoomIndex) if (! pRoomIndex->IsPrivate () && ! pRoomIndex->IsSolitary () && ! pRoomIndex->IsNoAstral () && ! pRoomIndex->GetArea ()->IsNoTeleport () && ! pRoomIndex->IsPrototype () && ! pRoomIndex->IsDeathRoom () && in_hard_range (ch, pRoomIndex->GetArea ())) break; } while (1); act (AT_MAGIC, "$n slowly fades out of view.", victim, NULL, NULL, TO_ROOM); victim->RemoveFromRoom (); victim->SendToRoom (pRoomIndex); act (AT_MAGIC, "$n slowly fades into view.", victim, NULL, NULL, TO_ROOM); do_look (victim, "auto"); return rNONE; } ch_ret spell_ventriloquate (int sn, int level, CCharacter *ch, void *vo) { char buf1 [MAX_STRING_LENGTH]; char buf2 [MAX_STRING_LENGTH]; char speaker [MAX_INPUT_LENGTH]; target_name = one_argument (target_name, speaker); sprintf (buf1, "%s says '%s'.\n\r", speaker, target_name); sprintf (buf2, "Someone makes %s say '%s'.\n\r", speaker, target_name); buf1 [0] = UPPER (buf1 [0]); CCharacter *vch = ch->GetInRoom ()->first_person; for (; vch; vch = vch->GetNextInRoom ()) { if (! is_name (speaker, vch->GetName ())) { set_char_color (AT_SAY, vch); vch->SendText (saves_spell_staff (level, vch) ? buf2 : buf1); } } return rNONE; } ch_ret spell_weaken (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter*) vo; CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); set_char_color (AT_MAGIC, ch); if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (is_affected (victim, sn) || saves_wands (level, victim)) { ch->SendText ("Your magic fails to take hold.\n\r"); return rSPELL_FAILED; } af.type = sn; af.duration = (short)(level / 2 * DUR_CONV); af.location = APPLY_STR; af.modifier = -2; af.bitvector = -1; affect_to_char (victim, &af); set_char_color (AT_MAGIC, victim); victim->SendText ("Your muscles seem to atrophy!\n\r"); if (ch != victim) { if ((((! victim->IsNpc () && ClassTable.GetAttrPrime (victim->GetClass ()) == APPLY_STR) || victim->IsNpc ()) && victim->GetCurrentStrength () < 25) || victim->GetCurrentStrength () < 20) { act (AT_MAGIC, "$N labors weakly as your spell atrophies $S muscles.", ch, NULL, victim, TO_CHAR ); act (AT_MAGIC, "$N labors weakly as $n's spell atrophies $S muscles.", ch, NULL, victim, TO_NOTVICT ); } else { act (AT_MAGIC, "You induce a mild atrophy in $N's muscles.", ch, NULL, victim, TO_CHAR ); act (AT_MAGIC, "$n induces a mild atrophy in $N's muscles.", ch, NULL, victim, TO_NOTVICT ); } } return rNONE; } // A spell as it should be -Thoric ch_ret spell_word_of_recall (int sn, int level, CCharacter *ch, void *vo) { do_recall ((CCharacter *) vo, ""); return rNONE; } // NPC spells. ch_ret spell_acid_breath (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CObjData *obj_lose; int dam; int hpch; if (chance (ch, 2 * level) && ! saves_breath (level, victim)) { POSITION pos = victim->GetHeadCarryPos (); while (obj_lose = victim->GetNextCarrying (pos)) { int iWear; if (number_bits (2) != 0) continue; switch (obj_lose->item_type) { case ITEM_ARMOR: if (obj_lose->value [0] > 0) { separate_obj (obj_lose); act (AT_DAMAGE, "$p is pitted and etched!", victim, obj_lose, NULL, TO_CHAR); if ((iWear = obj_lose->wear_loc) != WEAR_NONE) victim->AddArmor (-apply_ac (obj_lose, iWear)); obj_lose->value [0] -= 1; obj_lose->cost = 0; if (iWear != WEAR_NONE) victim->AddArmor (apply_ac (obj_lose, iWear)); } break; case ITEM_CONTAINER: separate_obj (obj_lose); act (AT_DAMAGE, "$p fumes and dissolves!", victim, obj_lose, NULL, TO_CHAR); act (AT_OBJECT, "The contents of $p spill out onto the ground.", victim, obj_lose, NULL, TO_ROOM); act (AT_OBJECT, "The contents of $p spill out onto the ground.", victim, obj_lose, NULL, TO_CHAR); empty_obj (obj_lose, NULL, victim->GetInRoom ()); extract_obj (obj_lose); break; } } } hpch = UMAX (10, ch->GetHp ()); dam = number_range (hpch/16+1, hpch/8); if (saves_breath (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_fire_breath (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CObjData *obj_lose; int dam; int hpch; if (chance (ch, 2 * level) && ! saves_breath (level, victim)) { POSITION pos = victim->GetHeadCarryPos (); while (obj_lose = victim->GetNextCarrying (pos)) { char *msg; if (number_bits (2) != 0) 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_COOK: case ITEM_FOOD: msg = "$p blackens and crisps!"; break; case ITEM_PILL: msg = "$p melts and drips!"; break; } separate_obj (obj_lose); act (AT_DAMAGE, msg, victim, obj_lose, NULL, TO_CHAR); if (obj_lose->item_type == ITEM_CONTAINER) { act (AT_OBJECT, "The contents of $p held by $N spill onto the ground.", victim, obj_lose, victim, TO_ROOM); act (AT_OBJECT, "The contents of $p spill out onto the ground.", victim, obj_lose, NULL, TO_CHAR); empty_obj (obj_lose, NULL, victim->GetInRoom ()); } extract_obj (obj_lose); } } hpch = UMAX (10, ch->GetHp ()); dam = number_range (hpch/16+1, hpch/8); if (saves_breath (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_frost_breath (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CObjData *obj_lose; int dam; int hpch; if (chance (ch, 2 * level) && ! saves_breath (level, victim)) { POSITION pos = victim->GetHeadCarryPos (); while (obj_lose = victim->GetNextCarrying (pos)) { char *msg; if (number_bits (2) != 0) continue; switch (obj_lose->item_type) { default: continue; case ITEM_CONTAINER: case ITEM_DRINK_CON: case ITEM_POTION: msg = "$p freezes and shatters!"; break; } separate_obj (obj_lose); act (AT_DAMAGE, msg, victim, obj_lose, NULL, TO_CHAR); if (obj_lose->item_type == ITEM_CONTAINER) { act (AT_OBJECT, "The contents of $p held by $N spill onto the ground.", victim, obj_lose, victim, TO_ROOM); act (AT_OBJECT, "The contents of $p spill out onto the ground.", victim, obj_lose, NULL, TO_CHAR); empty_obj (obj_lose, NULL, victim->GetInRoom ()); } extract_obj (obj_lose); } } hpch = UMAX (10, ch->GetHp ()); dam = number_range (hpch/16+1, hpch/8); if (saves_breath (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_gas_breath (int sn, int level, CCharacter *ch, void *vo) { CCharacter *vch; CCharacter *vch_next; int dam; int hpch; BOOL ch_died; ch_died = FALSE; if (ch->GetInRoom ()->IsSafe ()) { set_char_color (AT_MAGIC, ch); ch->SendText ("You fail to breathe.\n\r"); return rNONE; } for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) { vch_next = vch->GetNextInRoom (); if (! vch->IsNpc () && vch->IsWizInvis () && vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL) continue; if (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) { hpch = UMAX (10, ch->GetHp ()); dam = number_range (hpch/16+1, hpch/8); if (saves_breath (level, vch)) dam /= 2; if (damage (ch, vch, dam, sn) == rCHAR_DIED || char_died (ch)) ch_died = TRUE; } } return ch_died ? rCHAR_DIED : rNONE; } ch_ret spell_lightning_breath (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; int hpch = UMAX (10, ch->GetHp ()); int dam = number_range (hpch/16+1, hpch/8); if (saves_breath (level, victim)) dam /= 2; return damage (ch, victim, dam, sn); } ch_ret spell_null (int sn, int level, CCharacter *ch, void *vo) { ch->SendText ("That's not a spell!\n\r"); return rNONE; } // don't remove, may look redundant, but is important ch_ret spell_notfound (int sn, int level, CCharacter *ch, void *vo) { return spell_null (sn, level, ch, vo); } // Haus' Spell Additions // to do: portal (like mpcreatepassage) // enchant armour? (say -1/-2/-3 ac) // sharpness (makes weapon of caster's level) // repair (repairs armor) // blood burn (offensive) * name: net book of spells * // spirit scream (offensive) * name: net book of spells * // something about saltpeter or brimstone // Working on DM's transport eq suggestion - Scryn 8/13 ch_ret spell_transport (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CSkill *skill = SkillTable.GetValidSkill (sn); char arg3 [MAX_STRING_LENGTH]; CObjData *obj; target_name = one_argument (target_name, arg3); CCharacter *victim = get_char_world (ch, target_name); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || VRoom.IsNoRecall () || victim->GetLevel () >= (level + 15) || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim))) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->GetInRoom () == &CRoom) { ch->SendText ("They are right beside you!"); return rSPELL_FAILED; } if ((obj = get_obj_carry (ch, arg3)) == NULL || (victim->GetCarryWeight () + get_obj_weight (obj)) > can_carry_w (victim) || (victim->IsNpc () && victim->IsPrototype ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } separate_obj (obj); // altrag shoots, haus alley-oops! if (obj->IsNoDrop ()) { ch->SendText ("You can't seem to let go of it.\n\r"); return rSPELL_FAILED; // nice catch, caine } if (obj->IsPrototype () && victim->GetTrustLevel () < LEVEL_IMMORTAL) { ch->SendText ("That item is not for mortal hands to touch!\n\r"); return rSPELL_FAILED; // Thoric } act (AT_MAGIC, "$p slowly dematerializes...", ch, obj, NULL, TO_CHAR); act (AT_MAGIC, "$p slowly dematerializes from $n's hands..", ch, obj, NULL, TO_ROOM); obj_from_char (obj); obj_to_char (obj, victim); act (AT_MAGIC, "$p from $n appears in your hands!", ch, obj, victim, TO_VICT); act (AT_MAGIC, "$p appears in $n's hands!", victim, obj, NULL, TO_ROOM); if (SysData.IsSaveOnGive ()) save_char_obj (ch); if (SysData.IsSaveOnReceive ()) save_char_obj (victim); return rNONE; } // Syntax portal (mob/char) // opens a 2-way EX_PORTAL from caster's room to room inhabited by // mob or character won't mess with existing exits // // do_mp_open_passage, combined with spell_astral ch_ret spell_portal (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); // No go if all kinds of things aren't just right, including the caster // and victim are not both pkill or both peaceful. -- Narn if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsNoRecall () || VRoom.IsPrototype () || CRoom.IsNoRecall () || CRoom.IsNoAstral () || victim->GetLevel () >= level + 15 || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || (!victim->IsNpc () && ch->CanPkill () != victim->CanPkill ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (&VRoom == &CRoom) { ch->SendText ("They are right beside you!"); return rSPELL_FAILED; } CObjData *portalObj; CExitData *pexit; char buf [MAX_STRING_LENGTH]; // Check if there already is a portal in either room. for (pexit = CRoom.first_exit; pexit; pexit = pexit->GetNext ()) { if (pexit->IsPortal ()) { ch->SendText ("There is already a portal in this room.\n\r"); return rSPELL_FAILED; } if (pexit->vdir == DIR_PORTAL) { ch->SendText ("You may not create a portal in this room.\n\r"); return rSPELL_FAILED; } } for (pexit = VRoom.first_exit; pexit; pexit = pexit->GetNext ()) if (pexit->vdir == DIR_PORTAL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } pexit = make_exit (&CRoom, &VRoom, DIR_PORTAL); pexit->keyword = STRALLOC ("portal"); pexit->description = STRALLOC ("You gaze into the shimmering portal...\n\r"); pexit->key = -1; pexit->SetPortal (); pexit->SetCanEnter (); pexit->SetHidden (); pexit->SetCanLook (); pexit->vnum = VRoom.vnum; portalObj = create_object (OIdxTable.GetObj (OBJ_VNUM_PORTAL), 0); portalObj->timer = 3; sprintf (buf, "A portal created by %s", ch->GetName ()); portalObj->SetShortDescr (buf); // support for new casting messages if (! skill->ValidHitCharMsg ()) { set_char_color (AT_MAGIC, ch); ch->SendText ("You utter an incantation, and a portal forms " "in front of you!\n\r"); } else act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR); if (! skill->ValidHitRoomMsg ()) act (AT_MAGIC, "$n utters an incantation, and a portal forms in " "front of you!", ch, NULL, NULL, TO_ROOM); else act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM); if (! skill->ValidHitVictMsg ()) act (AT_MAGIC, "A shimmering portal forms in front of you!", victim, NULL, NULL, TO_ROOM); else act (AT_MAGIC, skill->GetHitVictMsg (), victim, NULL, victim, TO_ROOM); portalObj = obj_to_room (portalObj, &CRoom); pexit = make_exit (&VRoom, &CRoom, DIR_PORTAL); pexit->keyword = STRALLOC ("portal"); pexit->description = STRALLOC ("You gaze into the shimmering portal...\n\r"); pexit->key = -1; pexit->SetPortal (); pexit->SetCanEnter (); pexit->SetHidden (); pexit->vnum = VRoom.vnum; portalObj = create_object (OIdxTable.GetObj (OBJ_VNUM_PORTAL), 0); portalObj->timer = 3; portalObj->SetShortDescr (buf); portalObj = obj_to_room (portalObj, &VRoom); return rNONE; } ch_ret spell_farsight (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); // The spell fails if the victim isn't playing, the victim is the caster, // the target room has private, solitary, noastral, death or proto flags, // the caster's room is norecall, the victim is too high in level, the // victim is a proto mob, the victim makes the saving throw or the pkill // flag on the caster is not the same as on the victim. Got it? if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || CRoom.IsNoRecall () || victim->GetLevel () >= level + 15 || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || (!victim->IsNpc () && victim->CanPkill () && !ch->CanPkill ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } successful_casting (skill, ch, victim, NULL); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); do_look (ch, "auto"); ch->RemoveFromRoom (); ch->SendToRoom (&CRoom); return rNONE; } ch_ret spell_recharge (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj = (CObjData *) vo; if (obj->item_type == ITEM_STAFF || obj->item_type == ITEM_WAND) { separate_obj (obj); if (obj->value [2] == obj->value [1] || obj->value [1] > (obj->pIndexData->value [1] * 4)) { act (AT_FIRE, "$p bursts into flames, injuring you!", ch, obj, NULL, TO_CHAR); act (AT_FIRE, "$p bursts into flames, charring $n!", ch, obj, NULL, TO_ROOM); extract_obj (obj); if (damage (ch, ch, obj->level * 2, TYPE_UNDEFINED) == rCHAR_DIED || char_died (ch)) return rCHAR_DIED; else return rSPELL_FAILED; } if (chance (ch, 2)) { act (AT_YELLOW, "$p glows with a blinding magical luminescence.", ch, obj, NULL, TO_CHAR); obj->value [1] *= 2; obj->value [2] = obj->value [1]; return rNONE; } else if (chance (ch, 5)) { act (AT_YELLOW, "$p glows brightly for a few seconds...", ch, obj, NULL, TO_CHAR); obj->value [2] = obj->value [1]; return rNONE; } else if (chance (ch, 10)) { act (AT_WHITE, "$p disintegrates into a void.", ch, obj, NULL, TO_CHAR); act (AT_WHITE, "$n's attempt at recharging fails, and $p disintegrates.", ch, obj, NULL, TO_ROOM); extract_obj (obj); return rSPELL_FAILED; } else if (chance (ch, 50 - (ch->GetLevel () / 2))) { ch->SendText ("Nothing happens.\n\r"); return rSPELL_FAILED; } else { act (AT_MAGIC, "$p feels warm to the touch.", ch, obj, NULL, TO_CHAR); --obj->value [1]; obj->value [2] = obj->value [1]; return rNONE; } } else { ch->SendText ("You can't recharge that!\n\r"); return rSPELL_FAILED; } } // Idea from AD&D 2nd edition player's handbook (c)1989 TSR Hobbies Inc. // -Thoric ch_ret spell_plant_pass (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || (VRoom.sector_type != SECT_FOREST && VRoom.sector_type != SECT_FIELD) || (CRoom.sector_type != SECT_FOREST && CRoom.sector_type != SECT_FIELD) || CRoom.IsNoRecall () || victim->GetLevel () >= level + 15 || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ()) || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || ! in_hard_range (ch, VRoom.GetArea ()) || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (CRoom.sector_type == SECT_FOREST) act (AT_MAGIC, "$n melds into a nearby tree!", ch, NULL, NULL, TO_ROOM); else act (AT_MAGIC, "$n melds into the grass!", ch, NULL, NULL, TO_ROOM); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); if (CRoom.sector_type == SECT_FOREST) act (AT_MAGIC, "$n appears from behind a nearby tree!", ch, NULL, NULL, TO_ROOM); else act (AT_MAGIC, "$n grows up from the grass!", ch, NULL, NULL, TO_ROOM); do_look (ch, "auto"); return rNONE; } // Vampire version of astral_walk -Thoric ch_ret spell_mist_walk (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); BOOL allowday; if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (ch->IsPkiller () && ch->GetPcData ()->condition[COND_BLOODTHIRST] > 22) allowday = TRUE; else allowday = FALSE; CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if ((time_info.hour < 21 && time_info.hour > 5 && !allowday) || VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || CRoom.IsNoRecall () || victim->GetLevel () >= level + 15 || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ()) || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || !in_hard_range (ch, VRoom.GetArea ()) || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) { failed_casting (skill, ch, victim, NULL); ch->SendText ("You cannot sense your victim..."); return rSPELL_FAILED; } // Subtract 22 extra bp for mist walk from 0500 to 2100 SB if (time_info.hour < 21 && time_info.hour > 5 && !ch->IsNpc ()) gain_condition (ch, COND_BLOODTHIRST, - 22); act (AT_DGREEN, "$n dissolves into a cloud of glowing mist, then vanishes!", ch, NULL, NULL, TO_ROOM); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); act (AT_DGREEN, "A cloud of glowing mist engulfs you, then withdraws to unveil $n!", ch, NULL, NULL, TO_ROOM); do_look (ch, "auto"); return rNONE; } // Cleric version of astral_walk -Thoric ch_ret spell_solar_flight (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if ((time_info.hour > 18 || time_info.hour < 8) || ch->IsIndoors () || victim->IsIndoors () || weather_info.sky == SKY_RAINING || weather_info.sky == SKY_LIGHTNING || VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || CRoom.IsNoRecall () || victim->GetLevel () >= level + 15 || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ()) || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || !in_hard_range (ch, VRoom.GetArea ()) || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } act (AT_MAGIC, "$n disappears in a blinding flash of light!", ch, NULL, NULL, TO_ROOM); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); act (AT_MAGIC, "$n appears in a blinding flash of light!", ch, NULL, NULL, TO_ROOM); do_look (ch, "auto"); return rNONE; } // Scryn 2/2/96 ch_ret spell_remove_invis (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj; CSkill *skill = SkillTable.GetValidSkill (sn); if (target_name [0] == '\0') { ch->SendText ("What should the spell be cast upon?\n\r"); return rSPELL_FAILED; } obj = get_obj_carry (ch, target_name); if (obj) { if (! obj->IsInvisible ()) { ch->SendText ("Its not invisible!\n\r"); return rSPELL_FAILED; } obj->ClrInvisible (); act (AT_MAGIC, "$p becomes visible again.", ch, obj, NULL, TO_CHAR); ch->SendText ("Ok.\n\r"); return rNONE; } else { CCharacter *victim = get_char_room (ch, target_name); if (victim) { if (! can_see (ch, victim)) { ch->SendTextf ("You don't see %s!\n\r", target_name); return rSPELL_FAILED; } if (! victim->IsInvis ()) { ch->SendText ("They are not invisible!\n\r"); return rSPELL_FAILED; } if (is_safe (ch, victim)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (victim->IsImmuneMagic ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (! victim->IsNpc ()) { if (chance (ch, 50) && ch->GetLevel () + 10 < victim->GetLevel ()) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } else check_illegal_pk (ch, victim); } else { if (chance (ch, 50) && ch->GetLevel () + 15 < victim->GetLevel ()) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } } affect_strip (victim, gsn_invis); affect_strip (victim, gsn_mass_invis); victim->ClrInvis (); successful_casting (skill, ch, victim, NULL); return rNONE; } ch->SendTextf ("You can't find %s!\n\r", target_name); return rSPELL_FAILED; } } // Animate Dead: Scryn 3/2/96 // Modifications by Altrag 16/2/96 ch_ret spell_animate_dead (int sn, int level, CCharacter *ch, void *vo) { CCharacter *mob; CObjData *corpse; CObjData *obj; CAffectData af; char buf [MAX_STRING_LENGTH]; CSkill *skill = SkillTable.GetValidSkill (sn); CMobIndexData *pMobIndex; BOOL bFound = FALSE; POSITION pos = ch->GetInRoom ()->GetHeadContentPos (); while (corpse = ch->GetInRoom ()->GetNextContent (pos)) { if (corpse->item_type == ITEM_CORPSE_NPC && corpse->cost != -5) { bFound = TRUE; break; } } if (! bFound) { ch->SendText ("You cannot find a suitable corpse here.\n\r"); return rSPELL_FAILED; } if (MobTable.GetMob (MOB_VNUM_ANIMATED_CORPSE) == NULL) { bug ("Vnum 5 not found for spell_animate_dead!"); return rNONE; } if ((pMobIndex = MobTable.GetMob ((short) abs (corpse->cost))) == NULL) { bug ("Can not find mob for cost of corpse, spell_animate_dead"); return rSPELL_FAILED; } if (! ch->IsNpc ()) { if (ch->IsVampire ()) { if (ch->IsMortal () && ch->GetPcData ()->condition [COND_BLOODTHIRST] - (pMobIndex->level/3) < 0) { ch->SendText ( "You do not have enough blood power to reanimate this" " corpse.\n\r"); return rSPELL_FAILED; } gain_condition (ch, COND_BLOODTHIRST, pMobIndex->level / 3); } else if (ch->GetMana () - (pMobIndex->level*4) < 0) { ch->SendText ("You do not have enough mana to reanimate this " "corpse.\n\r"); return rSPELL_FAILED; } else ch->AddMana (-(pMobIndex->level*4)); } if (ch->IsImmortal () || (chance (ch, 75) && pMobIndex->level - ch->GetLevel () < 10)) { mob = create_mobile (MobTable.GetMob (MOB_VNUM_ANIMATED_CORPSE)); mob->SendToRoom (ch->GetInRoom ()); mob->SetLevel (UMIN (ch->GetLevel () / 2, pMobIndex->level)); mob->SetRace (pMobIndex->race); // should be undead // Fix so mobs wont have 0 hps and crash mud - Scryn 2/20/96 if (! pMobIndex->hitnodice) mob->SetMaxHp (pMobIndex->level * 8 + number_range ( pMobIndex->level * pMobIndex->level / 4, pMobIndex->level * pMobIndex->level)); else mob->SetMaxHp (dice (pMobIndex->hitnodice, pMobIndex->hitsizedice) + pMobIndex->hitplus); mob->SetMaxHp (UMAX (URANGE (mob->GetMaxHp () / 4, (mob->GetMaxHp () * corpse->value[3]) / 100, ch->GetLevel () * dice (20,10)), 1)); mob->SetHp (mob->GetMaxHp ()); mob->SetDamroll (ch->GetLevel () / 8); mob->SetHitroll (ch->GetLevel () / 6); mob->SetAlignment (ch->GetAlignment ()); act (AT_MAGIC, "$n makes $T rise from the grave!", ch, NULL, pMobIndex->GetShortDescr (), TO_ROOM); act (AT_MAGIC, "You make $T rise from the grave!", ch, NULL, pMobIndex->GetShortDescr (), TO_CHAR); sprintf (buf, "animated corpse %s", pMobIndex->GetPlayerName ()); mob->SetName (buf); sprintf (buf, "The animated corpse of %s", pMobIndex->GetShortDescr ()); mob->SetShortDescr (buf); sprintf (buf, "An animated corpse of %s struggles with the horror " "of its undeath.\n\r", pMobIndex->GetShortDescr ()); mob->SetLongDescr (buf); add_follower (mob, ch); af.type = sn; af.duration = (short)((number_fuzzy ((level + 1) / 4) + 1) * DUR_CONV); af.location = 0; af.modifier = 0; af.bitvector = AFF_CHARM; affect_to_char (mob, &af); if (! corpse->IsEmpty ()) { POSITION pos = corpse->GetHeadContentPos (); while (obj = corpse->GetNextContent (pos)) { obj_from_obj (obj); obj_to_room (obj, corpse->in_room); } } separate_obj (corpse); extract_obj (corpse); return rNONE; } else { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } } // Works now.. -- Altrag ch_ret spell_possess (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim; char buf [MAX_STRING_LENGTH]; CAffectData af; CSkill *skill = SkillTable.GetValidSkill (sn); if (ch->GetDesc ()->m_pOriginal) { ch->SendText ("You are not in your original state.\n\r"); return rSPELL_FAILED; } if ((victim = get_char_room (ch, target_name)) == NULL) { ch->SendText ("They aren't here!\n\r"); return rSPELL_FAILED; } if (victim == ch) { ch->SendText ("You can't possess yourself!\n\r"); return rSPELL_FAILED; } if (! victim->IsNpc ()) { ch->SendText ("You can't possess another player!\n\r"); return rSPELL_FAILED; } if (victim->GetDesc ()) { ch->SendTextf ("%s is already possessed.\n\r", victim->GetShortDescr ()); return rSPELL_FAILED; } if (victim->IsImmuneMagic () || victim->IsCharmed ()) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } BOOL bTry = ris_save (victim, level, RIS_CHARM); if (victim->IsPossessed () || level < (victim->GetLevel ()) || ch->IsCharmed () || victim->GetDesc () || saves_spell_staff (bTry, victim) || ! chance (ch, 25)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } af.type = sn; af.duration = 20 + (ch->GetLevel () - victim->GetLevel ()) / 2; af.location = 0; af.modifier = 0; af.bitvector = AFF_POSSESS; affect_to_char (victim, &af); sprintf (buf, "You have possessed %s!\n\r", victim->GetShortDescr ()); ch->GetDesc ()->m_pCharacter = victim; ch->GetDesc ()->m_pOriginal = ch; victim->SetDesc (ch->GetDesc ()); ch->SetDesc (NULL); ch->switched = victim; victim->SendText (buf); return rNONE; } // Ignores pickproofs, but can't unlock containers. -- Altrag 17/2/96 ch_ret spell_knock (int sn, int level, CCharacter *ch, void *vo) { CExitData *pexit; CSkill *skill = SkillTable.GetValidSkill (sn); set_char_color (AT_MAGIC, ch); // shouldn't know why it didn't work, and shouldn't work on pickproof // exits. -Thoric if (! (pexit=find_door (ch, target_name, FALSE)) || ! pexit->IsClosed () || ! pexit->IsLocked () || pexit->IsPickProof ()) { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } pexit->ClrLocked (); ch->SendText ("*Click*\n\r"); if (pexit->rexit && pexit->rexit->GetToRoom () == ch->GetInRoom ()) pexit->rexit->ClrLocked (); check_room_for_traps (ch, TRAP_UNLOCK | trap_door[pexit->vdir]); return rNONE; } // Tells to sleepers in are. -- Altrag 17/2/96 ch_ret spell_dream (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim; char arg [MAX_INPUT_LENGTH]; target_name = one_argument (target_name, arg); set_char_color (AT_MAGIC, ch); if (! (victim = get_char_world (ch, arg)) || victim->GetInRoom ()->GetArea () != ch->GetInRoom ()->GetArea ()) { ch->SendText ("They aren't here.\n\r"); return rSPELL_FAILED; } if (victim->GetPosition () != POS_SLEEPING) { ch->SendText ("They aren't asleep.\n\r"); return rSPELL_FAILED; } if (! target_name) { ch->SendText ("What do you want them to dream about?\n\r"); return rSPELL_FAILED; } set_char_color (AT_TELL, victim); victim->SendTextf ("You have dreams about %s telling you '%s'.\n\r", PERS (ch, victim), target_name); successful_casting (SkillTable.GetValidSkill (sn), ch, victim, NULL); return rNONE; } ch_ret spell_polymorph (int sn, int level, CCharacter *ch, void *vo) { int poly_vnum = -1; CCharacter *poly_mob; if (ch->IsNpc ()) { ch->SendText ("Mobs can't polymorph!\n\r"); return rSPELL_FAILED; } if (ch->GetDesc ()->m_pOriginal) { ch->SendText ("You are not in your original state.\n\r"); return rSPELL_FAILED; } if (! str_cmp (target_name, "wolf")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_WOLF; } else if (! str_cmp (target_name, "mist")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_MIST; } else if (! str_cmp (target_name, "bat")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_BAT; } else if (! str_cmp (target_name, "hawk")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_HAWK; } else if (! str_cmp (target_name, "cat")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_CAT; } else if (! str_cmp (target_name, "dove")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_DOVE; } else if (! str_cmp (target_name, "fish")) { if (CanMorph (*ch, target_name)) poly_vnum = MOB_VNUM_POLY_FISH; } if (poly_vnum == -1) { set_char_color (AT_MAGIC, ch); ch->SendText ("You can't polymorph into that!\n\r"); return rSPELL_FAILED; } poly_mob = make_poly_mob (ch, poly_vnum); if (! poly_mob) { bug ("Spell_polymorph: null polymob!"); return rSPELL_FAILED; } poly_mob->SendToRoom (ch->GetInRoom ()); ch->RemoveFromRoom (); ch->SendToRoom (RoomTable.GetRoom (SysData.m_RoomPoly)); ch->GetDesc ()->m_pCharacter = poly_mob; ch->GetDesc ()->m_pOriginal = ch; poly_mob->SetDesc (ch->GetDesc ()); ch->SetDesc (NULL); ch->switched = poly_mob; return rNONE; } BOOL CanMorph (CCharacter& Ch, const char* morph) { // Let Immortals & NPC's morph to anything if (Ch.IsImmortal () || Ch.IsNpc ()) return TRUE; return ClassTable.CanMorph (Ch.GetClass (), morph); } CCharacter *make_poly_mob (CCharacter *ch, int vnum) { CCharacter *mob; CMobIndexData *pMobIndex; if (!ch) { bug ("Make_poly_mob: null ch!", 0); return NULL; } if (vnum < 10 || vnum > 16) { bug ("Make_poly_mob: Vnum not in polymorphing mobs range", 0); return NULL; } if ((pMobIndex = MobTable.GetMob (vnum)) == NULL) { bug ("Make_poly_mob: Can't find mob %d", vnum); return NULL; } mob = create_mobile (pMobIndex); mob->SetPolymorphed (); return mob; } #ifdef XXXX // This is soo confusing that I rewrote it below - RCP void do_revert (CCharacter *ch, char *argument) { CCharacter *mob; if (! ch->IsNpc () || ! ch->IsPolymorphed ()) { ch->SendText ("You are not polymorphed.\n\r"); return; } ch->ClrPolymorphed (); ch->GetDesc ()->m_pOriginal->RemoveFromRoom (); if (ch->GetDesc ()->m_pCharacter) { mob = ch->GetDesc ()->m_pCharacter; ch->GetDesc ()->m_pOriginal->SendToRoom ( ch->GetDesc ()->m_pCharacter->GetInRoom ()); // WORKS!! ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal; ch->GetDesc ()->m_pOriginal = NULL; ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ()); ch->GetDesc ()->m_pCharacter->switched = NULL; ch->SetDesc (NULL); extract_char (mob, TRUE); return; } // else { // location = NULL; // if (ch->GetDesc ()->m_pOriginal->GetPcData ()->clan) // location = RoomTable.GetRoom (ch->GetDesc ()->m_pOriginal->GetPcData ()->clan->recall); // if (!location) // location = RoomTable.GetRoom (SysData.m_RoomTemple); // ch->GetDesc ()->m_pOriginal->SendToRoom (location); // } ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal; ch->GetDesc ()->m_pOriginal = NULL; ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ()); ch->GetDesc ()->m_pCharacter->switched = NULL; ch->SetDesc (NULL); } #endif void do_revert (CCharacter *ch, char *argument) { if (! ch->GetDesc ()) { bug ("do_revert:Null Descriptor on poly mob"); return; } CDescriptor &Ds = *ch->GetDesc (); CCharacter &Orig = *Ds.m_pOriginal; CCharacter &Mob = *ch; if (! Mob.IsNpc () || ! Mob.IsPolymorphed ()) { ch->SendText ("You are not polymorphed.\n\r"); return; } if (Ds.m_pCharacter != ch) bug ("do_revert:Poly Mob not in Descriptor"); Mob.ClrPolymorphed (); Orig.RemoveFromRoom (); // remove char from poly save room Orig.SendToRoom (Mob.GetInRoom ()); // WORKS!! Ds.m_pCharacter = &Orig; Ds.m_pOriginal = NULL; Orig.SetDesc (&Ds); Orig.switched = NULL; Mob.SetDesc (NULL); extract_char (&Mob, TRUE); } // Added spells spiral_blast, scorching surge, // nostrum, and astral by SB for Augurer class 7/10/96 ch_ret spell_spiral_blast (int sn, int level, CCharacter *ch, void *vo) { CCharacter *vch; CCharacter *vch_next; int dam; int hpch; BOOL ch_died; ch_died = FALSE; if (ch->GetInRoom ()->IsSafe ()) { set_char_color (AT_MAGIC, ch); ch->SendText ("You fail to breathe.\n\r"); return rNONE; } for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) { vch_next = vch->GetNextInRoom (); if (! vch->IsNpc () && vch->IsWizInvis () && vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL) continue; if (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) { act (AT_MAGIC, "Swirling colours radiate from $n" ", encompassing $N.", ch, ch, vch, TO_ROOM); act (AT_MAGIC, "Swirling colours radiate from you," " encompassing $N", ch, ch, vch , TO_CHAR); hpch = UMAX (10, ch->GetHp ()); dam = number_range (hpch/14+1, hpch/7); if (saves_breath (level, vch)) dam /= 2; if (damage (ch, vch, dam, sn) == rCHAR_DIED || char_died (ch)) ch_died = TRUE; } } return ch_died ? rCHAR_DIED : rNONE; } ch_ret spell_scorching_surge (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; static const short 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, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170 }; level = UMIN (level, DIM (dam_each) - 1); level = UMAX (0, level); int dam = number_range (dam_each [level] / 2, dam_each [level] * 2); if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "A fiery current lashes through $n's body!", ch, NULL, NULL, TO_ROOM); act (AT_MAGIC, "A fiery current lashes through your body!", ch, NULL, NULL, TO_CHAR); return damage (ch, victim, (int)(dam*1.4), sn); } ch_ret spell_helical_flow (int sn, int level, CCharacter *ch, void *vo) { ASSERT (ch); CCharacter *victim = get_char_world (ch, target_name); CSkill *skill = SkillTable.GetValidSkill (sn); if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } CRoomIndexData &CRoom = *ch->GetInRoom (); CRoomIndexData &VRoom = *victim->GetInRoom (); if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral () || VRoom.IsDeathRoom () || VRoom.IsPrototype () || CRoom.IsNoRecall () || victim->GetLevel () >= level + 15 || (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ()) || (victim->IsNpc () && victim->IsPrototype ()) || (victim->IsNpc () && saves_spell_staff (level, victim)) || !in_hard_range (ch, VRoom.GetArea ()) || (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } act (AT_MAGIC, "$n coils into an ascending column of colour," " vanishing into thin air.", ch, NULL, NULL, TO_ROOM); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); act (AT_MAGIC, "A coil of colours descends from above, " "revealing $n as it dissipates.", ch, NULL, NULL, TO_ROOM); do_look (ch, "auto"); return rNONE; } /******************************************************* * Everything after this point is part of SMAUG SPELLS * *******************************************************/ // saving throw check -Thoric BOOL check_save (int sn, int level, CCharacter *ch, CCharacter *victim) { CSkill *skill = SkillTable.GetValidSkill (sn); BOOL saved = FALSE; if (skill->IsPkSensitive () && ! ch->IsNpc () && ! victim->IsNpc ()) level /= 2; if (skill->GetSaves ()) switch (skill->GetSaves ()) { case SS_POISON_DEATH: saved = saves_poison_death (level, victim); break; case SS_ROD_WANDS: saved = saves_wands (level, victim); break; case SS_PARA_PETRI: saved = saves_para_petri (level, victim); break; case SS_BREATH: saved = saves_breath (level, victim); break; case SS_SPELL_STAFF: saved = saves_spell_staff (level, victim); break; } return saved; } // Generic offensive spell damage attack -Thoric ch_ret spell_attack (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); BOOL saved = check_save (sn, level, ch, victim); int dam; ch_ret retcode; if (saved && skill->GetSaveEffect () == SE_NEGATE) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (skill->GetDiceRoll ()) dam = UMAX (0, dice_parse (ch, level, skill->GetDiceRoll ())); else dam = dice (1, level/2); if (saved) { switch (skill->GetSaveEffect ()) { case SE_3QTRDAM: dam = (dam * 3) / 4; break; case SE_HALFDAM: dam >>= 1; break; case SE_QUARTERDAM: dam >>= 2; break; case SE_EIGHTHDAM: dam >>= 3; break; case SE_ABSORB: // victim absorbs spell for hp's act (AT_MAGIC, "$N absorbs your $t!", ch, skill->GetDamageMsg (), victim, TO_CHAR); act (AT_MAGIC, "You absorb $N's $t!", victim, skill->GetDamageMsg (), ch, TO_CHAR); act (AT_MAGIC, "$N absorbs $n's $t!", ch, skill->GetDamageMsg (), victim, TO_NOTVICT); victim->SetHp (URANGE (0, victim->GetHp () + dam, victim->GetMaxHp ())); update_pos (victim); if ((dam > 0 && ch->GetFightWho () == victim) || (dam > 0 && victim->GetFightWho () == ch)) { int xp = ch->GetFightData () ? ch->GetFightData ()->xp : victim->GetFightData ()->xp; int xp_gain = (int) (xp * dam * 2) / victim->GetMaxHp (); gain_exp (ch, 0 - xp_gain); } if (skill->GetAffects ()) retcode = spell_affectchar (sn, level, ch, victim); return retcode; case SE_REFLECT: // reflect the spell to the caster return spell_attack (sn, level, victim, ch); } } retcode = damage (ch, victim, dam, sn); if (retcode == rNONE && skill->GetAffects () && ! char_died (ch) && ! char_died (victim) && (! is_affected (victim, sn) || skill->IsAccumulative () || skill->IsRecastable ())) retcode = spell_affectchar (sn, level, ch, victim); return retcode; } // Generic area attack -Thoric ch_ret spell_area_attack (int sn, int level, CCharacter *ch, void *vo) { CCharacter *vch, *vch_next; CSkill *skill = SkillTable.GetValidSkill (sn); BOOL saved; BOOL affects; int dam; BOOL ch_died = FALSE; ch_ret retcode = rNONE; if (ch->GetInRoom ()->IsSafe ()) { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } affects = (skill->GetAffects () ? TRUE : FALSE); if (skill->ValidHitCharMsg ()) act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, NULL, TO_CHAR); if (skill->ValidHitRoomMsg ()) act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, NULL, TO_ROOM); for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) { vch_next = vch->GetNextInRoom (); if (!vch->IsNpc () && vch->IsWizInvis () && vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL) continue; if (vch == ch) continue; if (ch->IsNpc () ? !vch->IsNpc () : vch->IsNpc ()) { saved = check_save (sn, level, ch, vch); if (saved && skill->GetSaveEffect () == SE_NEGATE) { failed_casting (skill, ch, vch, NULL); continue; } else if (skill->GetDiceRoll ()) dam = dice_parse (ch, level, skill->GetDiceRoll ()); else dam = dice (1, level/2); if (saved) { switch (skill->GetSaveEffect ()) { case SE_3QTRDAM: dam = (dam * 3) / 4; break; case SE_HALFDAM: dam >>= 1; break; case SE_QUARTERDAM: dam >>= 2; break; case SE_EIGHTHDAM: dam >>= 3; break; case SE_ABSORB: // victim absorbs spell for hp's act (AT_MAGIC, "$N absorbs your $t!", ch, skill->GetDamageMsg (), vch, TO_CHAR); act (AT_MAGIC, "You absorb $N's $t!", vch, skill->GetDamageMsg (), ch, TO_CHAR); act (AT_MAGIC, "$N absorbs $n's $t!", ch, skill->GetDamageMsg (), vch, TO_NOTVICT); vch->SetHp (URANGE (0, vch->GetHp () + dam, vch->GetMaxHp ())); update_pos (vch); if ((dam > 0 && ch->GetFightWho () == vch) || (dam > 0 && vch->GetFightWho () == ch)) { int xp = ch->GetFightData () ? ch->GetFightData ()->xp : vch->GetFightData ()->xp; int xp_gain = (int) (xp * dam * 2) / vch->GetMaxHp (); gain_exp (ch, 0 - xp_gain); } continue; case SE_REFLECT: // reflect the spell to the caster retcode = spell_attack (sn, level, vch, ch); if (char_died (ch)) { ch_died = TRUE; break; } continue; } } retcode = damage (ch, vch, dam, sn); } if (retcode == rNONE && affects && !char_died (ch) && !char_died (vch) && (! is_affected (vch, sn) || skill->IsAccumulative () || skill->IsRecastable ())) retcode = spell_affectchar (sn, level, ch, vch); if (retcode == rCHAR_DIED || char_died (ch)) { ch_died = TRUE; break; } } return retcode; } ch_ret spell_affectchar (int sn, int level, CCharacter *ch, void *vo) { CAffectData af; CSmaugAffect *saf; CSkill *skill = SkillTable.GetValidSkill (sn); CCharacter *victim = (CCharacter *) vo; int chance; ch_ret retcode = rNONE; if (skill->IsRecastable ()) affect_strip (victim, sn); for (saf = skill->GetAffects (); saf; saf = saf->GetNext ()) { if (saf->location >= REVERSE_APPLY) victim = ch; else victim = (CCharacter *) vo; // Check if char has this bitvector already if ((af.bitvector = saf->bitvector) >= 0 && victim->IsAffected (af.bitvector) && ! skill->IsAccumulative ()) continue; // necessary for affect_strip to work properly... switch (af.bitvector) { default: af.type = sn; break; case AFF_POISON: af.type = gsn_poison; chance = ris_save (victim, level, RIS_POISON); if (chance == 1000) { retcode = rVICT_IMMUNE; if (skill->IsStopOnFail ()) return retcode; continue; } if (saves_poison_death (chance, victim)) { if (skill->IsStopOnFail ()) return retcode; continue; } victim->SetMentalState (URANGE (30, victim->GetMentalState () + 2, 100)); break; case AFF_BLIND: af.type = gsn_blindness; break; case AFF_CURSE: af.type = gsn_curse; break; case AFF_INVISIBLE: af.type = gsn_invis; break; case AFF_SLEEP: af.type = gsn_sleep; chance = ris_save (victim, level, RIS_SLEEP); if (chance == 1000) { retcode = rVICT_IMMUNE; if (skill->IsStopOnFail ()) return retcode; continue; } break; case AFF_CHARM: af.type = gsn_charm_person; chance = ris_save (victim, level, RIS_CHARM); if (chance == 1000) { retcode = rVICT_IMMUNE; if (skill->IsStopOnFail ()) return retcode; continue; } break; case AFF_POSSESS: af.type = gsn_possess; break; } af.duration = dice_parse (ch, level, saf->duration); af.modifier = dice_parse (ch, level, saf->modifier); af.location = saf->location % REVERSE_APPLY; if (af.duration == 0) { int xp_gain; switch (af.location) { case APPLY_HIT: victim->SetHp (URANGE (0, victim->GetHp () + af.modifier, victim->GetMaxHp ())); update_pos (victim); if ((af.modifier > 0 && ch->GetFightData () && ch->GetFightData ()->who == victim) || (af.modifier > 0 && victim->GetFightData () && victim->GetFightData ()->who == ch)) { int xp = ch->GetFightData () ? ch->GetFightData ()->xp : victim->GetFightData ()->xp; xp_gain = (int) (xp * af.modifier*2) / victim->GetMaxHp (); gain_exp (ch, 0 - xp_gain); } break; case APPLY_MANA: victim->SetMana (URANGE (0, victim->GetMana () + af.modifier, victim->GetMaxMana ())); update_pos (victim); break; case APPLY_MOVE: victim->SetMove (URANGE (0, victim->GetMove () + af.modifier, victim->GetMaxMove ())); update_pos (victim); break; default: affect_modify (victim, &af, TRUE); break; } } else if (skill->IsAccumulative ()) affect_join (victim, &af); else affect_to_char (victim, &af); } update_pos (victim); return retcode; } // Generic spell affect -Thoric ch_ret spell_affect (int sn, int level, CCharacter *ch, void *vo) { CSmaugAffect *saf; CSkill *skill = SkillTable.GetValidSkill (sn); CCharacter *victim = (CCharacter *) vo; BOOL hitchar, hitroom, hitvict = FALSE; ch_ret retcode; if (! skill->GetAffects ()) { bug ("spell_affect has no affects sn %d", sn); return rNONE; } BOOL bGroupSp = skill->IsGroupSpell (); BOOL bAreaSp = skill->IsAreaSpell (); if (! bGroupSp && ! bAreaSp) { // Can't find a victim if (! victim) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if ((skill->GetType () != SKILL_HERB && victim->IsImmuneMagic ()) || is_immune (victim, skill->GetSpellDamage ())) { immune_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } // Spell is already on this guy if (is_affected (victim, sn) && ! skill->IsAccumulative () && ! skill->IsRecastable ()) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if ((saf = skill->GetAffects ()) && !saf->GetNext () && saf->location == APPLY_STRIPSN && !is_affected (victim, dice_parse (ch, level, saf->modifier))) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } if (check_save (sn, level, ch, victim)) { failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } } else { if (skill->ValidHitCharMsg ()) { if (strstr (skill->GetHitCharMsg (), "$N")) hitchar = TRUE; else act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, NULL, TO_CHAR); } if (skill->ValidHitRoomMsg ()) { if (strstr (skill->GetHitRoomMsg (), "$N")) hitroom = TRUE; else act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, NULL, TO_ROOM); } if (skill->ValidHitVictMsg ()) hitvict = TRUE; if (victim) victim = victim->GetInRoom ()->first_person; else victim = ch->GetInRoom ()->first_person; } if (! victim) { bug ("spell_affect: could not find victim: sn %d", sn); failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } for (; victim; victim = victim->GetNextInRoom ()) { if (bGroupSp || bAreaSp) { if ((bGroupSp && ! is_same_group (victim, ch)) || victim->IsImmuneMagic () || is_immune (victim, skill->GetSpellDamage ()) || check_save (sn, level, ch, victim) || (! skill->IsRecastable () && is_affected (victim, sn))) continue; if (hitvict && ch != victim) { act (AT_MAGIC, skill->GetHitVictMsg (), ch, NULL, victim, TO_VICT); if (hitroom) { act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_NOTVICT); act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_CHAR); } } else if (hitroom) act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM); if (ch == victim) { if (hitvict) act (AT_MAGIC, skill->GetHitVictMsg (), ch, NULL, ch, TO_CHAR); else if (hitchar) act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, ch, TO_CHAR); } else if (hitchar) act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR); } retcode = spell_affectchar (sn, level, ch, victim); if (! bGroupSp && ! bAreaSp) { if (retcode == rVICT_IMMUNE) immune_casting (skill, ch, victim, NULL); else successful_casting (skill, ch, victim, NULL); break; } } return rNONE; } // Generic inventory object spell -Thoric ch_ret spell_obj_inv (int sn, int level, CCharacter *ch, void *vo) { CObjData *obj = (CObjData *) vo; CSkill *skill = SkillTable.GetValidSkill (sn); if (! obj) { failed_casting (skill, ch, NULL, NULL); return rNONE; } switch (skill->GetSpellAction ()) { default: case SA_NONE: return rNONE; case SA_CREATE: if (skill->IsWaterSpell ()) { // create water if (obj->item_type != ITEM_DRINK_CON) { ch->SendText ("It is unable to hold water.\n\r"); return rSPELL_FAILED; } if (obj->value [2] != LIQ_WATER && obj->value [1] != 0) { ch->SendText ("It contains some other liquid.\n\r"); return rSPELL_FAILED; } int water = UMIN ((skill->GetDiceRoll () ? dice_parse (ch, level, skill->GetDiceRoll ()) : level) * (weather_info.sky >= SKY_RAINING ? 2 : 1), obj->value[0] - obj->value[1]); if (water > 0) { separate_obj (obj); obj->value [2] = LIQ_WATER; obj->value [1] += water; if (! is_name ("water", obj->GetName ())) { char buf [MAX_STRING_LENGTH]; sprintf (buf, "%s water", obj->GetName ()); obj->SetName (buf); } } successful_casting (skill, ch, NULL, obj); return rNONE; } if (skill->IsFireSpell ()) { // burn object // return rNONE; } if (skill->IsPoisonSpell () // poison object || skill->IsDeathSpell ()) { switch (obj->item_type) { default: failed_casting (skill, ch, NULL, obj); break; case ITEM_COOK: case ITEM_FOOD: case ITEM_DRINK_CON: separate_obj (obj); obj->value [3] = 1; successful_casting (skill, ch, NULL, obj); break; } return rNONE; } if (skill->IsLifeSpell () // purify food/water && (obj->item_type == ITEM_FOOD || obj->item_type == ITEM_DRINK_CON || obj->item_type == ITEM_COOK)) { switch (obj->item_type) { default: failed_casting (skill, ch, NULL, obj); break; case ITEM_FOOD: case ITEM_DRINK_CON: separate_obj (obj); obj->value [3] = 0; successful_casting (skill, ch, NULL, obj); break; } return rNONE; } if (! skill->HasNoSpellClass ()) { failed_casting (skill, ch, NULL, obj); return rNONE; } switch (skill->GetSpellPower ()) { // clone object CObjData *clone; default: case SP_NONE: if (ch->GetLevel () - obj->level < 10 || obj->cost > ch->GetLevel () * ch->GetIntelligence () * ch->GetWisdom ()) { failed_casting (skill, ch, NULL, obj); return rNONE; } break; case SP_MINOR: if (ch->GetLevel () - obj->level < 20 || obj->cost > ch->GetLevel () * ch->GetIntelligence () / 5) { failed_casting (skill, ch, NULL, obj); return rNONE; } break; case SP_GREATER: if (ch->GetLevel () - obj->level < 5 || obj->cost > ch->GetLevel () * 10 * ch->GetIntelligence () * ch->GetWisdom ()) { failed_casting (skill, ch, NULL, obj); return rNONE; } break; case SP_MAJOR: if (ch->GetLevel () - obj->level < 0 || obj->cost > ch->GetLevel () * 50 * ch->GetIntelligence () * ch->GetWisdom ()) { failed_casting (skill, ch, NULL, obj); return rNONE; } break; clone = clone_object (obj); clone->timer = skill->GetDiceRoll () ? dice_parse (ch, level, skill->GetDiceRoll ()) : 0; obj_to_char (clone, ch); successful_casting (skill, ch, NULL, obj); } return rNONE; case SA_DESTROY: case SA_RESIST: case SA_SUSCEPT: case SA_DIVINATE: if (skill->IsPoisonSpell ()) { // detect poison if (obj->item_type == ITEM_DRINK_CON || obj->item_type == ITEM_FOOD || obj->item_type == ITEM_COOK) { if (obj->item_type == ITEM_COOK && obj->value [2] == 0) ch->SendText ("It looks undercooked.\n\r"); else if (obj->value [3] != 0) ch->SendText ("You smell poisonous fumes.\n\r"); else ch->SendText ("It looks very delicious.\n\r"); } else ch->SendText ("It doesn't look poisoned.\n\r"); return rNONE; } return rNONE; case SA_OBSCURE: // make obj invis if (obj->IsInvisible () || chance (ch, skill->GetDiceRoll () ? dice_parse (ch, level, skill->GetDiceRoll ()) : 20)) { failed_casting (skill, ch, NULL, NULL); return rSPELL_FAILED; } successful_casting (skill, ch, NULL, obj); obj->SetInvisible (); return rNONE; case SA_CHANGE: return rNONE; } return rNONE; } // Generic object creating spell -Thoric ch_ret spell_create_obj (int sn, int level, CCharacter *ch, void *vo) { CSkill *skill = SkillTable.GetValidSkill (sn); int lvl; int vnum = skill->GetValue (); CObjData *obj; switch (skill->GetSpellPower ()) { default: case SP_NONE: lvl = 10; break; case SP_MINOR: lvl = 0; break; case SP_GREATER: lvl = level/2; break; case SP_MAJOR: lvl = level; break; } // Add predetermined objects here if (vnum == 0) { if (! str_cmp (target_name, "sword")) vnum = SysData.m_ObjSword; if (! str_cmp (target_name, "shield")) vnum = SysData.m_ObjShield; } CObjIndexData *oi = OIdxTable.GetObj (vnum); if (oi == NULL || (obj = create_object (oi, lvl)) == NULL) { failed_casting (skill, ch, NULL, NULL); return rNONE; } obj->timer = skill->GetDiceRoll () ? dice_parse (ch, level, skill->GetDiceRoll ()) : 0; successful_casting (skill, ch, NULL, obj); if (obj->CanWear (ITEM_TAKE)) obj_to_char (obj, ch); else obj_to_room (obj, ch->GetInRoom ()); return rNONE; } // Generic mob creating spell -Thoric ch_ret spell_create_mob (int sn, int level, CCharacter *ch, void *vo) { CSkill *skill = SkillTable.GetValidSkill (sn); int lvl; int vnum = skill->GetValue (); CCharacter *mob; CAffectData af; // set maximum mob level switch (skill->GetSpellPower ()) { default: case SP_NONE: lvl = 20; break; case SP_MINOR: lvl = 5; break; case SP_GREATER: lvl = level/2; break; case SP_MAJOR: lvl = level; break; } // Add predetermined mobiles here if (vnum == 0) { if (!str_cmp (target_name, "cityguard")) vnum = MOB_VNUM_CITYGUARD; if (!str_cmp (target_name, "vampire")) vnum = MOB_VNUM_VAMPIRE; } CMobIndexData *mi = MobTable.GetMob (vnum); if (mi == NULL || (mob = create_mobile (mi)) == NULL) { failed_casting (skill, ch, NULL, NULL); return rNONE; } mob->SetLevel (UMIN (lvl, skill->GetDiceRoll () ? dice_parse (ch, level, skill->GetDiceRoll () ) : mob->GetLevel ())); mob->SetArmor (interpolate (mob->GetLevel (), 100, -100)); mob->SetMaxHp (mob->GetLevel () * 8 + number_range ( mob->GetLevel () * mob->GetLevel () / 4, mob->GetLevel () * mob->GetLevel ())); mob->SetHp (mob->GetMaxHp ()); mob->SetGold (0); successful_casting (skill, ch, mob, NULL); mob->SendToRoom (ch->GetInRoom ()); add_follower (mob, ch); af.type = sn; af.duration = (short)((number_fuzzy ((level + 1) / 3) + 1) * DUR_CONV); af.location = 0; af.modifier = 0; af.bitvector = AFF_CHARM; affect_to_char (mob, &af); return rNONE; } // Generic handler for new "SMAUG" spells -Thoric ch_ret spell_smaug (int sn, int level, CCharacter *ch, void *vo) { CSkill *skill = SkillTable.GetValidSkill (sn); switch (skill->GetTarget ()) { case TAR_IGNORE: // offensive area spell if (skill->IsAreaSpell () && (skill->IsDestroySpell () && skill->IsLifeSpell ()) || (skill->IsCreateSpell () && skill->IsDeathSpell ())) return spell_area_attack (sn, level, ch, vo); if (skill->IsCreateSpell ()) { if (skill->IsObjectSpell ()) // create object return spell_create_obj (sn, level, ch, vo); if (skill->IsLifeSpell ()) // create mob return spell_create_mob (sn, level, ch, vo); } // affect a distant player if (skill->IsDistantSpell () && skill->IsCharacterSpell ()) return spell_affect (sn, level, ch, get_char_world (ch, target_name)); // affect a player in this room (should have been TAR_CHAR_XXX) if (skill->IsCharacterSpell ()) return spell_affect (sn, level, ch, get_char_room (ch, target_name)); // 1.4add if (skill->GetRange () > 0 && (skill->IsDestroySpell () && skill->IsLifeSpell ()) || (skill->IsCreateSpell () && skill->IsDeathSpell ())) return ranged_attack (ch, ranged_target_name, NULL, NULL, sn, skill->GetRange ()); // will fail, or be an area/group affect return spell_affect (sn, level, ch, vo); case TAR_CHAR_OFFENSIVE: // a regular damage inflicting spell attack if ((skill->IsDestroySpell () && skill->IsLifeSpell ()) || (skill->IsCreateSpell () && skill->IsDeathSpell ())) return spell_attack (sn, level, ch, vo); // a nasty spell affect return spell_affect (sn, level, ch, vo); case TAR_CHAR_DEFENSIVE: case TAR_CHAR_SELF: // 1.4add if (skill->IsNoFightSpell () && ch->IsFightPosition ()) { ch->SendText ("You can't concentrate enough for that!\n\r"); return rNONE; } if (vo && skill->IsDestroySpell ()) { CCharacter *victim = (CCharacter *) vo; // cure poison if (skill->IsPoisonSpell ()) { if (is_affected (victim, gsn_poison)) { affect_strip (victim, gsn_poison); victim->SetMentalState (URANGE (-100, victim->GetMentalState (), -10)); successful_casting (skill, ch, victim, NULL); return rNONE; } failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } // cure blindness if (skill->IsIllusionSpell ()) { if (is_affected (victim, gsn_blindness)) { affect_strip (victim, gsn_blindness); successful_casting (skill, ch, victim, NULL); return rNONE; } failed_casting (skill, ch, victim, NULL); return rSPELL_FAILED; } } return spell_affect (sn, level, ch, vo); case TAR_OBJ_INV: return spell_obj_inv (sn, level, ch, vo); } return rNONE; } // Haus' new, new mage spells follow // 4 Energy Spells ch_ret spell_ethereal_fist (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (35, level); int dam = (int) (1.3 * (level * number_range (1, 6))) - 31; dam = UMAX (0,dam); if (saves_spell_staff (level, victim)) dam /= 4; act (AT_MAGIC, "A fist of black, otherworldly ether rams into $N, " "leaving $M looking stunned!", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_spectral_furor (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (16, level); int dam = (int) (1.3 * (level*number_range (1, 7))) + 7; if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "The fabric of the cosmos strains in fury about $N!", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_hand_of_chaos (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (18, level); int dam = (int) (1.3 * (level*number_range (1, 7))) + 9; if (saves_spell_staff (level, victim)) dam /= 4; act (AT_MAGIC, "$N is grasped by an incomprehensible hand of chaos!", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_disruption (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (14, level); int dam = (int) (1.3 * (level*number_range (1, 6))) + 8; if (saves_spell_staff (level, victim)) dam = 0; act (AT_MAGIC, "A weird energy encompasses $N, causing you to " "question $S continued existence.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_sonic_resonance (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (23, level); int dam = (int) (1.3 * (level*number_range (1, 8))); if (saves_spell_staff (level, victim)) dam = dam * 3/4; act (AT_MAGIC, "A cylinder of kinetic energy enshrouds $N causing $S to" " resonate.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } // 3 Mentalstate spells ch_ret spell_mind_wrack (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; // decrement mentalstate by up to 50 level = UMAX (0, level); int dam = number_range (0, 0); if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "$n stares intently at $N, causing $N to seem very " "lethargic.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_mind_wrench (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; // increment mentalstate by up to 50 level = UMAX (0, level); int dam = number_range (0, 0); if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "$n stares intently at $N, causing $N to seem very " "hyperactive.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } // Non-offensive spell! ch_ret spell_revive (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; // set mentalstate to mentalstate/2 level = UMAX (0, level); int dam = number_range (0, 0); if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "$n concentrates intently, and begins looking more " "centered.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } // n Acid Spells ch_ret spell_sulfurous_spray (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (19, level); int dam = (int) (1.3 * (2 * level*number_range (1, 7))) + 11; if (saves_spell_staff (level, victim)) dam /= 4; act (AT_MAGIC, "A stinking spray of sulfurous liquid rains down on $N.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_caustic_fount (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (42, level); int dam = (int) (1.3 * (2*level*number_range (1, 6))) - 31; dam = UMAX (0,dam); if (saves_spell_staff (level, victim)) dam = dam * 1/2; act (AT_MAGIC, "A fountain of caustic liquid forms below $N. The smell" " of $S degenerating tissues is revolting! ", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_acetum_primus (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); int dam = (int) (1.3 * (2*level*number_range (1, 4))) + 7; if (saves_spell_staff (level, victim)) dam = 3 * dam/4; act (AT_MAGIC, "A cloak of primal acid enshrouds $N, sparks form as " "it consumes all it touches. ", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } // Electrical ch_ret spell_galvanic_whip (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (10, level); int dam = (int) (1.3 * (level*number_range (1, 6))) + 5; if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "$n conjures a whip of ionized particles, which lashes " "ferociously at $N.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_magnetic_thrust (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (29, level); int dam = (int) (.65 * (5*level*number_range (1, 6))) + 16; if (saves_spell_staff (level, victim)) dam /= 3; act (AT_MAGIC, "An unseen energy moves nearby, causing your hair to " "stand on end!", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_quantum_spike (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); int l = 2 * level/10; int dam = (int) (1.3 * (l * number_range (1,40))) + 145; if (saves_spell_staff (level, victim)) dam /= 2; act (AT_MAGIC, "$N seems to dissolve into tiny unconnected particles, " "then is painfully reassembled.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } // Black-magicish guys // L2 Mage Spell ch_ret spell_black_hand (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (5, level); int dam = (int) (1.3 * (level*number_range (1, 6))) + 3; if (saves_poison_death (level, victim)) dam /= 4; act (AT_MAGIC, "$n conjures a mystical hand, which swoops menacingly " "at $N.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_black_fist (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); level = UMIN (30, level); int dam = (int) (1.3 * (level*number_range (1, 9))) + 4; if (saves_poison_death (level, victim)) dam /= 4; act (AT_MAGIC, "$n conjures a mystical fist, which swoops menacingly " "at $N.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_black_lightning (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim = (CCharacter *) vo; level = UMAX (0, level); int l = 2 * level/10; int dam = (int) (1.3 * (l*number_range (1,50))) + 135; if (saves_poison_death (level, victim)) dam /= 4; act (AT_MAGIC, "$n conjures a mystical black thundercloud directly " "over $N's head.", ch, NULL, victim, TO_NOTVICT); return damage (ch, victim, dam, sn); } ch_ret spell_midas_touch (int sn, int level, CCharacter *ch, void *vo) { CCharacter *victim; int val; CObjData *obj = (CObjData *) vo; separate_obj (obj); // nice, alty :) if (obj->IsNoDrop ()) { ch->SendText ("You can't seem to let go of it.\n\r"); return rSPELL_FAILED; } if (obj->IsPrototype () && ch->GetTrustLevel () < LEVEL_IMMORTAL) { ch->SendText ("That item is not for mortal hands to touch!\n\r"); return rSPELL_FAILED; // Thoric } if (! obj->CanWear (ITEM_TAKE) || (obj->item_type == ITEM_CORPSE_NPC) || (obj->item_type == ITEM_CORPSE_PC)) { ch->SendText ("Your cannot seem to turn this item to gold!"); return rNONE; } val = obj->cost/2; val = UMAX (0, val); if (obj->item_type == ITEM_WEAPON) { switch (number_bits (2)) { case 0: victim = get_char_world (ch, "shmalnoth"); break; case 1: victim = get_char_world (ch, "midas001"); break; case 2: victim = get_char_world (ch, "midas003"); break; case 3: victim = get_char_world (ch, "shmalnak"); break; } } else if (obj->item_type == ITEM_ARMOR) { switch (number_bits (3)) { case 0: victim = get_char_world (ch, "shmalnoth"); break; case 1: victim = get_char_world (ch, "shmalnoth"); break; case 2: victim = get_char_world (ch, "midas001"); break; case 3: victim = get_char_world (ch, "midas001"); break; case 4: victim = get_char_world (ch, "midas003"); break; case 5: victim = get_char_world (ch, "midas005"); break; case 6: victim = get_char_world (ch, "crafter"); break; case 7: victim = get_char_world (ch, "crafter"); break; } } else if (obj->item_type == ITEM_SCROLL) { switch( number_bits (2)) { case 0: victim = get_char_world (ch, "tatorious"); break; case 1: victim = get_char_world (ch, "midas002"); break; case 2: victim = get_char_world (ch, "midas002"); break; case 3: victim = get_char_world (ch, "midas007"); break; } } else if (obj->item_type == ITEM_STAFF) { switch (number_bits (2)) { case 0: victim = get_char_world (ch, "tatorious"); break; case 1: victim = get_char_world (ch, "midas007"); break; case 2: victim = get_char_world (ch, "midas007"); break; case 3: victim = get_char_world (ch, "midas008"); break; } } else if (obj->item_type == ITEM_WAND) { switch (number_bits (2)) { case 0: victim = get_char_world (ch, "tatorious"); break; case 1: victim = get_char_world (ch, "midas006"); break; case 2: victim = get_char_world (ch, "midas007"); break; case 3: victim = get_char_world (ch, "midas008"); break; } } else { victim = get_char_world (ch, "terror"); } if (victim == NULL) { ch->AddGold (val); if (obj_extracted (obj)) return rNONE; if (cur_obj == obj->serial) global_objcode = rOBJ_SACCED; extract_obj (obj); ch->SendText ("O.K."); return rNONE; } if ((victim->GetCarryWeight () + get_obj_weight (obj)) > can_carry_w (victim) || (victim->IsNpc () && victim->IsPrototype ())) { ch->AddGold (val); if (obj_extracted (obj)) return rNONE; if (cur_obj == obj->serial) global_objcode = rOBJ_SACCED; extract_obj (obj); ch->SendText ("Ok\n\r"); return rNONE; } ch->AddGold (val); obj_from_char (obj); obj_to_char (obj, victim); ch->SendText ("You transmogrify the item to gold!\n\r"); return rNONE; }