Pyom.1.00a/
Pyom.1.00a/pysrc/miniboa/
"""
#**************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

#**************************************************************************
*   ROM 2.4 is copyright 1993-1998 Russ Taylor                             *
*   ROM has been brought to you by the ROM consortium                      *
*       Russ Taylor (rtaylor@hypercube.org)                                *
*       Gabrielle Taylor (gtaylor@hypercube.org)                           *
*       Brian Moore (zump@rom.org)                                         *
*   By using this code, you have agreed to follow the terms of the         *
*   ROM license, in the file Rom24/doc/rom.license                         *
***************************************************************************/
#***********
 * Ported to Python by Davion of MudBytes.net
 * Using Miniboa https://code.google.com/p/miniboa/
 * Now using Python 3 version https://code.google.com/p/miniboa-py3/
 ************/
"""

from merc import *
from handler import *
from fight import update_pos, is_safe, is_safe_spell, check_killer, multi_hit
from skills import check_improve
import const

def say_spell(ch, spell):
    syl_dict = {"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",  "ra":"gru", "fresh":"ima", "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" }
    incantation = mass_replace(spell.name, syl_dict)

    buf = "$n utters the words, '%s'." % incantation
    buf2 = "$n utters the words, '%s'." % spell.name

    for rch in ch.in_room.people:
        send = buf2 if ch.guild==rch.guild else buf
        act(send, ch, None, rch, TO_VICT)


def saves_spell(level, victim, dam_type):
    save = 50 + ( victim.level - level) * 5 - victim.saving_throw * 2
    if IS_AFFECTED(victim, AFF_BERSERK):
        save += victim.level//2
    
    immunity = victim.check_immune(dam_type)
    if immunity == IS_IMMUNE:
        return True
    elif immunity == IS_RESISTANT:
        save += 2
    elif immunity == IS_VULNERABLE:
        save -= 2

    if not IS_NPC(victim) and victim.guild.fMana:
        save = 9 * save // 10
    save = max( 5, min(save, 95 ) )

    return random.randint(1,99) < save

def saves_dispel(dis_level, spell_level, duration):
    if duration == -1:
      spell_level += 5  
      # very hard to dispel permanent effects */
    
    save = 50 + (spell_level - dis_level) * 5
    save = max( 5, min(save, 95 ) )
    return random.randint(1,99) < save

def check_dispel(dis_level, victim, skill):
    if is_affected(victim, skill):
        for af in victim.affected[:]:
            if af.type == skill:
                if not saves_dispel(dis_level,af.level,af.duration):
                    victim.affect_strip(sn)
                    if skill.msg_off:
                        victim.send(const._table[sn].msg_off + "\n")
                    return True
                else:
                    af.level -= 1
    return False


# for finding mana costs -- temporary version */
def mana_cost (ch, min_mana, level):
    if ch.level + 2 == level:
        return 1000
    return max(min_mana, (100 // (2 + ch.level - level)))

def find_spell(ch, name):
    #* finds a spell the character can cast if possible */
    found = -1
    if IS_NPC(ch):
        return prefix_lookup(const.skill_table,name)
    for key, sn in const.skill_table.items():
        if sn.name.lower().startswith(name.lower()):
            if found == -1:
                found = sn
        if ch.level >= sn.skill_level[ch.guild.name] and key in ch.pcdata.learned:
            return sn
    return found

target_name = ''

def do_cast(ch, argument):
     #* Switched NPC's can cast spells, but others can't.
    global target_name
    if IS_NPC(ch) and not ch.desc:
        return

    target_name, arg1 = read_word(argument)
    holder, arg2 = read_word(target_name)

    if not arg1:
        ch.send("Cast which what where?\n")
        return
    sn = find_spell(ch,arg1)
    if not sn or sn.spell_fun == spell_null \
    or (not IS_NPC(ch) and (ch.level < sn.skill_level[ch.guild.name] \
    or ch.pcdata.learned[sn.name] == 0)):
        ch.send("You don't know any spells of that name.\n")
        return
    if ch.position < sn.minimum_position:
        ch.send("You can't concentrate enough.\n")
        return
    if ch.level + 2 == sn.skill_level[ch.guild.name]:
        mana = 50
    else:
        mana = max(sn.min_mana, 100 // (2 + ch.level - sn.skill_level[ch.guild.name]))
    # Locate targets.
    victim=None
    obj=None
    vo=None
    target=TARGET_NONE
    if sn.target == TAR_IGNORE:
        pass
    elif sn.target == TAR_CHAR_OFFENSIVE:
        if not arg2:
            victim = ch.fighting
            if not victim:
                ch.send("Cast the spell on whom?\n")
                return
        else:
            victim = ch.get_char_room(target_name)
            if not victim:
                ch.send("They aren't here.\n")
                return
#            if ch == victim:
#                ch.send("You can't do that to yourself.\n")
#                return
            if not IS_NPC(ch):
                if is_safe(ch,victim) and victim != ch:
                    ch.send("Not on that target.\n")
                    return
                    
                check_killer(ch,victim)
                
            if IS_AFFECTED(ch, AFF_CHARM) and ch.master == victim:
                ch.send("You can't do that on your own follower.\n")
                return
            vo = victim
            target = TARGET_CHAR
    elif sn.target == TAR_CHAR_DEFENSIVE:
        if not arg2:
            victim = ch
        else:
            victim = ch.get_char_room(target_name)
        if not victim:
            ch.send("They aren't here.\n")
            return
        vo = victim
        target = TARGET_CHAR
    elif sn.target == TAR_CHAR_SELF:
        if arg2 and target_name not in ch.name.lower():
            ch.send("You can! cast this spell on a!her.\n")
            return
    
        vo = ch
        target = TARGET_CHAR
    elif sn.target == TAR_OBJ_INV:
        if not arg2:
            ch.send("What should the spell be cast upon?\n")
            return
        obj = ch.get_obj_carry(target_name, ch)
        if not obj:
            ch.send("You are not carrying that.\n")
            return
        vo = obj
        target = TARGET_OBJ
    elif sn.target == TAR_OBJ_CHAR_OFF:
        if not arg2:
            victim = ch.fighting
            if not victim:
                ch.send("Cast the spell on whom or what?\n")
                return
            target = TARGET_CHAR
        else:
            victim = ch.get_char_room(target_name)
            obj = ch.get_obj_here(target_name)
            if victim:
                target = TARGET_CHAR
                 # check the sanity of the attack */
                if is_safe_spell(ch,victim,False) and victim != ch:
                    ch.send("Not on that target.\n")
                    return
                if IS_AFFECTED(ch, AFF_CHARM) and ch.master == victim:
                    ch.send("You can't do that on your own follower.\n")
                    return
                if not IS_NPC(ch):
                    check_killer(ch,victim)
                vo = victim
            elif obj:
                vo = obj
                target = TARGET_OBJ
            else:
                ch.send("You don't see that here.\n")
                return
    elif sn.target == TAR_OBJ_CHAR_DEF:
        if not arg2:
            vo = ch
            target = TARGET_CHAR                                                 
        else:
            victim = ch.get_char_room(target_name)
            obj = ch.get_obj_carry(target_name, ch)
            if not victim:
                vo = victim
                target = TARGET_CHAR
            elif not obj:
                vo = obj
                target = TARGET_OBJ
            else:
                ch.send("You don't see that here.\n")
                return
    else:
        print ("BUG: Do_cast: bad target for sn %s." % sn)
        return
        
    if not IS_NPC(ch) and ch.mana < mana:
        ch.send("You don't have enough mana.\n")
        return
    
    if sn.name != "ventriloquate":
        say_spell(ch, sn)
    WAIT_STATE(ch, sn.beats)
     
    if random.randint(1,99) > ch.get_skill(sn.name):
        ch.send("You lost your concentration.\n")
        check_improve(ch,sn,False,1)
        ch.mana -= mana // 2
    else:
        ch.mana -= mana
        if IS_NPC(ch) or ch.guild.fMana:
            # class has spells */
            sn.spell_fun( sn, ch.level, ch, vo,target)
        else:
            sn.spell_fun(sn, 3 * ch.level // 4, ch, vo,target)
            check_improve(ch,sn,True,1)

    if (sn.target == TAR_CHAR_OFFENSIVE or  (sn.target == TAR_OBJ_CHAR_OFF and target == TARGET_CHAR)) \
    and victim != ch and victim.master != ch:
        for vch in ch.in_room.people[:]:
            if victim == vch and not victim.fighting:
                check_killer(victim,ch)
                multi_hit(victim, ch, TYPE_UNDEFINED)
                break
    return
# * Spell functions.

def spell_acid_blast(sn, level, ch, victim, target):
    dam = dice( level, 12 )
    if saves_spell( level, victim, DAM_ACID ):
        dam =  dam//2
    damage( ch, victim, dam, sn, DAM_ACID, True)

def spell_armor(sn, level, ch, victim, target):
    if is_affected( victim, sn ):
        if victim == ch:
            ch.send("You are already armored.\n")
        else:
            act("$N is already armored.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 24
    af.modifier  = -20
    af.location  = APPLY_AC
    af.bitvector = 0
    victim.affect_add(af)
    victim.send( "You feel someone protecting you.\n")
    if  ch is not victim:
        act("$N is protected by your magic.",ch,None,victim,TO_CHAR)

def spell_bless(sn, level, ch, victim, target):
    # deal with the object case first */
    if target == TARGET_OBJ:
        obj = victim
        if IS_OBJ_STAT(obj,ITEM_BLESS):
            act("$p is already blessed.",ch,obj,target=TO_CHAR)
            return
        if IS_OBJ_STAT(obj,ITEM_EVIL):
            paf = affect_find(obj.affected,"curse")
            level = obj.level
            if paf:
                level = paf.level
            if not saves_dispel(level,level,0):
                if paf:
                    obj.affect_remove(paf)
                    act("$p glows a pale blue.",ch,obj,None,TO_ALL)
                    REMOVE_BIT(obj.extra_flags,ITEM_EVIL)
                    return
                else:
                    act("The evil of $p is too powerful for you to overcome.", ch,obj,target=TO_CHAR)
                    return
        af = AFFECT_DATA()
        af.where    = TO_OBJECT
        af.type     = sn
        af.level    = level
        af.duration = 6 + level
        af.location = APPLY_SAVES
        af.modifier = -1
        af.bitvector    = ITEM_BLESS
        obj.affect_add(af)

        act("$p glows with a holy aura.",ch,obj,target=TO_ALL)

        if obj.wear_loc != WEAR_NONE:
            ch.saving_throw = ch.saving_throw-1
        return


    # character target */
    if victim.position == POS_FIGHTING or is_affected( victim, sn ):
        if victim == ch:
            ch.send("You are already blessed.\n")
        else:
            act("$N already has divine favor.",ch,None,victim,TO_CHAR)
        return
    
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 6+level
    af.location  = APPLY_HITROLL
    af.modifier  = level // 8
    af.bitvector = 0
    victim.affect_add(af)

    af.location  = APPLY_SAVING_SPELL
    af.modifier  = 0 - level // 8
    victim.affect_add(af)
    victim.send( "You feel righteous.\n")
    if  ch is not victim:
        act("You grant $N the favor of your god.",ch,None,victim,TO_CHAR)

def spell_blindness( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_BLIND) or saves_spell(level,victim,DAM_OTHER):
        return

    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.location  = APPLY_HITROLL
    af.modifier  = -4
    af.duration  = 1+level
    af.bitvector = AFF_BLIND
    victim.affect_add(af)
    victim.send( "You are blinded! \n")
    act("$n appears to be blinded.",victim,target=TO_ROOM)

def spell_burning_hands( sn, level, ch, victim, target ):
    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
    ]
    
    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2, dam_each[level] * 2 )
    if saves_spell( level, victim,DAM_FIRE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_FIRE,True)


def spell_call_lightning( sn, level, ch, victim, target ):
    if not IS_OUTSIDE(ch):
        ch.send("You must be out of doors.\n")
        return

    if weather_info.sky < SKY_RAINING:
        ch.send("You need bad weather.\n")
        return

    dam = dice(level//2, 8)

    ch.send("Mota's lightning strikes your foes! \n")
    act( "$n calls Mota's lightning to strike $s foes! ", ch, None, None, TO_ROOM )

    for vch in char_list[:]:
        if vch.in_room == None:
            continue
        if vch.in_room == ch.in_room:
            if vch is not ch and ( not IS_NPC(vch) if IS_NPC(ch) else IS_NPC(vch) ):
                damage( ch, vch, dam//2 if saves_spell( level,vch,DAM_LIGHTNING) else dam, sn,DAM_LIGHTNING,True)
            continue
    
        if vch.in_room.area == ch.in_room.area and IS_OUTSIDE(vch) and IS_AWAKE(vch):
            vch.send("Lightning flashes in the sky.\n")

# RT calm spell stops all fighting in the room */

def spell_calm( sn, level, ch, victim, target ):
    # get sum of all mobile levels in the room */
    count=0
    mlevel=0
    for vch in ch.in_room.people:
        if vch.position == POS_FIGHTING:
            count=count+1
        if IS_NPC(vch):
            mlevel += vch.level
        else:
            mlevel += vch.level//2
        high_level = max(high_level,vch.level)

    # compute chance of stopping combat */
    chance = 4 * level - high_level + 2 * count

    if IS_IMMORTAL(ch): # always works */
      mlevel = 0

    if random.randint(0, chance) >= mlevel: # hard to stop large fights */
        for vch in ch.in_room.people:
            if IS_NPC(vch) and (IS_SET(vch.imm_flags,IMM_MAGIC) or IS_SET(vch.act,ACT_UNDEAD)):
                return

            if IS_AFFECTED(vch,AFF_CALM) or IS_AFFECTED(vch,AFF_BERSERK) or  is_affected(vch,const.skill_table['frenzy']):
                return
            
            vch.send("A wave of calm passes over you.\n")

            if vch.fighting or vch.position == POS_FIGHTING:
                stop_fighting(vch,False)


            af.where = TO_AFFECTS
            af.type = sn
            af.level = level
            af.duration = level//4
            af.location = APPLY_HITROLL
            if not IS_NPC(vch):
              af.modifier = -5
            else:
              af.modifier = -2
            af.bitvector = AFF_CALM
            vch.affect_add(af)

            af.location = APPLY_DAMROLL
            vch.affect_add(af)

def spell_cancellation( sn, level, ch, victim, target ):
    
    found = False
    level += 2

    if (not IS_NPC(ch) and IS_NPC(victim) and not (IS_AFFECTED(ch, AFF_CHARM) and ch.master == victim) ) or (IS_NPC(ch) and not IS_NPC(victim)):
        ch.send("You failed, try dispel magic.\n")
        return

    # unlike dispel magic, the victim gets NO save */
    # begin running through the spells */
 
    spells = { 'armor':None,
               'bless':None,
               'blindness': '$n is no longer blinded',
               'calm': '$n no longer looks so peaceful...',
               'change sex': '$n looks more like $mself again.',
               'charm person': '$n regains $s free will.',
               'chill touch': '$n looks warmer',
               'curse':None,
               'detect evil':None,
               'detect good':None,
               'detect hidden':None,
               'detect invis':None,
               'detect magic':None,
               'faerie fire':"$n's outline fades",
               'fly':'$n falls to the ground! ',
               'frenzy': "$n no longer looks so wild.",
               'giant strength': "$n no longer looks so mighty.",
               'haste': '$n is no longer moving so quickly',
               'infravision':None,
               'invis':'$n fades into existence.',
               'mass invis': '$n fades into existence',
               'pass door': None,
               'protection evil': None,
               'protection good': None,
               'sanctuary': "The white aura around $n's body vanishes.",
               'shield': 'The shield protecting $n vanishes',
               'sleep': None,
               'slow': '$n is no longer moving so slowly.',
               'stone skin': "$n's skin regains its normal texture.",
               'weaken': "$n looks stronger." }

    for k,v in spells.items():
        if check_dispel(level,victim,const.skill_table[k]):
            if v:
                act(v,victim,None,None,TO_ROOM)
            found = True
    
    if found:
        ch.send("Ok.\n")
    else:
        ch.send("Spell failed.\n")

def spell_cause_light( sn, level, ch, victim, target ):
    damage( ch,victim, dice(1, 8) + level // 3, sn,DAM_HARM,True)
    return

def spell_cause_critical( sn, level, ch, victim, target ):
    damage( ch, victim, dice(3, 8) + level - 6, sn,DAM_HARM,True)
    return

def spell_cause_serious( sn, level, ch, victim, target ):
    damage( ch, victim, dice(2, 8) + level // 2, sn,DAM_HARM,True)
    return

def spell_chain_lightning( sn, level, ch, victim, target ):
    #H first strike */
    act("A lightning bolt leaps from $n's hand and arcs to $N.",ch,None,victim,TO_ROOM)
    act("A lightning bolt leaps from your hand and arcs to $N.",ch,None,victim,TO_CHAR)
    act("A lightning bolt leaps from $n's hand and hits you! ",ch,None,victim,TO_VICT)  

    dam = dice(level,6)
    if saves_spell(level,victim,DAM_LIGHTNING):
        dam = dam//3
    damage(ch,victim,dam,sn,DAM_LIGHTNING,True)
    last_vict = victim
    level = level-4   # decrement damage */

    # new targets */
    while level > 0:
        found = False
        for tmp_vict in ch.in_room.people:
            if  not is_safe_spell(ch,tmp_vict,True) and tmp_vict is not last_vict:
                found = True
                last_vict = tmp_vict
                act("The bolt arcs to $n! ",tmp_vict,None,None,TO_ROOM)
                act("The bolt hits you! ",tmp_vict,None,None,TO_CHAR)
                dam = dice(level,6)
                if saves_spell(level,tmp_vict,DAM_LIGHTNING):
                    dam = dam//3
                damage(ch,tmp_vict,dam,sn,DAM_LIGHTNING,True)
                level = level-4  # decrement damage */
        
        if not found: # no target found, hit the caster */
            if ch == None:
                return

            if last_vict == ch: # no double hits */
                act("The bolt seems to have fizzled out.",ch,None,None,TO_ROOM)
                act("The bolt grounds out through your body.", ch,None,None,TO_CHAR)
                return
    

            last_vict = ch
            act("The bolt arcs to $n...whoops! ",ch,None,None,TO_ROOM)
            ch.send("You are struck by your own lightning! \n")
            dam = dice(level,6)
            if saves_spell(level,ch,DAM_LIGHTNING):
                dam = dam // 3
            damage(ch,ch,dam,sn,DAM_LIGHTNING,True)
            level = level-4  # decrement damage */
            if ch == None:
                return
   
def spell_change_sex( sn, level, ch, victim, target ):
    if is_affected( victim, sn ):
        if victim == ch:
            ch.send("You've already been changed.\n")
        else:
            act("$N has already had $s(?) sex changed.",ch,None,victim,TO_CHAR)
        return

    if saves_spell(level , victim,DAM_OTHER):
        return 
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 2 * level
    af.location  = APPLY_SEX

    while af.modifier == 0:
        af.modifier = random.randint( 0, 2 ) - victim.sex
    
    af.bitvector = 0
    victim.affect_add(af)
    victim.send("You feel different.\n")
    act("$n doesn't look like $mself anymore...",victim,None,None,TO_ROOM)

def spell_charm_person( sn, level, ch, victim, target ):
    if is_safe(ch,victim):
        return

    if victim == ch:
        ch.send("You like yourself even better! \n")
        return

    if ( IS_AFFECTED(victim, AFF_CHARM) \
    or   IS_AFFECTED(ch, AFF_CHARM) \
    or   level < victim.level \
    or   IS_SET(victim.imm_flags,IMM_CHARM) \
    or   saves_spell( level, victim,DAM_CHARM) ):
        return


    if IS_SET(victim.in_room.room_flags,ROOM_LAW):
        ch.send("The mayor does not allow charming in the city limits.\n")
        return
  
    if victim.master:
        stop_follower( victim )
    add_follower( victim, ch )
    victim.leader = ch
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = number_fuzzy( level // 4 )
    af.location  = 0
    af.modifier  = 0
    af.bitvector = AFF_CHARM
    victim.affect_add(af)
    act( "Isn't $n just so nice?", ch, None, victim, TO_VICT )
    if ch is not victim:
        act("$N looks at you with adoring eyes.",ch,None,victim,TO_CHAR)

def spell_chill_touch( sn, level, ch, victim, target ):
    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 ]

    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2, dam_each[level] * 2 )
    if not saves_spell( level, victim,DAM_COLD ):
        act("$n turns blue and shivers.",victim,None,None,TO_ROOM)
        af.where     = TO_AFFECTS
        af.type      = sn
        af.level     = level
        af.duration  = 6
        af.location  = APPLY_STR
        af.modifier  = -1
        af.bitvector = 0
        victim.affect_join(af)
    else:
        dam = dam//2
    damage( ch, victim, dam, sn, DAM_COLD,True )
    
def spell_colour_spray( sn, level, ch, victim, target ):
    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 ]
    
    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2,  dam_each[level] * 2 )
    if saves_spell( level, victim,DAM_LIGHT):
        dam = dam//2
    else:
        spell_blindness(const.skill_table["blindness"], level//2,ch,victim,TARGET_CHAR)

    damage( ch, victim, dam, sn, DAM_LIGHT,True )

def spell_continual_light( sn, level, ch, victim, target ):
    if victim:  # do a glow on some object */
        light = ch.get_obj_carry(victim, ch)
    
        if not light:
            ch.send("You don't see that here.\n")
            return


        if IS_OBJ_STAT(light,ITEM_GLOW):
            act("$p is already glowing.",ch,light,None,TO_CHAR)
            return

        SET_BIT(light.extra_flags,ITEM_GLOW)
        act("$p glows with a white light.",ch,light,None,TO_ALL)
        return


    light = create_object( obj_index_hash[OBJ_VNUM_LIGHT_BALL], 0 )
    light.to_room(ch.in_room)
    act( "$n twiddles $s thumbs and $p appears.",   ch, light, None, TO_ROOM )
    act( "You twiddle your thumbs and $p appears.", ch, light, None, TO_CHAR )

def spell_control_weather( sn, level, ch, victim, target ): 
    if victim.lower() == "better":
        weather_info.change += dice( level // 3, 4 )
    elif victim.lower() == "worse":
        weather_info.change -= dice( level // 3, 4 )
    else:
        ch.send("Do you want it to get better or worse?\n")

    ch.send("Ok.\n")
    return

def spell_create_food( sn, level, ch, victim, target ):
    mushroom = create_object( obj_index_hash[OBJ_VNUM_MUSHROOM], 0 )
    mushroom.value[0] = level // 2
    mushroom.value[1] = level
    mushroom.to_room(ch.in_room)
    act( "$p suddenly appears.", ch, mushroom, None, TO_ROOM )
    act( "$p suddenly appears.", ch, mushroom, None, TO_CHAR )
    return

def spell_create_rose( sn, level, ch, victim, target ):
    rose = create_object(obj_index_hash[OBJ_VNUM_ROSE], 0)
    act("$n has created a beautiful red rose.",ch,rose,None,TO_ROOM)
    ch.send("You create a beautiful red rose.\n")
    rose.to_char(ch)

def spell_create_spring( sn, level, ch, victim, target ):
    spring = create_object( obj_index_hash[OBJ_VNUM_SPRING], 0 )
    spring.timer = level
    spring.to_room(ch.in_room)
    act( "$p flows from the ground.", ch, spring, None, TO_ROOM )
    act( "$p flows from the ground.", ch, spring, None, TO_CHAR )

def spell_create_water( sn, level, ch, victim, target ):
    if obj.item_type != ITEM_DRINK_CON:
        ch.send("It is unable to hold water.\n")
        return

    if obj.value[2] != LIQ_WATER and obj.value[1] != 0:
        ch.send("It contains some other liquid.\n")
        return

    water = min( level * (4 if weather_info.sky >= SKY_RAINING else 2), obj.value[0] - obj.value[1] )
  
    if water > 0:
        obj.value[2] = LIQ_WATER
        obj.value[1] += water
        if "water" in obj.name.lower():
            obj.name = "%s water" % obj.name

        act( "$p is filled.", ch, obj, None, TO_CHAR )
    
def spell_cure_blindness( sn, level, ch, victim, target ):
    if not is_affected( victim, const.skill_table['blindness'] ):
        if victim == ch:
            ch.send("You aren't blind.\n")
        else:
            act("$N doesn't appear to be blinded.",ch,None,victim,TO_CHAR)
        return
 
    if check_dispel(level,victim,const.skill_table['blindness']):
        victim.send("Your vision returns! \n")
        act("$n is no longer blinded.",victim,None,None,TO_ROOM)
    else:
        ch.send("Spell failed.\n")

def spell_cure_critical( sn, level, ch, victim, target ):
    heal = dice(3, 8) + level - 6
    victim.hit = min( victim.hit + heal, victim.max_hit )
    update_pos( victim )
    victim.send("You feel better! \n")
    if ch != victim:
        ch.send("Ok.\n")

# RT added to cure plague */
def spell_cure_disease( sn, level, ch, victim, target ):
    if not is_affected( victim, const.skill_table['plague'] ):
        if victim == ch:
            ch.send("You aren't ill.\n")
        else:
            act("$N doesn't appear to be diseased.",ch,None,victim,TO_CHAR)
        return
    
    if check_dispel(level,victim,const.skill_table['plague']):
        victim.send("Your sores vanish.\n")
        act("$n looks relieved as $s sores vanish.",victim,None,None,TO_ROOM)
        return

    ch.send("Spell failed.\n")

def spell_cure_light( sn, level, ch, victim, target ):
    heal = dice(1, 8) + level // 3
    victim.hit = min( victim.hit + heal, victim.max_hit )
    update_pos( victim )
    victim.send("You feel better! \n")
    if ch != victim:
        ch.send("Ok.\n")
    return

def spell_cure_poison( sn, level, ch, victim, target ):
    if not is_affected( victim, const.skill_table['poison'] ):
        if victim == ch:
            ch.send("You aren't poisoned.\n")
        else:
          act("$N doesn't appear to be poisoned.",ch,None,victim,TO_CHAR)
        return
 
    if check_dispel(level,victim,const.skill_table['poison']):
        victim.send("A warm feeling runs through your body.\n")
        act("$n looks much better.",victim,None,None,TO_ROOM)
        return

    ch.send("Spell failed.\n")


def spell_cure_serious( sn, level, ch, victim, target ):
    heal = dice(2, 8) + level //2 
    victim.hit = min( victim.hit + heal, victim.max_hit )
    update_pos( victim )
    victim.send("You feel better! \n")
    if ch != victim:
        ch.send("Ok.\n")
    
def spell_curse( sn, level, ch, victim, target ):
    # deal with the object case first */
    if target == TARGET_OBJ:
        obj = victim
        if IS_OBJ_STAT(obj,ITEM_EVIL):
            act("$p is already filled with evil.",ch,obj,None,TO_CHAR)
            return

        if IS_OBJ_STAT(obj,ITEM_BLESS):
            paf = affect_find(obj.affected, const.skill_table["bless"])
            if not saves_dispel(level, paf.level if paf != None else obj.level, 0):
                if paf:
                    obj.affect_remove(paf)
                act("$p glows with a red aura.", ch, obj, None, TO_ALL)
                REMOVE_BIT(obj.extra_flags, ITEM_BLESS)
                return
            else:
                act("The holy aura of $p is too powerful for you to overcome.", ch, obj, None, TO_CHAR)
                return
        af = AFFECT_DATA()
        af.where = TO_OBJECT
        af.type = sn
        af.level = level
        af.duration = 2 * level
        af.location = APPLY_SAVES
        af.modifier = +1
        af.bitvector = ITEM_EVIL
        obj.affect_add(af)

        act("$p glows with a malevolent aura.",ch,obj,None,TO_ALL)

        if obj.wear_loc != WEAR_NONE:
            ch.saving_throw += 1
        return

    # character curses */
    if IS_AFFECTED(victim,AFF_CURSE) or saves_spell(level,victim,DAM_NEGATIVE):
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 2*level
    af.location  = APPLY_HITROLL
    af.modifier  = -1 * (level // 8)
    af.bitvector = AFF_CURSE
    victim.affect_add(af)

    af.location  = APPLY_SAVING_SPELL
    af.modifier  = level // 8
    victim.affect_add(af)

    victim.send("You feel unclean.\n")
    if ch != victim:
        act("$N looks very uncomfortable.",ch,None,victim,TO_CHAR)

# RT replacement demonfire spell */

def spell_demonfire( sn, level, ch, victim, target ):
    if not IS_NPC(ch) and not IS_EVIL(ch):
        victim = ch
        ch.send("The demons turn upon you! \n")

    ch.alignment = max(-1000, ch.alignment - 50)

    if victim != ch:
        act("$n calls forth the demons of Hell upon $N! ", ch,None,victim,TO_ROOM)
        act("$n has assailed you with the demons of Hell! ", ch,None,victim,TO_VICT)
        ch.send("You conjure forth the demons of hell! \n")
    dam = dice( level, 10 )
    if saves_spell( level, victim,DAM_NEGATIVE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_NEGATIVE ,True)
    spell_curse(const.skill_table['curse'], 3 * level // 4, ch, victim,TARGET_CHAR)

def spell_detect_evil( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_DETECT_EVIL):
        if victim == ch:
            ch.send("You can already sense evil.\n")
        else:
            act("$N can already detect evil.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.modifier  = 0
    af.location  = APPLY_NONE
    af.bitvector = AFF_DETECT_EVIL
    victim.affect_add(af)
    victim.send("Your eyes tingle.\n")
    if ch != victim:
        ch.send("Ok.\n")

def spell_detect_good( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_DETECT_GOOD):
        if victim == ch:
            ch.send("You can already sense good.\n")
        else:
            act("$N can already detect good.",ch,None,victim,TO_CHAR)
        return
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.modifier  = 0
    af.location  = APPLY_NONE
    af.bitvector = AFF_DETECT_GOOD
    victim.affect_add(af)
    victim.send("Your eyes tingle.\n")
    if ch != victim:
        ch.send("Ok.\n")

def spell_detect_hidden( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_DETECT_HIDDEN):
        if victim == ch:
            ch.send("You are already as alert as you can be. \n")
        else:
            act("$N can already sense hidden lifeforms.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = AFF_DETECT_HIDDEN
    victim.affect_add(af)
    victim.send("Your awareness improves.\n")
    if ch != victim:
        ch.send("Ok.\n")

def spell_detect_invis( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_DETECT_INVIS):
        if victim == ch:
            ch.send("You can already see invisible.\n")
        else:
            act("$N can already see invisible things.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.modifier  = 0
    af.location  = APPLY_NONE
    af.bitvector = AFF_DETECT_INVIS
    victim.affect_add(af)
    victim.send("Your eyes tingle.\n")
    if ch != victim:
        ch.send("Ok.\n")

def spell_detect_magic( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_DETECT_MAGIC):
        if victim == ch:
            ch.send("You can already sense magical auras.\n")
        else:
            act("$N can already detect magic.",ch,None,victim,TO_CHAR)
        return

    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.modifier  = 0
    af.location  = APPLY_NONE
    af.bitvector = AFF_DETECT_MAGIC
    victim.affect_add(af)
    victim.send("Your eyes tingle.\n")
    if ch != victim:
        ch.send("Ok.\n")

def spell_detect_poison( sn, level, ch, victim, target ):
    if victim.item_type == ITEM_DRINK_CON or obj.item_type == ITEM_FOOD:
        if obj.value[3] != 0:
            ch.send("You smell poisonous fumes.\n")
        else:
            ch.send("It looks delicious.\n")
    else:
        ch.send("It doesn't look poisoned.\n")
    return

def spell_dispel_evil( sn, level, ch, victim, target ):
    if not IS_NPC(ch) and IS_EVIL(ch):
        victim = ch
  
    if IS_GOOD(victim):
        act( "Mota protects $N.", ch, None, victim, TO_ROOM )
        return

    if IS_NEUTRAL(victim):
        act( "$N does not seem to be affected.", ch, None, victim, TO_CHAR )
        return

    if victim.hit > (ch.level * 4):
        dam = dice( level, 4 )
    else:
        dam = max(victim.hit, dice(level,4))
    if saves_spell( level, victim,DAM_HOLY):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_HOLY ,True)

def spell_dispel_good( sn, level, ch, victim, target ):
    if not IS_NPC(ch) and IS_GOOD(ch):
        victim = ch
 
    if IS_EVIL(victim):
        act( "$N is protected by $S evil.", ch, None, victim, TO_ROOM )
        return

    if IS_NEUTRAL(victim):
        act( "$N does not seem to be affected.", ch, None, victim, TO_CHAR )
        return

    if victim.hit > (ch.level * 4):
        dam = dice( level, 4 )
    else:
        dam = max(victim.hit, dice(level,4))
    if saves_spell( level, victim,DAM_NEGATIVE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_NEGATIVE ,True)

# modified for enhanced use */
def spell_dispel_magic( sn, level, ch, victim, target ):
    if saves_spell(level, victim,DAM_OTHER):
        victim.send("You feel a brief tingling sensation.\n")
        ch.send("You failed.\n")
        return

    spells = { 'armor':None,
               'bless':None,
               'blindness': '$n is no longer blinded',
               'calm': '$n no longer looks so peaceful...',
               'change sex': '$n looks more like $mself again.',
               'charm person': '$n regains $s free will.',
               'chill touch': '$n looks warmer',
               'curse':None,
               'detect evil':None,
               'detect good':None,
               'detect hidden':None,
               'detect invis':None,
               'detect magic':None,
               'faerie fire':"$n's outline fades",
               'fly':'$n falls to the ground! ',
               'frenzy': "$n no longer looks so wild.",
               'giant strength': "$n no longer looks so mighty.",
               'haste': '$n is no longer moving so quickly',
               'infravision':None,
               'invis':'$n fades into existence.',
               'mass invis': '$n fades into existence',
               'pass door': None,
               'protection evil': None,
               'protection good': None,
               'sanctuary': "The white aura around $n's body vanishes.",
               'shield': 'The shield protecting $n vanishes',
               'sleep': None,
               'slow': '$n is no longer moving so slowly.',
               'stone skin': "$n's skin regains its normal texture.",
               'weaken': "$n looks stronger." }


    for k,v in spells.items():
        if check_dispel(level,victim,const.skill_table[k]):
            if v:
                act(v,victim,None,None,TO_ROOM)
            found = True

    if IS_AFFECTED(victim,AFF_SANCTUARY) and not saves_dispel(level, victim.level,-1) and not is_affected(victim,const.skill_table["sanctuary"]):
        REMOVE_BIT(victim.affected_by,AFF_SANCTUARY)
        act("The white aura around $n's body vanishes.", victim,None,None,TO_ROOM)
        found = True

 
    if found:
        ch.send("Ok.\n")
    else:
        ch.send("Spell failed.\n")

def spell_earthquake( sn, level, ch, victim, target ):
    ch.send("The earth trembles beneath your feet! \n")
    act( "$n makes the earth tremble and shiver.", ch, None, None, TO_ROOM )

    for vch in char_list[:]:
        if not vch.in_room:
            continue
        if vch.in_room == ch.in_room:
            if vch != ch and not is_safe_spell(ch,vch,True):
                if IS_AFFECTED(vch,AFF_FLYING):
                    damage(ch,vch,0,sn,DAM_BASH,True)
                else:
                    damage( ch,vch,level + dice(2, 8), sn, DAM_BASH,True)
            continue

        if vch.in_room.area == ch.in_room.area:
            vch.send("The earth trembles and shivers.\n")

def spell_enchant_armor( sn, level, ch, victim, target ):
    obj = victim
    if obj.item_type != ITEM_ARMOR:
        ch.send("That isn't an armor.\n")
        return

    if obj.wear_loc != -1:
        ch.send("The item must be carried to be enchanted.\n")
        return

    # this means they have no bonus */
    ac_bonus = 0
    ac_found = False
    fail = 25  # base 25% chance of failure */
    affected = obj.affected
    # find the bonuses */
    if not obj.enchanted:
        affected = obj.pIndexData.affected

    for paf in affected:
        if paf.location == APPLY_AC:
            ac_bonus = paf.modifier
            ac_found = True
            fail += 5 * (ac_bonus * ac_bonus)
        
        else: # things get a little harder */
            fail += 20

    # apply other modifiers */
    fail -= level

    if IS_OBJ_STAT(obj,ITEM_BLESS):
        fail -= 15
    if IS_OBJ_STAT(obj,ITEM_GLOW):
        fail -= 5

    fail = max(5,min(fail,85))

    result = random.randint(1,99)

    # the moment of truth */
    if result < (fail // 5):  # item destroyed */
        act("$p flares blindingly... and evaporates! ",ch,obj,None,TO_CHAR)
        act("$p flares blindingly... and evaporates! ",ch,obj,None,TO_ROOM)
        obj.extract()

    if result < (fail // 3): # item disenchanted */
        act("$p glows brightly, then fades...oops.",ch,obj,None,TO_CHAR)
        act("$p glows brightly, then fades.",ch,obj,None,TO_ROOM)
        obj.enchanted = True

        # remove all affects */
        obj.affected[:] = []

        # clear all flags */
        obj.extra_flags = 0
        return

    if result <= fail: # failed, no bad result */
        ch.send("Nothing seemed to happen.\n")
        return
    

    # okay, move all the old flags into new vectors if we have to */
    if not obj.enchanted:
        obj.enchanted = True
        for paf in obj.pIndexData.affected:
            af_new = AFFECT_DATA()
            af_new.where   = paf.where
            af_new.type    = max(0,paf.type)
            af_new.level   = paf.level
            af_new.duration    = paf.duration
            af_new.location    = paf.location
            af_new.modifier    = paf.modifier
            af_new.bitvector   = paf.bitvector
            obj.affected.append(af_new)

    if result <= (90 - level//5):  # success!  */
        act("$p shimmers with a gold aura.",ch,obj,None,TO_CHAR)
        act("$p shimmers with a gold aura.",ch,obj,None,TO_ROOM)
        SET_BIT(obj.extra_flags, ITEM_MAGIC)
        added = -1
    else:  # exceptional enchant */
        act("$p glows a brillant gold! ",ch,obj,None,TO_CHAR)
        act("$p glows a brillant gold! ",ch,obj,None,TO_ROOM)
        SET_BIT(obj.extra_flags,ITEM_MAGIC)
        SET_BIT(obj.extra_flags,ITEM_GLOW)
        added = -2
       
    # now add the enchantments */ 
    if obj.level < LEVEL_HERO:
        obj.level = min(LEVEL_HERO - 1,obj.level + 1)

    if ac_found:
        for paf in obj.affected:
            if paf.location == APPLY_AC:
                paf.type = sn
                paf.modifier += added
                paf.level = max(paf.level,level)
    else: # add a new affect */
        paf = AFFECT_DATA()

        paf.where  = TO_OBJECT
        paf.type   = sn
        paf.level  = level
        paf.duration   = -1
        paf.location   = APPLY_AC
        paf.modifier   =  added
        paf.bitvector  = 0
        obj.affected.append(paf)
    
def spell_enchant_weapon( sn, level, ch, victim, target ):
    obj = victim

    if obj.item_type != ITEM_WEAPON:
        ch.send("That isn't a weapon.\n")
        return

    if obj.wear_loc != -1:
        ch.send("The item must be carried to be enchanted.\n")
        return

    # this means they have no bonus */
    hit_bonus = 0
    dam_bonus = 0
    fail = 25  # base 25% chance of failure */
    dam_found = False
    hit_found = False
    # find the bonuses */
    affected = obj.affected
    if not obj.enchanted:
        affected = obj.pIndexData.affected

    for paf in affected:
        if paf.location == APPLY_HITROLL:
            hit_bonus = paf.modifier
            hit_found = True
            fail += 2 * (hit_bonus * hit_bonus)
        elif paf.location == APPLY_DAMROLL:
            dam_bonus = paf.modifier
            dam_found = True
            fail += 2 * (dam_bonus * dam_bonus)
        else: # things get a little harder */
            fail += 25

    # apply other modifiers */
    fail -= 3 * level//2

    if IS_OBJ_STAT(obj,ITEM_BLESS):
        fail -= 15
    if IS_OBJ_STAT(obj,ITEM_GLOW):
        fail -= 5

    fail = max(5,min(fail,95))

    result = random.randint(1,99)

    # the moment of truth */
    if result < (fail // 5):  # item destroyed */
        act("$p shivers violently and explodes! ",ch,obj,None,TO_CHAR)
        act("$p shivers violently and explodeds! ",ch,obj,None,TO_ROOM)
        obj.extract()
        return

    if result < (fail // 2): # item disenchanted */
        act("$p glows brightly, then fades...oops.",ch,obj,None,TO_CHAR)
        act("$p glows brightly, then fades.",ch,obj,None,TO_ROOM)
        obj.enchanted = True
        # remove all affects */
        obj.affected[:] = []

        # clear all flags */
        obj.extra_flags = 0
        return
    

    if result <= fail:  # failed, no bad result */
        ch.send("Nothing seemed to happen.\n")
        return

    # okay, move all the old flags into new vectors if we have to */
    if not obj.enchanted:
        obj.enchanted = True
        for paf in obj.pIndexData.affected:
            af_new = AFFECT_DATA()
            af_new.where   = paf.where
            af_new.type    = max(0,paf.type)
            af_new.level   = paf.level
            af_new.duration    = paf.duration
            affect_loc_nameew.location    = paf.location
            af_new.modifier    = paf.modifier
            af_new.bitvector   = paf.bitvector
            obj.affected.append(af_new)
    if result <= (100 - level//5):  # success!  */
        act("$p glows blue.",ch,obj,None,TO_CHAR)
        act("$p glows blue.",ch,obj,None,TO_ROOM)
        SET_BIT(obj.extra_flags, ITEM_MAGIC)
        added = 1
    else:  # exceptional enchant */
        act("$p glows a brillant blue! ",ch,obj,None,TO_CHAR)
        act("$p glows a brillant blue! ",ch,obj,None,TO_ROOM)
        SET_BIT(obj.extra_flags,ITEM_MAGIC)
        SET_BIT(obj.extra_flags,ITEM_GLOW)
        added = 2
      
    # now add the enchantments */ 
    if obj.level < LEVEL_HERO - 1:
        obj.level = min(LEVEL_HERO - 1,obj.level + 1)

    if dam_found:
        for paf in obj.affected:
            if  paf.location == APPLY_DAMROLL:
                paf.type = sn
                paf.modifier += added
                paf.level = max(paf.level,level)
                if paf.modifier > 4:
                    SET_BIT(obj.extra_flags,ITEM_HUM)
    else: # add a new affect */
        paf = AFFECT_DATA()

        paf.where  = TO_OBJECT
        paf.type   = sn
        paf.level  = level
        paf.duration   = -1
        paf.location   = APPLY_DAMROLL
        paf.modifier   =  added
        paf.bitvector  = 0
        obj.affected.append(paf)

    if hit_found:
        for paf in obj.affected:
            if paf.location == APPLY_HITROLL:
                paf.type = sn
                paf.modifier += added
                paf.level = max(paf.level,level)
                if  paf.modifier > 4:
                    SET_BIT(obj.extra_flags,ITEM_HUM)
    else: # add a new affect */
        paf = AFFECT_DATA()
 
        paf.type       = sn
        paf.level      = level
        paf.duration   = -1
        paf.location   = APPLY_HITROLL
        paf.modifier   =  added
        paf.bitvector  = 0
        obj.affected.append( paf )
#
#  Drain XP, MANA, HP.
#  Caster gains HP.

def spell_energy_drain( sn, level, ch, victim, target ):
    if victim != ch:
        ch.alignment = max(-1000, ch.alignment - 50)

    if saves_spell( level, victim,DAM_NEGATIVE):
        victim.send("You feel a momentary chill.\n")     
        return
    if victim.level <= 2:
        dam      = ch.hit + 1
    else:
        gain_exp( victim, 0 - random.randint( level//2, 3 * level // 2 ) )
        victim.mana    //= 2
        victim.move    //= 2
        dam      = dice(1, level)
        ch.hit     += dam

    victim.send("You feel your life slipping away! \n")
    ch.send("Wow....what a rush! \n")
    damage( ch, victim, dam, sn, DAM_NEGATIVE ,True)

def spell_fireball( sn, level, ch, victim, target ):
    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 ]

    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2, dam_each[level] * 2 )
    if saves_spell( level, victim, DAM_FIRE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_FIRE ,True)

def spell_fireproof( sn, level, ch, victim, target ):
    if IS_OBJ_STAT(obj,ITEM_BURN_PROOF):
        act("$p is already protected from burning.",ch,obj,None,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_OBJECT
    af.type      = sn
    af.level     = level
    af.duration  = number_fuzzy(level // 4)
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = ITEM_BURN_PROOF
 
    obj.affect_add(af)
 
    act("You protect $p from fire.",ch,obj,None,TO_CHAR)
    act("$p is surrounded by a protective aura.",ch,obj,None,TO_ROOM)

def spell_flamestrike( sn, level, ch, victim, target ):
    dam = dice(6 + level // 2, 8)
    if saves_spell( level, victim,DAM_FIRE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_FIRE ,True)

def spell_faerie_fire( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_FAERIE_FIRE):
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.location  = APPLY_AC
    af.modifier  = 2 * level
    af.bitvector = AFF_FAERIE_FIRE
    victim.affect_add(af)
    victim.send("You are surrounded by a pink outline.\n")
    act( "$n is surrounded by a pink outline.", victim, None, None, TO_ROOM )

def spell_faerie_fog( sn, level, ch, victim, target ):
    act( "$n conjures a cloud of purple smoke.", ch, None, None, TO_ROOM )
    ch.send("You conjure a cloud of purple smoke.\n")

    for ich in ch.in_room.people:
        if ich.invis_level > 0:
            continue

        if ich == ch or saves_spell( level, ich,DAM_OTHER):
            continue

        ich.affect_strip('invis')
        ich.affect_strip('mass_invis')
        ich.affect_strip('sneak')
        REMOVE_BIT(ich.affected_by, AFF_HIDE)
        REMOVE_BIT(ich.affected_by, AFF_INVISIBLE)
        REMOVE_BIT(ich.affected_by, AFF_SNEAK)
        act("$n is revealed! ", ich, None, None, TO_ROOM)
        ich.send("You are revealed! \n")

def spell_floating_disc( sn, level, ch, victim, target ):
    floating = ch.get_eq(WEAR_FLOAT)
    if floating and IS_OBJ_STAT(floating,ITEM_NOREMOVE):
        act("You can't remove $p.",ch,floating,None,TO_CHAR)
        return

    disc = create_object(obj_index_hash[OBJ_VNUM_DISC], 0)
    disc.value[0]  = ch.level * 10 # 10 pounds per level capacity */
    disc.value[3]  = ch.level * 5 # 5 pounds per level max per item */
    disc.timer     = ch.level * 2 - random.randint(0,level // 2) 

    act("$n has created a floating black disc.",ch,None,None,TO_ROOM)
    ch.send("You create a floating disc.\n")
    disc.to_char(ch)
    wear_obj(ch,disc,True)

def spell_fly( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_FLYING):
        if victim == ch:
            ch.send("You are already airborne.\n")
        else:
            act("$N doesn't need your help to fly.",ch,None,victim,TO_CHAR)
        return
    af = AFFECDT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level + 3
    af.location  = 0
    af.modifier  = 0
    af.bitvector = AFF_FLYING
    victim.affect_add(af)
    victim.send("Your feet rise off the ground.\n")
    act( "$n's feet rise off the ground.", victim, None, None, TO_ROOM )
    return

# RT clerical berserking spell */
def spell_frenzy( sn, level, ch, victim, target ):
    if is_affected(victim,sn) or IS_AFFECTED(victim,AFF_BERSERK):
        if victim == ch:
            ch.send("You are already in a frenzy.\n")
        else:
            act("$N is already in a frenzy.",ch,None,victim,TO_CHAR)
        return

    if is_affected(victim,const.skill_table['calm']):
        if victim == ch:
            ch.send("Why don't you just relax for a while?\n")
        else:
            act("$N doesn't look like $e wants to fight anymore.", ch,None,victim,TO_CHAR)
        return
    if (IS_GOOD(ch) and not IS_GOOD(victim)) or  \
    (IS_NEUTRAL(ch) and not IS_NEUTRAL(victim)) or \
    (IS_EVIL(ch) and not IS_EVIL(victim)):
        act("Your god doesn't seem to like $N",ch,None,victim,TO_CHAR)
        return

    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level // 3
    af.modifier  = level // 6
    af.bitvector = 0

    af.location  = APPLY_HITROLL
    victim.affect_add(af)

    af.location  = APPLY_DAMROLL
    victim.affect_add(af)

    af.modifier  = 10 * (level // 12)
    af.location  = APPLY_AC
    victim.affect_add(af)

    victim.send("You are filled with holy wrath! \n")
    act("$n gets a wild look in $s eyes! ",victim,None,None,TO_ROOM)

# RT ROM-style gate */
def spell_gate( sn, level, ch, victim, target ):
    victim = ch.get_char_world(target_name)
    if not victim \
    or   victim == ch \
    or   victim.in_room == None \
    or   not ch.can_see_room(victim.in_room)  \
    or   IS_SET(victim.in_room.room_flags, ROOM_SAFE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_PRIVATE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_SOLITARY) \
    or   IS_SET(victim.in_room.room_flags, ROOM_NO_RECALL) \
    or   IS_SET(ch.in_room.room_flags, ROOM_NO_RECALL) \
    or   victim.level >= level + 3 \
    or   (victim.is_clan() and not ch.is_same_clan(victim)) \
    or   (not IS_NPC(victim) and victim.level >= LEVEL_HERO) \
    or   (IS_NPC(victim) and IS_SET(victim.imm_flags,IMM_SUMMON)) \
    or   (IS_NPC(victim) and saves_spell( level, victim,DAM_OTHER) ):
        ch.send("You failed.\n")
        return
 
    if ch.pet and ch.in_room == ch.pet.in_room:
        gate_pet = True
    else:
        gate_pet = False
    
    act("$n steps through a gate and vanishes.",ch,None,None,TO_ROOM)
    ch.send("You step through a gate and vanish.\n")
    ch.from_room()
    ch.to_room(victim.in_room)

    act("$n has arrived through a gate.",ch,None,None,TO_ROOM)
    ch.do_look("auto")

    if gate_pet:
        act("$n steps through a gate and vanishes.",ch.pet,None,None,TO_ROOM)
        send_to_char("You step through a gate and vanish.\n",ch.pet)
        ch.pet.from_room()
        ch.pet.to_room(victim.in_room)
        act("$n has arrived through a gate.",ch.pet,None,None,TO_ROOM)
        ch.pet.do_look("auto")

def spell_giant_strength( sn, level, ch, victim, target ):
    if is_affected( victim, sn ):
        if victim == ch:
            ch.send("You are already as strong as you can get! \n")
        else:
            act("$N can't get any stronger.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.location  = APPLY_STR
    af.modifier  = 1 + (level >= 18) + (level >= 25) + (level >= 32)
    af.bitvector = 0
    victim.affect_add(af)
    victim.send("Your muscles surge with heightened power! \n")
    act("$n's muscles surge with heightened power.",victim,None,None,TO_ROOM)

def spell_harm( sn, level, ch, victim, target ):
    dam = max(  20, victim.hit - dice(1,4) )
    if saves_spell( level, victim,DAM_HARM):
        dam = min( 50, dam // 2 )
    dam = min( 100, dam )
    damage( ch, victim, dam, sn, DAM_HARM ,True)

# RT haste spell */
def spell_haste( sn, level, ch, victim, target ):
    if is_affected( victim, sn ) or IS_AFFECTED(victim,AFF_HASTE) or IS_SET(victim.off_flags,OFF_FAST):
        if victim == ch:
            ch.send("You can't move any faster! \n")
        else:
            act("$N is already moving as fast as $E can.", ch,None,victim,TO_CHAR)
        return
    if IS_AFFECTED(victim,AFF_SLOW):
        if not check_dispel(level,victim,const.skill_table["slow"]):
            if victim != ch:
                ch.send("Spell failed.\n")
            victim.send("You feel momentarily faster.\n")
            return
        act("$n is moving less slowly.",victim,None,None,TO_ROOM)
        return

    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    if victim == ch:
        af.duration  = level//2
    else:
        af.duration  = level//4
    af.location  = APPLY_DEX
    af.modifier  = 1 + (level >= 18) + (level >= 25) + (level >= 32)
    af.bitvector = AFF_HASTE
    victim.affect_add(af)
    victim.send("You feel yourself moving more quickly.\n")
    act("$n is moving more quickly.",victim,None,None,TO_ROOM)
    if ch != victim:
        ch.send("Ok.\n")

def spell_heal( sn, level, ch, victim, target ):
    victim.hit = min( victim.hit + 100, victim.max_hit )
    update_pos( victim )
    victim.send("A warm feeling fills your body.\n")
    if ch != victim:
        ch.send("Ok.\n")
    return


def spell_heat_metal( sn, level, ch, victim, target ):
    fail = True
 
    if not saves_spell(level + 2,victim,DAM_FIRE) and  not IS_SET(victim.imm_flags,IMM_FIRE):
        for obj_lose in victim.carrying[:]:
            if  random.randint(1,2 * level) > obj_lose.level \
            and not saves_spell(level,victim,DAM_FIRE) \
            and not IS_OBJ_STAT(obj_lose,ITEM_NONMETAL) \
            and not IS_OBJ_STAT(obj_lose,ITEM_BURN_PROOF):
                if obj_lose.item_type == ITEM_ARMOR:
                    if obj_lose.wear_loc != -1: # remove the item */
                        if victim.can_drop_obj(obj_lose) \
                        and  (obj_lose.weight // 10) < random.randint(1,2 * victim.get_curr_stat(STAT_DEX)) \
                        and  remove_obj( victim, obj_lose.wear_loc, True ):
                            act("$n yelps and throws $p to the ground! ", victim,obj_lose,None,TO_ROOM)
                            act("You remove and drop $p before it burns you.", victim,obj_lose,None,TO_CHAR)
                            dam += (random.randint(1,obj_lose.level) // 3)
                            obj_lose.from_char()
                            obj_lose.to_room(victim.in_room)
                            fail = False
                        else: # stuck on the body!  ouch!  */
                           act("Your skin is seared by $p! ",
                           victim,obj_lose,None,TO_CHAR)
                           dam += (random.randint(1,obj_lose.level))
                           fail = False
                    else: # drop it if we can */
                        if victim.can_drop_obj(obj_lose):
                            act("$n yelps and throws $p to the ground! ", victim,obj_lose,None,TO_ROOM)
                            act("You and drop $p before it burns you.", victim,obj_lose,None,TO_CHAR)
                            dam += (random.randint(1,obj_lose.level) // 6)
                            obj_lose.from_char()
                            obj_lose.to_room(victim.in_room)
                            fail = False
                        else: # can! drop */
                            act("Your skin is seared by $p! ", victim,obj_lose,None,TO_CHAR)
                            dam += (random.randint(1,obj_lose.level) // 2)
                            fail = False
                if obj_lose.item_type == ITEM_WEAPON:
                    if obj_lose.wear_loc != -1: # try to drop it */
                        if IS_WEAPON_STAT(obj_lose,WEAPON_FLAMING):
                            continue
                        if victim.can_drop_obj(obj_lose) and  remove_obj(victim,obj_lose.wear_loc,True):
                            act("$n is burned by $p, and throws it to the ground.", victim,obj_lose,None,TO_ROOM)
                            victim.send("You throw your red-hot weapon to the ground! \n")
                            dam += 1
                            obj_lose.from_char()
                            obj_lose.to_room(victim.in_room)
                            fail = False
                        else: # YOWCH!  */
                            victim.send("Your weapon sears your flesh! \n")
                            dam += random.randint(1,obj_lose.level)
                            fail = False
                    else: # drop it if we can */
                        if victim.can_drop_obj(obj_lose):
                            act("$n throws a burning hot $p to the ground! ", victim,obj_lose,None,TO_ROOM)
                            act("You and drop $p before it burns you.", victim,obj_lose,None,TO_CHAR)
                            dam += (random.randint(1,obj_lose.level) // 6)
                            obj_lose.from_char()
                            obj_lose.to_room(victim.in_room)
                            fail = False
                        else: # can! drop */
                            act("Your skin is seared by $p! ", victim,obj_lose,None,TO_CHAR)
                            dam += (random.randint(1,obj_lose.level) // 2)
                            fail = False
    if fail:
        ch.send("Your spell had no effect.\n")
        victim.send("You feel momentarily warmer.\n")
    else: # damage!  */
        if saves_spell(level,victim,DAM_FIRE):
            dam = 2 * dam // 3
        damage(ch,victim,dam,sn,DAM_FIRE,True)

# RT really nasty high-level attack spell */
def spell_holy_word( sn, level, ch, victim, target ):
    bless_num = const.skill_table['bless']
    curse_num = const.skill_table['curse'] 
    frenzy_num = const.skill_table['frenzy']

    act("$n utters a word of divine power! ",ch,None,None,TO_ROOM)
    ch.send("You utter a word of divine power.\n")
 
    for vch in ch.in_room.people[:]:
        if(IS_GOOD(ch) and IS_GOOD(vch)) or (IS_EVIL(ch) and IS_EVIL(vch)) or (IS_NEUTRAL(ch) and IS_NEUTRAL(vch)):
            vch.send("You feel full more powerful.\n")
            spell_frenzy(frenzy_num,level,ch,vch,TARGET_CHAR) 
            spell_bless(bless_num,level,ch,vch,TARGET_CHAR)
        elif (IS_GOOD(ch) and IS_EVIL(vch)) or (IS_EVIL(ch) and IS_GOOD(vch)):
            if not is_safe_spell(ch,vch,True):
                spell_curse(curse_num,level,ch,vch,TARGET_CHAR)
                vch.send("You are struck down! \n")
                dam = dice(level,6)
                damage(ch,vch,dam,sn,DAM_ENERGY,True)
        elif IS_NEUTRAL(ch):
            if not is_safe_spell(ch,vch,True):
                spell_curse(curse_num,level//2,ch,vch,TARGET_CHAR)
                vch.send("You are struck down! \n")
                dam = dice(level,4)
                damage(ch,vch,dam,sn,DAM_ENERGY,True)
    ch.send("You feel drained.\n")
    ch.move = 0
    ch.hit = hit // 2
 
def spell_identify( sn, level, ch, victim, target ):
    ch.send("Object '%s' is type %s, extra flags %s.\nWeight is %d, value is %d, level is %d.\n" % ( obj.name,
        item_name(obj.item_type),
        extra_bit_name( obj.extra_flags ),
        obj.weight // 10,
        obj.cost,
        obj.level ) )
    
    if obj.item_type == ITEM_SCROLL or obj.item_type == ITEM_POTION or obj.item_type == ITEM_PILL:
        ch.send("Level %d spells of:" % obj.value[0] )
        for i in obj.value:
            if i >= 0 and i < MAX_SKILL:
                ch.send(" '%s'" %  const.skill_table[i].name)
        ch.send(".\n")
    elif obj.item_type == ITEM_WAND or obj.item_type == ITEM_STAFF: 
        ch.send("Has %d charges of level %d" % ( obj.value[2], obj.value[0] ) )
        if obj.value[3] >= 0 and obj.value[3] < MAX_SKILL:
            ch.send( "' %s'" % const.skill_table[obj.value[3]].name)
        ch.send(".\n")
    elif obj.item_type ==  ITEM_DRINK_CON:
        ch.send("It holds %s-colored %s.\n" % ( liq_table[obj.value[2]].liq_color, liq_table[obj.value[2]].liq_name) )
        send_to_char(buf,ch)
    elif obj.item_type == ITEM_CONTAINER:
        ch.send("Capacity: %d#  Maximum weight: %d#  flags: %s\n" % (obj.value[0], obj.value[3], cont_bit_name(obj.value[1])))
        if obj.value[4] != 100:
            ch.send("Weight multiplier: %d%%\n" % obj.value[4])
    elif obj.item_type == ITEM_WEAPON:
        ch.send("Weapon type is ")
        
        weapons = {   WEAPON_EXOTIC:"exotic",
                    WEAPON_SWORD:"sword",
                    WEAPON_DAGGER:"dagger",
                    WEAPON_SPEAR:"spear//staff",
                    WEAPON_MACE:"mace//club",
                    WEAPON_AXE:"axe",
                    WEAPON_FLAIL:"flail",
                    WEAPON_WHIP:"whip",
                    WEAPON_POLEARM:"polearm" }

        if obj.value[0] not in weapons:
            ch.send("unknown")
        else:
            ch.send(weapons[obj.value[0]])

        if obj.pIndexData.new_format:
            ch.send("Damage is %dd%d (average %d).\n" % ( obj.value[1],obj.value[2], (1 + obj.value[2]) * obj.value[1] // 2) )
        else:
            ch.send("Damage is %d to %d (average %d).\n" % ( obj.value[1], obj.value[2], ( obj.value[1] + obj.value[2] ) // 2 ) )

        if obj.value[4]:  # weapon flags */
            ch.send("Weapons flags: %s\n" % weapon_bit_name(obj.value[4]))
    elif obj.item_type == ITEM_ARMOR:
        ch.send("Armor class is %d pierce, %d bash, %d slash, and %d vs. magic.\n" % ( obj.value[0], 
            obj.value[1], obj.value[2], obj.value[3] ) )

    affected = obj.affected
    if not obj.enchanted:
        affected.extend(obj.pIndexData.affected)

    for paf in affected:
        if paf.location != APPLY_NONE and paf.modifier != 0:
            ch.send("Affects %s by %d.\n" % ( affect_loc_name( paf.location ), paf.modifier ) )
            if paf.bitvector:
                if paf.where == TO_AFFECTS:
                    ch.send("Adds %s affect.\n" % affect_bit_name(paf.bitvector))
                elif paf.where == TO_OBJECT:
                    ch.send("Adds %s object flag.\n" % extra_bit_name(paf.bitvector))
                elif paf.where == TO_IMMUNE:
                    ch.send("Adds immunity to %s.\n" % imm_bit_name(paf.bitvector))
                elif paf.where == TO_RESIST:
                    ch.send("Adds resistance to %s.\n" % imm_bit_name(paf.bitvector))
                elif paf.where == TO_VULN:
                    ch.send("Adds vulnerability to %s.\n" % imm_bit_name(paf.bitvector))
                else:
                    ch.send("Unknown bit %d: %d\n" % (paf.where,paf.bitvector))
    
def spell_infravision( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_INFRARED):
        if victim == ch:
            ch.send("You can already see in the dark.\n")
        else:
            act("$N already has infravision.\n",ch,None,victim,TO_CHAR)
        return
    
    act( "$n's eyes glow red.\n", ch, None, None, TO_ROOM )
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 2 * level
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = AFF_INFRARED
    victim.affect_add(af)
    victim.send("Your eyes glow red.\n")
    return

def spell_invis( sn, level, ch, victim, target ):
    # object invisibility */
    if target == TARGET_OBJ:
        obj = victim
        if IS_OBJ_STAT(obj,ITEM_INVIS):
            act("$p is already invisible.",ch,obj,None,TO_CHAR)
            return
    
        af = AFFECT_DATA()    
        af.where    = TO_OBJECT
        af.type     = sn
        af.level    = level
        af.duration = level + 12
        af.location = APPLY_NONE
        af.modifier = 0
        af.bitvector    = ITEM_INVIS
        obj.affect_add(af)
        act("$p fades out of sight.",ch,obj,None,TO_ALL)
        return
    # character invisibility */
    if IS_AFFECTED(victim, AFF_INVISIBLE):
        return

    act( "$n fades out of existence.", victim, None, None, TO_ROOM )
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level + 12
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = AFF_INVISIBLE
    victim.affect_add(af)
    victim.send("You fade out of existence.\n")
    return

def spell_know_alignment( sn, level, ch, victim, target ):
    ap = victim.alignment

    if ap >  700: msg = "$N has a pure and good aura."
    elif ap >  350: msg = "$N is of excellent moral character."
    elif ap >  100: msg = "$N is often kind and thoughtful."
    elif ap > -100: msg = "$N doesn't have a firm moral commitment."
    elif ap > -350: msg = "$N lies to $S friends."
    elif ap > -700: msg = "$N is a black-hearted murderer."
    else: msg = "$N is the embodiment of pure evil! ."

    act( msg, ch, None, victim, TO_CHAR )
    return

def spell_lightning_bolt( sn, level, ch, victim, target ):
    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 ]

    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2, dam_each[level] * 2 )
    if saves_spell( level, victim,DAM_LIGHTNING):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_LIGHTNING ,True)

def spell_locate_object( sn, level, ch, victim, target ):
    found = False
    number = 0
    max_found = 200 if IS_IMMORTAL(ch) else 2 * level

    for obj in object_list:
        if not ch.can_see_obj(obj) or not is_name( target_name, obj.name ) \
        or  IS_OBJ_STAT(obj,ITEM_NOLOCATE) or random.randint(1,99) > 2 * level \
        or ch.level < obj.level:
            continue

        found = True
        number=number+1
        in_obj = obj
        while in_obj.in_obj:
            in_obj = in_obj.in_obj
        
        if in_obj.carried_by and ch.can_see(in_obj.carried_by):
            ch.send("one is carried by %s\n" % PERS(in_obj.carried_by, ch) )
        else:
            if IS_IMMORTAL(ch) and in_obj.in_room != None:
                ch.send("one is in %s [Room %d]\n" % (in_obj.in_room.name, in_obj.in_room.vnum) )
            else: 
                ch.send("one is in %s\n" % ( "somewhere" if in_obj.in_room == None else in_obj.in_room.name ) )
        
        if number >= max_found:
            break

    if not found:
        ch.send("Nothing like that in heaven or earth.\n")

def spell_magic_missile( sn, level, ch, victim, target ):
    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 ]
    
    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2, dam_each[level] * 2 )
    if saves_spell( level, victim,DAM_ENERGY):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_ENERGY ,True)

def spell_mass_healing( sn, level, ch, victim, target ):
    heal_num = const.skill_table['heal']
    refresh_num = const.skill_table['refresh'] 

    for gch in ch.in_room.people:
        if (IS_NPC(ch) and IS_NPC(gch) ) or ( not IS_NPC(ch) and not IS_NPC(gch)):
            spell_heal(heal_num,level,ch,gch,TARGET_CHAR)
            spell_refresh(refresh_num,level,ch,gch,TARGET_CHAR)  

def spell_mass_invis( sn, level, ch, victim, target ):
    for gch in ch.in_room.people:
        if not gch.is_same_group(ch) or IS_AFFECTED(gch, AFF_INVISIBLE):
            continue
        act( "$n slowly fades out of existence.", gch, None, None, TO_ROOM )
        gch.send("You slowly fade out of existence.\n")
        af = AFFECT_DATA()
        af.where     = TO_AFFECTS
        af.type      = sn
        af.level     = level//2
        af.duration  = 24
        af.location  = APPLY_NONE
        af.modifier  = 0
        af.bitvector = AFF_INVISIBLE
        gch.affect_add(af)
    ch.send("Ok.\n")

def spell_null( sn, level, ch, victim, target ):
    ch.send("That's not a spell! \n")
    return

def spell_pass_door( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_PASS_DOOR):
        if victim == ch:
            ch.send("You are already out of phase.\n")
        else:
            act("$N is already shifted out of phase.",ch,None,victim,TO_CHAR)
        return
    
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = number_fuzzy( level // 4 )
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = AFF_PASS_DOOR
    victim.affect_add(af)
    act( "$n turns translucent.", victim, None, None, TO_ROOM )
    victim.send("You turn translucent.\n")

# RT plague spell, very nasty */
def spell_plague( sn, level, ch, victim, target ):
    if saves_spell(level,victim,DAM_DISEASE) or (IS_NPC(victim) and IS_SET(victim.act,ACT_UNDEAD)):
        if ch == victim:
            ch.send("You feel momentarily ill, but it passes.\n")
        else:
            act("$N seems to be unaffected.",ch,None,victim,TO_CHAR)
        return

    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type       = sn
    af.level      = level * 3//4
    af.duration  = level
    af.location  = APPLY_STR
    af.modifier  = -5 
    af.bitvector = AFF_PLAGUE
    victim.affect_join(af)
   
    victim.send("You scream in agony as plague sores erupt from your skin.\n")
    act("$n screams in agony as plague sores erupt from $s skin.", victim,None,None,TO_ROOM)

def spell_poison( sn, level, ch, victim, target ):
    if target == TARGET_OBJ:
        obj = victim

        if obj.item_type == ITEM_FOOD or obj.item_type == ITEM_DRINK_CON:
            if IS_OBJ_STAT(obj,ITEM_BLESS) or IS_OBJ_STAT(obj,ITEM_BURN_PROOF):
                act("Your spell fails to corrupt $p.",ch,obj,None,TO_CHAR)
                return
            obj.value[3] = 1
            act("$p is infused with poisonous vapors.",ch,obj,None,TO_ALL)
            return
        if obj.item_type == ITEM_WEAPON:
            if IS_WEAPON_STAT(obj,WEAPON_FLAMING) \
            or IS_WEAPON_STAT(obj,WEAPON_FROST) \
            or IS_WEAPON_STAT(obj,WEAPON_VAMPIRIC) \
            or IS_WEAPON_STAT(obj,WEAPON_SHARP) \
            or IS_WEAPON_STAT(obj,WEAPON_VORPAL) \
            or IS_WEAPON_STAT(obj,WEAPON_SHOCKING) \
            or IS_OBJ_STAT(obj,ITEM_BLESS) \
            or IS_OBJ_STAT(obj,ITEM_BURN_PROOF):
                act("You can't seem to envenom $p.",ch,obj,None,TO_CHAR)
                return
            if IS_WEAPON_STAT(obj,WEAPON_POISON):
                act("$p is already envenomed.",ch,obj,None,TO_CHAR)
                return
            af = AFFECT_DATA()
            af.where     = TO_WEAPON
            af.type  = sn
            af.level     = level // 2
            af.duration  = level//8
            af.location  = 0
            af.modifier  = 0
            af.bitvector = WEAPON_POISON
            obj.affect_add(af)
            act("$p is coated with deadly venom.",ch,obj,None,TO_ALL)
            return
        act("You can't poison $p.",ch,obj,None,TO_CHAR)
        return

    if saves_spell( level, victim,DAM_POISON):
        act("$n turns slightly green, but it passes.",victim,None,None,TO_ROOM)
        victim.send("You feel momentarily ill, but it passes.\n")
        return

    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.location  = APPLY_STR
    af.modifier  = -2
    af.bitvector = AFF_POISON
    victim.affect_join(af)
    victim.send("You feel very sick.\n")
    act("$n looks very ill.",victim,None,None,TO_ROOM)

def spell_protection_evil( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_PROTECT_EVIL) or IS_AFFECTED(victim, AFF_PROTECT_GOOD):
        if victim == ch:
            ch.send("You are already protected.\n")
        else:
            act("$N is already protected.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 24
    af.location  = APPLY_SAVING_SPELL
    af.modifier  = -1
    af.bitvector = AFF_PROTECT_EVIL
    victim.affect_add(af)
    victim.send("You feel holy and pure.\n")
    if ch != victim:
        act("$N is protected from evil.",ch,None,victim,TO_CHAR)

def spell_protection_good( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_PROTECT_GOOD) or IS_AFFECTED(victim, AFF_PROTECT_EVIL):
        if victim == ch:
            ch.send("You are already protected.\n")
        else:
            act("$N is already protected.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 24
    af.location  = APPLY_SAVING_SPELL
    af.modifier  = -1
    af.bitvector = AFF_PROTECT_GOOD
    victim.affect_add(af)
    victim.send("You feel aligned with darkness.\n")
    if ch != victim:
        act("$N is protected from good.",ch,None,victim,TO_CHAR)

def spell_ray_of_truth(sn, level, ch, victim, target):
    if IS_EVIL(ch):
        victim = ch
        ch.send("The energy explodes inside you! \n")
    if victim != ch:
        act("$n raises $s hand, and a blinding ray of light shoots forth! ", ch,None,None,TO_ROOM)
        ch.send("You raise your hand and a blinding ray of light shoots forth! \n")

    if IS_GOOD(victim):
        act("$n seems unharmed by the light.",victim,None,victim,TO_ROOM)
        victim.send("The light seems powerless to affect you.\n")
        return

    dam = dice( level, 10 )
    if saves_spell( level, victim,DAM_HOLY):
        dam = dam // 2

    align = victim.alignment
    align -= 350

    if align < -1000:
        align = -1000 + (align + 1000) // 3

    dam = (dam * align * align) // 1000000

    damage( ch, victim, dam, sn, DAM_HOLY ,True)
    spell_blindness(const.skill_table['blindness'], 3 * level // 4, ch, victim,TARGET_CHAR)

def spell_recharge( sn, level, ch, victim, target ):
    obj = victim
    if obj.item_type != ITEM_WAND and obj.item_type != ITEM_STAFF:
        ch.send("That item does not carry charges.\n")
        return

    if obj.value[3] >= 3 * level // 2:
        ch.send("Your skills are not great enough for that.\n")
        return
    if obj.value[1] == 0:
        ch.send("That item has already been recharged once.\n")
        return
    
    chance = 40 + 2 * level

    chance -= obj.value[3] # harder to do high-level spells */
    chance -= (obj.value[1] - obj.value[2]) * (obj.value[1] - obj.value[2])

    chance = max(level//2,chance)

    percent = random.randint(1,99)

    if percent < chance // 2:
        act("$p glows softly.",ch,obj,None,TO_CHAR)
        act("$p glows softly.",ch,obj,None,TO_ROOM)
        obj.value[2] = max(obj.value[1],obj.value[2])
        obj.value[1] = 0
        return
    elif percent <= chance:
        act("$p glows softly.",ch,obj,None,TO_CHAR)
        act("$p glows softly.",ch,obj,None,TO_CHAR)

        chargemax = obj.value[1] - obj.value[2]
    
        if chargemax > 0:
            chargeback = max(1,chargemax * percent // 100)
        else:
            chargeback = 0

        obj.value[2] += chargeback
        obj.value[1] = 0
        return
    elif percent <= min(95, 3 * chance // 2):
        ch.send("Nothing seems to happen.\n")
        if obj.value[1] > 1:
            obj.value[1] -= 1
        return
    else: # whoops!  */
        act("$p glows brightly and explodes! ",ch,obj,None,TO_CHAR)
        act("$p glows brightly and explodes! ",ch,obj,None,TO_ROOM)
        obj.extract()

def spell_refresh( sn, level, ch, victim, target ):
    victim.move = min( victim.move + level, victim.max_move )
    if victim.max_move == victim.move:
        victim.send("You feel fully refreshed! \n")
    else:
        victim.send("You feel less tired.\n")
    if ch != victim:
        ch.send("Ok.\n")
    return

def spell_remove_curse( sn, level, ch, victim, target ):
    found = False
    # do object cases first */
    if target == TARGET_OBJ:
        obj = victim

        if IS_OBJ_STAT(obj,ITEM_NODROP) or IS_OBJ_STAT(obj,ITEM_NOREMOVE):
            if not IS_OBJ_STAT(obj,ITEM_NOUNCURSE) and  not saves_dispel(level + 2,obj.level,0):
                REMOVE_BIT(obj.extra_flags,ITEM_NODROP)
                REMOVE_BIT(obj.extra_flags,ITEM_NOREMOVE)
                act("$p glows blue.",ch,obj,None,TO_ALL)
                return
            act("The curse on $p is beyond your power.",ch,obj,None,TO_CHAR)
            return
    
        act("There doesn't seem to be a curse on $p.",ch,obj,None,TO_CHAR)
        return

    # characters */
    if check_dispel(level,victim,const.skill_table['curse']):
        victim.send("You feel better.\n")
        act("$n looks more relaxed.",victim,None,None,TO_ROOM)
    
    for obj in victim.carrying:
        if (IS_OBJ_STAT(obj,ITEM_NODROP) or IS_OBJ_STAT(obj,ITEM_NOREMOVE)) and not IS_OBJ_STAT(obj,ITEM_NOUNCURSE):
            # attempt to remove curse */
            if not saves_dispel(level,obj.level,0):
                REMOVE_BIT(obj.extra_flags,ITEM_NODROP)
                REMOVE_BIT(obj.extra_flags,ITEM_NOREMOVE)
                act("Your $p glows blue.",victim,obj,None,TO_CHAR)
                act("$n's $p glows blue.",victim,obj,None,TO_ROOM)
                break

def spell_sanctuary( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_SANCTUARY):
        if victim == ch:
            ch.send("You are already in sanctuary.\n")
        else:
            act("$N is already in sanctuary.",ch,None,victim,TO_CHAR)
        return

    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level // 6
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = AFF_SANCTUARY
    victim.affect_add(af)
    act( "$n is surrounded by a white aura.", victim, None, None, TO_ROOM )
    victim.send("You are surrounded by a white aura.\n")

def spell_shield( sn, level, ch, victim, target ):
    if is_affected( victim, sn ):
        if victim == ch:
            ch.send("You are already shielded from harm.\n")
        else:
            act("$N is already protected by a shield.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 8 + level
    af.location  = APPLY_AC
    af.modifier  = -20
    af.bitvector = 0
    victim.affect_add(af)
    act( "$n is surrounded by a force shield.", victim, None, None, TO_ROOM )
    victim.send("You are surrounded by a force shield.\n")
    return

def spell_shocking_grasp( sn, level, ch, victim, target ):
    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 ]

    level   = min(level, len(dam_each)-1)
    level   = max(0, level)
    dam     = random.randint( dam_each[level] // 2, dam_each[level] * 2 )
    if saves_spell( level, victim,DAM_LIGHTNING):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_LIGHTNING ,True)

def spell_sleep( sn, level, ch, victim, target ):
    if IS_AFFECTED(victim, AFF_SLEEP) \
    or (IS_NPC(victim) and IS_SET(victim.act,ACT_UNDEAD)) \
    or (level + 2) < victim.level \
    or saves_spell( level-4, victim,DAM_CHARM):
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = 4 + level
    af.location  = APPLY_NONE
    af.modifier  = 0
    af.bitvector = AFF_SLEEP
    victim.affect_join(af)

    if  IS_AWAKE(victim):
        victim.send("You feel very sleepy ..... zzzzzz.\n")
        act( "$n goes to sleep.", victim, None, None, TO_ROOM )
        victim.position = POS_SLEEPING

def spell_slow( sn, level, ch, victim, target ):
    if is_affected( victim, sn ) or IS_AFFECTED(victim,AFF_SLOW):
        if victim == ch:
            ch.send("You can't move any slower! \n")
        else:
            act("$N can't get any slower than that.", ch,None,victim,TO_CHAR)
        return
 
    if saves_spell(level,victim,DAM_OTHER) or IS_SET(victim.imm_flags,IMM_MAGIC):
        if victim != ch:
            ch.send("Nothing seemed to happen.\n")
        victim.send("You feel momentarily lethargic.\n")
        return

    if IS_AFFECTED(victim,AFF_HASTE):
        if not check_dispel(level,victim,const.skill_table['haste']):
            if victim != ch:
                ch.send("Spell failed.\n")
            victim.send("You feel momentarily slower.\n")
            return
        act("$n is moving less quickly.",victim,None,None,TO_ROOM)
        return
 
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level//2
    af.location  = APPLY_DEX
    af.modifier  = -1 - (level >= 18) - (level >= 25) - (level >= 32)
    af.bitvector = AFF_SLOW
    victim.affect_add(af)
    victim.send("You feel yourself slowing d o w n...\n")
    act("$n starts to move in slow motion.",victim,None,None,TO_ROOM)

def spell_stone_skin( sn, level, ch, victim, target ):

    if is_affected( ch, sn ):
        if victim == ch:
            ch.send("Your skin is already as hard as a rock.\n") 
        else:
            act("$N is already as hard as can be.",ch,None,victim,TO_CHAR)
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level
    af.location  = APPLY_AC
    af.modifier  = -40
    af.bitvector = 0
    victim.affect_add(af)
    act( "$n's skin turns to stone.", victim, None, None, TO_ROOM )
    victim.send("Your skin turns to stone.\n")

def spell_summon( sn, level, ch, victim, target ):
    victim = ch.get_char_world(target_name)
    if  not victim \
    or   victim == ch \
    or   victim.in_room == None \
    or   IS_SET(ch.in_room.room_flags, ROOM_SAFE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_SAFE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_PRIVATE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_SOLITARY) \
    or   IS_SET(victim.in_room.room_flags, ROOM_NO_RECALL) \
    or   (IS_NPC(victim) and IS_SET(victim.act,ACT_AGGRESSIVE)) \
    or   victim.level >= level + 3 \
    or   (not IS_NPC(victim) and victim.level >= LEVEL_IMMORTAL) \
    or   victim.fighting != None \
    or   (IS_NPC(victim) and IS_SET(victim.imm_flags,IMM_SUMMON)) \
    or   (IS_NPC(victim) and victim.pIndexData.pShop != None) \
    or   (not IS_NPC(victim) and IS_SET(victim.act,PLR_NOSUMMON)) \
    or   (IS_NPC(victim) and saves_spell( level, victim,DAM_OTHER)):
        ch.send("You failed.\n")
        return
    

    act( "$n disappears suddenly.", victim, None, None, TO_ROOM )
    victim.from_room()
    victim.to_room(ch.in_room)
    act( "$n arrives suddenly.", victim, None, None, TO_ROOM )
    act( "$n has summoned you! ", ch, None, victim,   TO_VICT )
    victim.do_look("auto")

def spell_teleport( sn, level, ch, victim, target ):
    if   victim.in_room == None \
    or   IS_SET(victim.in_room.room_flags, ROOM_NO_RECALL) \
    or ( victim != ch and IS_SET(victim.imm_flags,IMM_SUMMON)) \
    or ( not IS_NPC(ch) and victim.fighting != None ) \
    or ( victim != ch \
    and ( saves_spell( level - 5, victim,DAM_OTHER))):
        ch.send("You failed.\n")
        return

    pRoomIndex = get_random_room(victim)

    if victim != ch:
        victim.send("You have been teleported! \n")

    act( "$n vanishes! ", victim, None, None, TO_ROOM )
    victim.from_room()
    victim.to_room(pRoomIndex)
    act( "$n slowly fades into existence.", victim, None, None, TO_ROOM )
    victim.do_look( "auto" )

def spell_ventriloquate( sn, level, ch, victim, target ):
    target_name, speaker = read_word( target_name )
    buf1 =  "%s says '%s'.\n" % ( speaker.capitalize(), target_name )
    buf2 = "Someone makes %s say '%s'.\n" % ( speaker, target_name )

    for vch in ch.in_room.people:
        if not is_exact_name( speaker, vch.name) and IS_AWAKE(vch):
            vch.send( buf2 if saves_spell(level,vch,DAM_OTHER) else buf1)

def spell_weaken( sn, level, ch, victim, target ):
    if is_affected( victim, sn ) or saves_spell( level, victim,DAM_OTHER):
        return
    af = AFFECT_DATA()
    af.where     = TO_AFFECTS
    af.type      = sn
    af.level     = level
    af.duration  = level // 2
    af.location  = APPLY_STR
    af.modifier  = -1 * (level // 5)
    af.bitvector = AFF_WEAKEN
    victim.affect_add(af)
    victim.send("You feel your strength slip away.\n")
    act("$n looks tired and weak.",victim,None,None,TO_ROOM)

# RT recall spell is back */
def spell_word_of_recall( sn, level, ch, victim, target ):
    if IS_NPC(victim):
        return
   
    
    if ROOM_VNUM_TEMPLE not in room_index_hash:
        victim.send("You are completely lost.\n")
        return
    location = room_index_hash[ROOM_VNUM_TEMPLE]

    if IS_SET(victim.in_room.room_flags,ROOM_NO_RECALL) or IS_AFFECTED(victim,AFF_CURSE):
        victim.send("Spell failed.\n")
        return

    if victim.fighting:
        stop_fighting(victim,True)
    
    ch.move = move // 2
    act("$n disappears.",victim,None,None,TO_ROOM)
    victim.from_room()
    victim.to_room(location)
    act("$n appears in the room.",victim,None,None,TO_ROOM)
    victim.do_look("auto")


# NPC spells.
def spell_acid_breath( sn, level, ch, victim, target ):
    act("$n spits acid at $N.",ch,None,victim,TO_NOTVICT)
    act("$n spits a stream of corrosive acid at you.",ch,None,victim,TO_VICT)
    act("You spit acid at $N.",ch,None,victim,TO_CHAR)

    hpch = max(12,ch.hit)
    hp_dam = random.randint(hpch//11 + 1, hpch//6)
    dice_dam = dice(level,16)

    dam = max(hp_dam + dice_dam//10,dice_dam + hp_dam//10)
    
    if saves_spell(level,victim,DAM_ACID):
        acid_effect(victim,level//2,dam//4,TARGET_CHAR)
        damage(ch,victim,dam//2,sn,DAM_ACID,True)
    else:
        acid_effect(victim,level,dam,TARGET_CHAR)
        damage(ch,victim,dam,sn,DAM_ACID,True)

def spell_fire_breath( sn, level, ch, victim, target ):
    act("$n breathes forth a cone of fire.",ch,None,victim,TO_NOTVICT)
    act("$n breathes a cone of hot fire over you! ",ch,None,victim,TO_VICT)
    act("You breath forth a cone of fire.",ch,None,None,TO_CHAR)

    hpch = max( 10, ch.hit )
    hp_dam  = random.randint( hpch//9+1, hpch//5 )
    dice_dam = dice(level,20)

    dam = max(hp_dam + dice_dam //10, dice_dam + hp_dam // 10)
    fire_effect(victim.in_room,level,dam//2,TARGET_ROOM)

    for vch in victim.in_room.people[:]:
        if is_safe_spell(ch,vch,True) or (IS_NPC(vch) and IS_NPC(ch) and (ch.fighting != vch or vch.fighting != ch)):
            continue

        if vch == victim: # full damage */
            if saves_spell(level,vch,DAM_FIRE):
                fire_effect(vch,level//2,dam//4,TARGET_CHAR)
                damage(ch,vch,dam//2,sn,DAM_FIRE,True)
            else:
                fire_effect(vch,level,dam,TARGET_CHAR)
                damage(ch,vch,dam,sn,DAM_FIRE,True)
        else: # partial damage */
            if saves_spell(level - 2,vch,DAM_FIRE):
                fire_effect(vch,level//4,dam//8,TARGET_CHAR)
                damage(ch,vch,dam//4,sn,DAM_FIRE,True)
            else:
                fire_effect(vch,level//2,dam//4,TARGET_CHAR)
                damage(ch,vch,dam//2,sn,DAM_FIRE,True)

def spell_frost_breath( sn, level, ch, victim, target ):
    act("$n breathes out a freezing cone of frost! ",ch,None,victim,TO_NOTVICT)
    act("$n breathes a freezing cone of frost over you! ", ch,None,victim,TO_VICT)
    act("You breath out a cone of frost.",ch,None,None,TO_CHAR)

    hpch = max(12,ch.hit)
    hp_dam = random.randint(hpch//11 + 1, hpch//6)
    dice_dam = dice(level,16)

    dam = max(hp_dam + dice_dam//10,dice_dam + hp_dam//10)
    cold_effect(victim.in_room,level,dam//2,TARGET_ROOM) 

    for vch in victim.in_room.people[:]:
        if is_safe_spell(ch,vch,True) or (IS_NPC(vch) and IS_NPC(ch) and (ch.fighting != vch or vch.fighting != ch)):
            continue

        if vch == victim: # full damage */
            if saves_spell(level,vch,DAM_COLD):
                cold_effect(vch,level//2,dam//4,TARGET_CHAR)
                damage(ch,vch,dam//2,sn,DAM_COLD,True)
            else:
                cold_effect(vch,level,dam,TARGET_CHAR)
                damage(ch,vch,dam,sn,DAM_COLD,True)
        else:
            if saves_spell(level - 2,vch,DAM_COLD):
                cold_effect(vch,level//4,dam//8,TARGET_CHAR)
                damage(ch,vch,dam//4,sn,DAM_COLD,True)
            else:
                cold_effect(vch,level//2,dam//4,TARGET_CHAR)
                damage(ch,vch,dam//2,sn,DAM_COLD,True)
    
def spell_gas_breath( sn, level, ch, victim, target ):
    act("$n breathes out a cloud of poisonous gas! ",ch,None,None,TO_ROOM)
    act("You breath out a cloud of poisonous gas.",ch,None,None,TO_CHAR)

    hpch = max(16,ch.hit)
    hp_dam = random.randint(hpch//15+1,8)
    dice_dam = dice(level,12)

    dam = max(hp_dam + dice_dam//10,dice_dam + hp_dam//10)
    poison_effect(ch.in_room,level,dam,TARGET_ROOM)

    for vch in ch.in_room.people[:]:
        if is_safe_spell(ch,vch,True) or (IS_NPC(ch) and IS_NPC(vch) and (ch.fighting == vch or vch.fighting == ch)):
            continue

        if saves_spell(level,vch,DAM_POISON):
            poison_effect(vch,level//2,dam//4,TARGET_CHAR)
            damage(ch,vch,dam//2,sn,DAM_POISON,True)
        else:
            poison_effect(vch,level,dam,TARGET_CHAR)
            damage(ch,vch,dam,sn,DAM_POISON,True)

def spell_lightning_breath( sn, level, ch, victim, target ):
    act("$n breathes a bolt of lightning at $N.",ch,None,victim,TO_NOTVICT)
    act("$n breathes a bolt of lightning at you! ",ch,None,victim,TO_VICT)
    act("You breathe a bolt of lightning at $N.",ch,None,victim,TO_CHAR)

    hpch = max(10,ch.hit)
    hp_dam = random.randint(hpch//9+1,hpch//5)
    dice_dam = dice(level,20)

    dam = max(hp_dam + dice_dam//10,dice_dam + hp_dam//10)

    if saves_spell(level,victim,DAM_LIGHTNING):
        shock_effect(victim,level//2,dam//4,TARGET_CHAR)
        damage(ch,victim,dam//2,sn,DAM_LIGHTNING,True)
    else:
        shock_effect(victim,level,dam,TARGET_CHAR)
        damage(ch,victim,dam,sn,DAM_LIGHTNING,True) 

#
 #* Spells for mega1.are from Glop//Erkenbrand.
 
def spell_general_purpose( sn, level, ch, victim, target ):
    dam = random.randint( 25, 100 )
    if saves_spell( level, victim, DAM_PIERCE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_PIERCE ,True)
    return

def spell_high_explosive( sn, level, ch, victim, target ):
    dam = random.randint( 30, 120 )
    if saves_spell( level, victim, DAM_PIERCE):
        dam = dam // 2
    damage( ch, victim, dam, sn, DAM_PIERCE ,True)

####
#### What was Magic2.c

def spell_farsight(sn, level, ch, victim, target):
    if IS_AFFECTED(ch,AFF_BLIND):
        ch.send("Maybe it would help if you could see?\n")
        return
   
    ch.do_scan(target_name)


def spell_portal( sn, level, ch, victim, target):
    victim = ch.get_char_world(target_name)

    if not victim \
    or   victim == ch \
    or   victim.in_room == None \
    or   not ch.can_see_room(victim.in_room) \
    or   IS_SET(victim.in_room.room_flags, ROOM_SAFE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_PRIVATE) \
    or   IS_SET(victim.in_room.room_flags, ROOM_SOLITARY) \
    or   IS_SET(victim.in_room.room_flags, ROOM_NO_RECALL) \
    or   IS_SET(ch.in_room.room_flags, ROOM_NO_RECALL) \
    or   victim.level >= level + 3 \
    or   (not IS_NPC(victim) and victim.level >= LEVEL_HERO) \
    or   (IS_NPC(victim) and IS_SET(victim.imm_flags,IMM_SUMMON)) \
    or   (IS_NPC(victim) and saves_spell( level, victim,DAM_NONE) ) \
    or  (victim.is_clan() and not ch.is_same_clan(victim)):
        ch.send( "You failed.\n")
        return
    

    stone = ch.get_eq(WEAR_HOLD)
    if not IS_IMMORTAL(ch) and  (stone == None or stone.item_type != ITEM_WARP_STONE):
        ch.send("You lack the proper component for this spell.\n")
        return
    

    if stone and stone.item_type == ITEM_WARP_STONE:
        act("You draw upon the power of $p.",ch,stone,None,TO_CHAR)
        act("It flares brightly and vanishes! ",ch,stone,None,TO_CHAR)
        stone.extract()


    portal = create_object(obj_index_hash[OBJ_VNUM_PORTAL],0)
    portal.timer = 2 + level // 25 
    portal.value[3] = victim.in_room.vnum

    portal.to_room(ch.in_room)

    act("$p rises up from the ground.",ch,portal,None,TO_ROOM)
    act("$p rises up before you.",ch,portal,None,TO_CHAR)

def spell_nexus( sn, level, ch, victim, target):
    from_room = ch.in_room
    victim = ch.get_char_world(target_name) 
    to_room = victim.in_room

    if not victim \
    or victim == ch \
    or not to_room \
    or not ch.can_see_room(to_room) or not ch.can_see_room(from_room) \
    or IS_SET(to_room.room_flags, ROOM_SAFE) \
    or IS_SET(from_room.room_flags,ROOM_SAFE) \
    or IS_SET(to_room.room_flags, ROOM_PRIVATE) \
    or IS_SET(to_room.room_flags, ROOM_SOLITARY) \
    or IS_SET(to_room.room_flags, ROOM_NO_RECALL) \
    or IS_SET(from_room.room_flags,ROOM_NO_RECALL) \
    or victim.level >= level + 3 \
    or (not IS_NPC(victim) and victim.level >= LEVEL_HERO) \
    or (IS_NPC(victim) and IS_SET(victim.imm_flags,IMM_SUMMON)) \
    or (IS_NPC(victim) and saves_spell( level, victim,DAM_NONE) ) \
    or (victim.is_clan() and not ch.is_same_clan(victim)):
        ch.send("You failed.\n")
        return
 
    stone = ch.get_eq(WEAR_HOLD)
    if not IS_IMMORTAL(ch) and  (stone == None or stone.item_type != ITEM_WARP_STONE):
        ch.send("You lack the proper component for this spell.\n")
        return
 
    if stone and stone.item_type == ITEM_WARP_STONE:
        act("You draw upon the power of $p.",ch,stone,None,TO_CHAR)
        act("It flares brightly and vanishes! ",ch,stone,None,TO_CHAR)
        stone.extract()

    # portal one */ 
    portal = create_object(obj_index_hash[OBJ_VNUM_PORTAL],0)
    portal.timer = 1 + level // 10
    portal.value[3] = to_room.vnum
 
    portal.to_room(from_room)
 
    act("$p rises up from the ground.",ch,portal,None,TO_ROOM)
    act("$p rises up before you.",ch,portal,None,TO_CHAR)

    # no second portal if rooms are the same */
    if to_room == from_room:
        return

    # portal two */
    portal = create_object(obj_index_hash[OBJ_VNUM_PORTAL],0)
    portal.timer = 1 + level//10
    portal.value[3] = from_room.vnum

    portal.to_room(to_room)

    if to_room.people:
        act("$p rises up from the ground.",to_room.people[0],portal,None,TO_ROOM)
        act("$p rises up from the ground.",to_room.people[0],portal,None,TO_CHAR)